From 1b91fee77e42b616dc67c4ff34653b1f16f934e2 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Fri, 16 Aug 2024 20:27:24 -0400 Subject: [PATCH] Rejigger the CI Pipeline again - Pass through expected merge SHA from security workflow for security confirmation. - Rely on one check from ReleaseNotes, `always()` updated in completion gate. - Cache ReleaseNotes build throughout pipeline. There's literally no way this will work as expected --- .github/workflows/ci-pipeline.yml | 590 +++++++++++++++--- .github/workflows/ci-security.yml | 72 ++- .../Tgstation.Server.ReleaseNotes/Program.cs | 79 +++ 3 files changed, 618 insertions(+), 123 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 8704b477d8..b1cf211e63 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_current_merge_sha: + description: 'Pull Request Merge SHA' + required: true env: TGS_DOTNET_VERSION: 8 @@ -47,8 +50,68 @@ concurrency: cancel-in-progress: true jobs: + ci-start-gate: + name: CI Start Gate + runs-on: ubuntu-latest + 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: Checkout (Branch) + uses: actions/checkout@v4 + if: github.event_name == 'push' || github.event_name == 'schedule' + + - name: Checkout (PR Merge) + uses: actions/checkout@v4 + if: github.event_name != 'push' && github.event_name != 'schedule' + with: + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } + + - name: Restore + run: dotnet restore + + - 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: Store ReleaseNotes Binaries + uses: actions/upload-artifact@v4 + with: + 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.event.inputs.pull_request_current_merge_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 @@ -70,7 +133,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -94,6 +181,7 @@ jobs: dmapi-build: name: Build DMAPI + needs: ci-start-gate strategy: fail-fast: false matrix: @@ -151,7 +239,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Build DMAPI Test Project run: | @@ -175,6 +287,7 @@ jobs: opendream-build: name: Build DMAPI (OpenDream) + needs: ci-start-gate strategy: fail-fast: false matrix: @@ -203,7 +316,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Checkout OpenDream run: | @@ -235,6 +372,7 @@ jobs: efcore-version-match: name: Check Nuget Versions Match Tools + needs: ci-start-gate runs-on: ubuntu-latest steps: - name: Checkout (Branch) @@ -245,7 +383,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Retrieve dotnet-ef Tool Version id: dotnet-ef-tool @@ -291,6 +453,7 @@ jobs: pages-build: name: Build gh-pages + needs: ci-start-gate runs-on: ubuntu-latest steps: - name: Setup dotnet @@ -299,33 +462,23 @@ jobs: dotnet-version: '${{ env.TGS_DOTNET_VERSION }}.0.x' dotnet-quality: ${{ env.TGS_DOTNET_QUALITY }} - - name: Checkout (Branch) - uses: actions/checkout@v4 - if: github.event_name == 'push' || github.event_name == 'schedule' - - - name: Checkout (PR Merge) - uses: actions/checkout@v4 - if: github.event_name != 'push' && github.event_name != 'schedule' - with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" - - - name: Restore - run: dotnet restore - - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj - - name: gh-pages Clone run: git clone -b gh-pages --single-branch "https://git@github.com/tgstation/tgstation-server" $HOME/tgsdox + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Build Changelog (Incremental) run: | mv $HOME/tgsdox/changelog.yml ./ 2>/dev/null - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --generate-full-notes + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --generate-full-notes - name: Generate App Token run: | - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} echo "INSTALLATION_TOKEN=$(cat ${{ runner.temp }}/installation_secret.txt)" >> $GITHUB_ENV rm ${{ runner.temp }}/installation_secret.txt @@ -365,6 +518,7 @@ jobs: docker-build: name: Build Docker Image + needs: ci-start-gate runs-on: ubuntu-latest env: TGS_TELEMETRY_KEY_FILE: tgs_telemetry_key.txt @@ -377,7 +531,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Setup Telemetry Key File shell: bash @@ -427,7 +605,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Restore run: dotnet restore @@ -492,7 +694,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Restore run: dotnet restore @@ -626,7 +852,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Restore run: dotnet restore @@ -850,7 +1100,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Restore run: dotnet restore @@ -938,7 +1212,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Retrieve OpenAPI Spec uses: actions/download-artifact@v4 @@ -962,7 +1260,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Retrieve Linux Unit Test Coverage (Debug) uses: actions/download-artifact@v4 @@ -1206,6 +1528,7 @@ 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 @@ -1250,7 +1573,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Grab Most Recent Changelog run: curl -L https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml -o changelog.yml @@ -1320,6 +1667,7 @@ 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 @@ -1348,7 +1696,31 @@ jobs: uses: actions/checkout@v4 if: github.event_name != 'push' && github.event_name != 'schedule' with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" + ref: "refs/pull/${{ inputs.pull_request_number}}/merge" + + - name: Read Current SHA + id: get-pr-sha + if: github.event_name != 'push' && github.event_name != 'schedule' + shell: bash + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - 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 != inputs.pull_request_current_merge_sha + with: + script: | + const delay = ms => new Promise(res => setTimeout(res, ms)); + + github.rest.actions.cancelWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + + while (true) { + core.info('Waiting for workflow to cancel ...'); + await delay(5000); + } - name: Restore Wix dotnet Tool run: | @@ -1468,6 +1840,7 @@ jobs: check-winget-pr-template: name: Check winget-pkgs Pull Request Template is up to date + needs: ci-start-gate runs-on: ubuntu-latest steps: - name: Setup dotnet @@ -1482,38 +1855,54 @@ jobs: curl -L -u "${{ vars.DEV_PUSH_USERNAME }}:${{ secrets.DEV_PUSH_TOKEN }}" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" -o commits.json https://api.github.com/repos/microsoft/winget-pkgs/commits?path=.github/PULL_REQUEST_TEMPLATE.md echo "pr_template_sha=$(cat commits.json | jq '.[0].sha')" >> $GITHUB_OUTPUT - - name: Checkout (Branch) - uses: actions/checkout@v4 - if: github.event_name == 'push' || github.event_name == 'schedule' - - - name: Checkout (PR Merge) - uses: actions/checkout@v4 - if: github.event_name != 'push' && github.event_name != 'schedule' + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 with: - ref: "refs/pull/${{ github.event.inputs.pull_request_number }}/merge" - - - name: Restore - run: dotnet restore - - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj + name: release_notes_bins + path: release_notes_bins - name: Run ReleaseNotes Check - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --winget-template-check ${{ steps.get-sha.outputs.pr_template_sha }} + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --winget-template-check ${{ steps.get-sha.outputs.pr_template_sha }} - ci-completion-gate: # This job exists so there isn't a moving target for branch protections + 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 ] runs-on: ubuntu-latest + if: always() steps: - - name: GitHub Requires at Least One Step for a Job - run: exit 0 + - 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: cancelled() + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.event.inputs.pull_request_current_merge_sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Cancelled ${{ github.run_id }} + + - name: Update CI Check Run (Failure) + if: !cancelled() && failure() + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.event.inputs.pull_request_current_merge_sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Failure ${{ github.run_id }} + + - name: Update CI Check Run (Success) + if: !cancelled() && failure() + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ci-check ${{ github.event.inputs.pull_request_current_merge_sha }} ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} Success ${{ github.run_id }} + + - name: Fail Job if Prerequisites Failed + if: failure() + run: exit 1 deployment-gate: name: Deployment Start Gate needs: ci-completion-gate runs-on: ubuntu-latest - if: github.event_name == 'push' && (github.event.ref == 'refs/heads/master' || github.event.ref == 'refs/heads/dev') + if: github.event_name == 'push' steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 @@ -1533,12 +1922,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Restore - run: dotnet restore - - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj - - name: Parse API version shell: powershell run: | @@ -1558,13 +1941,19 @@ jobs: $ProgressPreference = 'SilentlyContinue' Invoke-WebRequest -Uri https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml -OutFile changelog.yml + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Generate Release Notes - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes ${{ env.TGS_API_VERSION }} --httpapi + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll ${{ env.TGS_API_VERSION }} --httpapi - name: Generate App Token shell: powershell run: | - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} $installSecret = Get-Content ${{ runner.temp }}/installation_secret.txt echo "INSTALLATION_TOKEN=$installSecret" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append rm ${{ runner.temp }}/installation_secret.txt @@ -1605,12 +1994,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Restore - run: dotnet restore - - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj - - name: Parse DMAPI version shell: powershell run: | @@ -1629,13 +2012,19 @@ jobs: $ProgressPreference = 'SilentlyContinue' Invoke-WebRequest -Uri https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml -OutFile changelog.yml + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Generate Release Notes - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes ${{ env.TGS_DM_VERSION }} --dmapi + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll ${{ env.TGS_DM_VERSION }} --dmapi - name: Generate App Token shell: powershell run: | - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} $installSecret = Get-Content ${{ runner.temp }}/installation_secret.txt echo "INSTALLATION_TOKEN=$installSecret" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append rm ${{ runner.temp }}/installation_secret.txt @@ -1679,14 +2068,17 @@ jobs: - name: Restore run: dotnet restore - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj - - name: Grab Most Recent Changelog run: curl -L https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml -o changelog.yml + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Generate Release Notes - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --nuget + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --nuget - name: Publish Tgstation.Server.Common to NuGet uses: alirezanet/publish-nuget@e276c40afeb2a154046f0997820f2a9ea74832d9 # v3.1.0 @@ -1724,17 +2116,14 @@ jobs: dotnet-version: '${{ env.TGS_DOTNET_VERSION }}.0.x' dotnet-quality: ${{ env.TGS_DOTNET_QUALITY }} - - name: Checkout - uses: actions/checkout@v4 - - - name: Restore - run: dotnet restore - - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins - name: Run ReleaseNotes with --ensure-release - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --ensure-release ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --ensure-release ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} deploy-tgs: name: Deploy TGS @@ -1784,9 +2173,6 @@ jobs: - name: Build Service run: dotnet build -c Release src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj - - name: Build ReleaseNotes - run: dotnet build -c Release tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj - - name: Prepare Artifacts shell: powershell run: build/package/winget/prepare_installer_input_artifacts.ps1 @@ -1871,13 +2257,19 @@ jobs: &"C:/Program Files/7-Zip/7z.exe" a ServerConsole.zip ./ServerConsole/* -tzip &"C:/Program Files/7-Zip/7z.exe" a ServerUpdatePackage.zip ./ServerUpdatePackage/* -tzip + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Generate Release Notes - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes ${{ env.TGS_VERSION }} + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll ${{ env.TGS_VERSION }} - name: Generate App Token shell: powershell run: | - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} $installSecret = Get-Content ${{ runner.temp }}/installation_secret.txt echo "INSTALLATION_TOKEN=$installSecret" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append rm ${{ runner.temp }}/installation_secret.txt @@ -1985,26 +2377,23 @@ jobs: dotnet-version: '${{ env.TGS_DOTNET_VERSION }}.0.x' dotnet-quality: ${{ env.TGS_DOTNET_QUALITY }} - - name: Checkout - uses: actions/checkout@v4 - - - name: Restore - run: dotnet restore - - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes/Tgstation.Server.ReleaseNotes.csproj - - name: gh-pages Clone run: git clone -b gh-pages --single-branch "https://git@github.com/tgstation/tgstation-server" $HOME/tgsdox + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Build Changelog (Incremental) run: | mv $HOME/tgsdox/changelog.yml ./ 2>/dev/null - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --generate-full-notes + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --generate-full-notes - name: Generate App Token run: | - dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} + dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --token-output-file ${{ runner.temp }}/installation_secret.txt ${{ secrets.TGS_CI_GITHUB_APP_TOKEN_BASE64 }} echo "INSTALLATION_TOKEN=$(cat ${{ runner.temp }}/installation_secret.txt)" >> $GITHUB_ENV rm ${{ runner.temp }}/installation_secret.txt @@ -2090,15 +2479,18 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Build ReleaseNotes - run: dotnet build -c Release -p:TGS_HOST_NO_WEBPANEL=true tools/Tgstation.Server.ReleaseNotes - - name: Retrieve Server Service uses: actions/download-artifact@v4 with: name: packaging-windows-raw-msi path: artifacts + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + - name: Execute Push Script shell: powershell run: build/package/winget/push_manifest.ps1 @@ -2111,4 +2503,4 @@ jobs: - name: Run ReleaseNotes with --link-winget shell: powershell - run: dotnet run -c Release --no-build --project tools/Tgstation.Server.ReleaseNotes --link-winget ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll --link-winget ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/ci-security.yml b/.github/workflows/ci-security.yml index 407346915a..6fb53695fb 100644 --- a/.github/workflows/ci-security.yml +++ b/.github/workflows/ci-security.yml @@ -6,6 +6,9 @@ on: branches: - dev - master +env: + TGS_DOTNET_VERSION: 8 + TGS_DOTNET_QUALITY: ga concurrency: group: "ci-security-${{ github.head_ref || github.run_id }}-${{ github.event_name }}" @@ -62,30 +65,51 @@ jobs: actions: write contents: write steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '${{ env.TGS_DOTNET_VERSION }}.0.x' + dotnet-quality: ${{ env.TGS_DOTNET_QUALITY }} - - name: Generate Temporary Branch to Reference Merge - run: | - git checkout -b ${{ github.event.pull_request.number }}-merge - git push -f -u origin ${{ github.event.pull_request.number }}-merge + - name: Checkout + uses: actions/checkout@v4 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge - - name: Send Workflow Dispatch - uses: lasith-kg/dispatch-workflow@5623bf13f09bbbbdb549ec692b070307f39b66ac #v2.0.0 + setup_node@v4 - with: - dispatch-method: workflow_dispatch - owner: ${{ github.repository_owner }} - repo: ${{ github.event.pull_request.base.repo.name }} - ref: ${{ github.event.pull_request.number }}-merge - workflow: ci-pipeline.yml - token: ${{ github.token }} - workflow-inputs: | - { - "pull_request_number": "${{ github.event.pull_request.number }}" - } + - name: Restore + run: dotnet restore - - name: Delete Temporary Branch - if: always() - run: git push -d origin ${{ github.event.pull_request.number }}-merge + - 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: Read Current SHA + id: get-pr-sha + run: echo "head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Generate Temporary Branch to Reference Merge + run: | + git checkout -b ${{ github.event.pull_request.number }}-merge + git push -f -u origin ${{ github.event.pull_request.number }}-merge + + - name: Send Workflow Dispatch + uses: lasith-kg/dispatch-workflow@5623bf13f09bbbbdb549ec692b070307f39b66ac #v2.0.0 + setup_node@v4 + id: dispatch + with: + dispatch-method: workflow_dispatch + owner: ${{ github.repository_owner }} + repo: ${{ github.event.pull_request.base.repo.name }} + ref: ${{ github.event.pull_request.number }}-merge + workflow: ci-pipeline.yml + token: ${{ github.token }} + workflow-inputs: | + { + "pull_request_number": "${{ github.event.pull_request.number }}" + "pull_request_current_merge_sha": "${{ steps.get-pr-sha.outputs.head_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 diff --git a/tools/Tgstation.Server.ReleaseNotes/Program.cs b/tools/Tgstation.Server.ReleaseNotes/Program.cs index 0ea358d399..47faffceca 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Program.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Program.cs @@ -59,6 +59,7 @@ namespace Tgstation.Server.ReleaseNotes 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) @@ -67,6 +68,7 @@ namespace Tgstation.Server.ReleaseNotes && !shaCheck && !fullNotes && !nuget + && !ciCheck && !genToken) { Console.WriteLine("Invalid version: " + versionString); @@ -148,6 +150,17 @@ namespace Tgstation.Server.ReleaseNotes return await Winget(client, actionsUrl, null); } + 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) { @@ -1663,6 +1676,72 @@ package (version) distribution(s); urgency=urgency 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