From 0813b225e624563cd48ca414c84e6fa0d69def44 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 09:45:08 -0400 Subject: [PATCH 01/23] Minor corrections to Master Merge workflow --- .github/workflows/stable-merge.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stable-merge.yml b/.github/workflows/stable-merge.yml index e70a60b577..a1aa943f3a 100644 --- a/.github/workflows/stable-merge.yml +++ b/.github/workflows/stable-merge.yml @@ -1,9 +1,9 @@ name: 'Master Merge' on: - push: - branches: - - master + push: + branches: + - master jobs: master-merge: From 6cb1de0f7bed1db2c4905211f66c761aba5a5c41 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:12:40 -0400 Subject: [PATCH 02/23] CI Overhaul - Switched from `pull_request` to `pull_request_target`, explicitly checking out the PR code. - Added security-checkpoint job that will fail on pull requests from forks. - Added concurrency limiter. - Added missing live tests filter. - Fixed bad indentation. Fixes #1107 --- .github/workflows/ci-suite.yml | 642 ++++++++++++++++++++------------- 1 file changed, 390 insertions(+), 252 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 852d85dbfb..c68d96617e 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -5,7 +5,8 @@ on: branches: - dev - master - pull_request: + pull_request_target: + types: [opened, reopened, labeled, synchronize] branches: - dev - master @@ -19,9 +20,35 @@ env: TGS_TEST_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} +concurrency: + group: "ci-${{ (github.event_name == 'push' ? github.head_ref : github.event.number) }}" + cancel-in-progress: true + jobs: + security-checkpoint: + name: Check CI Clearance + needs: security-checkpoint + runs-on: ubuntu-latest + steps: + - name: Comment on new Fork PR + if: github.event_name != 'push' && github.pull_request.head.repo.id != github.repository.id && github.event.action == 'opened' + uses: thollander/actions-comment-pull-request@v2 + with: + message: Thank you for contributing to tgstation-server! As this pull request is from a fork, we can't allow the CI actions which require repository secrets to run on it without approval. After a brief review to make sure you're not misusing those secrets, a maintainer will add the `CI Cleared` label to allow the CI suite to run. Maintainers, please note that any changes to workflow files will not be reflected in the CI run. + + - name: "Remove Stale 'CI Cleared' Label" + if: github.event_name != 'push' && github.pull_request.head.repo.id != github.repository.id && (github.event.action == 'synchronize' || github.event.action == 'reopened') + uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: CI Cleared + + - name: Fail Clearance Check if PR has Unlabeled new Commits from Fork + if: github.event_name != 'push' && github.pull_request.head.repo.id != github.repository.id && (github.event.action != 'labeled' || !contains(github.event.pull_request.labels.*.name, 'CI Cleared')) + run: exit 1 + analyze: name: Code Scanning + needs: security-checkpoint runs-on: ubuntu-latest steps: - name: Install Node 12.X @@ -32,11 +59,19 @@ jobs: - name: Upgrade NPM run: npm install -g npm - - name: Checkout repository + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' with: fetch-depth: 2 + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + fetch-depth: 2 + ref: "refs/pull/${{ github.event.number }}/merge" + - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: @@ -73,8 +108,15 @@ jobs: make here exit 0 - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Build DMAPI Test Project run: | @@ -100,58 +142,70 @@ jobs: name: Build Doxygen Site runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Patch Doxyfile - run: | - VERSION=$(cat "build/Version.props" | grep -oPm1 "(?<=)[^<]+") - echo -e "\nPROJECT_NUMBER = $VERSION\nINPUT = .\nOUTPUT_DIRECTORY = ./doxout\nPROJECT_LOGO = ./build/tgs.ico\nHAVE_DOT=YES" >> "docs/Doxyfile" + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - - name: Doxygen Build - uses: mattnotmitt/doxygen-action@v1 - with: - doxyfile-path: 'docs/Doxyfile' + - name: Patch Doxyfile + run: | + VERSION=$(cat "build/Version.props" | grep -oPm1 "(?<=)[^<]+") + echo -e "\nPROJECT_NUMBER = $VERSION\nINPUT = .\nOUTPUT_DIRECTORY = ./doxout\nPROJECT_LOGO = ./build/tgs.ico\nHAVE_DOT=YES" >> "docs/Doxyfile" - - name: gh-pages push - if: github.event_name == 'push' && github.event.ref == 'refs/heads/dev' - run: | - git clone -b gh-pages --single-branch "https://git@github.com/tgstation/tgstation-server" $HOME/tgsdox - pushd $HOME/tgsdox - rm -r * - popd - echo ./doxout/* | xargs -n 10 sudo mv -t $HOME/tgsdox - cd $HOME/tgsdox - git config --global push.default simple - git config user.name "tgstation-server" - git config user.email "tgstation-server@tgstation13.org" - echo '# THIS BRANCH IS AUTO GENERATED BY GITHUB ACTIONS' > README.md + - name: Doxygen Build + uses: mattnotmitt/doxygen-action@v1 + with: + doxyfile-path: 'docs/Doxyfile' - # Need to create a .nojekyll file to allow filenames starting with an underscore - # to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. - echo "" > .nojekyll - echo "Adding files..." - git add --all - echo "Committing..." - git diff-index --quiet HEAD || git commit -m "Deploy code docs to GitHub Pages for workflow run ${{ github.run_number }}" -m "Commit: ${{ github.event.head_commit.id }}" - echo "Pushing..." - git push -f "https://${{ secrets.DEV_PUSH_TOKEN }}@github.com/tgstation/tgstation-server" 2>&1 + - name: gh-pages push + if: github.event_name == 'push' && github.event.ref == 'refs/heads/dev' + run: | + git clone -b gh-pages --single-branch "https://git@github.com/tgstation/tgstation-server" $HOME/tgsdox + pushd $HOME/tgsdox + rm -r * + popd + echo ./doxout/* | xargs -n 10 sudo mv -t $HOME/tgsdox + cd $HOME/tgsdox + git config --global push.default simple + git config user.name "tgstation-server" + git config user.email "tgstation-server@tgstation13.org" + echo '# THIS BRANCH IS AUTO GENERATED BY GITHUB ACTIONS' > README.md + + # Need to create a .nojekyll file to allow filenames starting with an underscore + # to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. + echo "" > .nojekyll + echo "Adding files..." + git add --all + echo "Committing..." + git diff-index --quiet HEAD || git commit -m "Deploy code docs to GitHub Pages for workflow run ${{ github.run_number }}" -m "Commit: ${{ github.event.head_commit.id }}" + echo "Pushing..." + git push -f "https://${{ secrets.DEV_PUSH_TOKEN }}@github.com/tgstation/tgstation-server" 2>&1 docker-build: name: Build Docker Image runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Upgrade NPM - run: sudo npm install -g npm + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Build Docker Image run: docker build . -f build/Dockerfile linux-unit-tests: name: Linux Tests + needs: security-checkpoint strategy: fail-fast: false matrix: @@ -163,8 +217,15 @@ jobs: with: dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Upgrade NPM run: sudo npm install -g npm @@ -183,6 +244,7 @@ jobs: windows-unit-tests: name: Windows Tests + needs: security-checkpoint strategy: fail-fast: false matrix: @@ -194,8 +256,15 @@ jobs: with: dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Upgrade NPM run: npm install -g npm @@ -214,7 +283,7 @@ jobs: windows-integration-test: name: Windows Live Tests - needs: dmapi-build + needs: [security-checkpoint, dmapi-build] env: TGS_TEST_DATABASE_TYPE: SqlServer TGS_TEST_DUMP_API_SPEC: yes @@ -243,17 +312,24 @@ jobs: TGS_CONNSTRING_VALUE="Server=(localdb)\MSSQLLocalDB;Integrated Security=true;Initial Catalog=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }};Application Name=tgstation-server" echo "TGS_TEST_CONNECTION_STRING=$(echo $TGS_CONNSTRING_VALUE)" >> $GITHUB_ENV - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Build run: dotnet build -c ${{ matrix.configuration }} tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj - - name: Run Integration Test + - name: Run Live Tests run: | cd tests/Tgstation.Server.Tests Start-Sleep -Seconds 10 - dotnet test -c ${{ matrix.configuration }} --no-build --logger GitHubActions --collect:"XPlat Code Coverage" --settings ../../build/coverlet.runsettings --results-directory ../../TestResults + dotnet test -c ${{ matrix.configuration }} --no-build --filter FullyQualifiedName~TestLiveServer --logger GitHubActions --collect:"XPlat Code Coverage" --settings ../../build/coverlet.runsettings --results-directory ../../TestResults - name: Store Code Coverage uses: actions/upload-artifact@v3 @@ -286,7 +362,7 @@ jobs: linux-integration-tests: name: Linux Live Tests - needs: dmapi-build + needs: [security-checkpoint, dmapi-build] services: # We start all dbs here so we can just code the stuff once postgres: image: cyberboss/postgres-max-connections # Fork of _/postgres:latest with max_connections=500 becuase GitHub actions service containers have no way to set command lines. Rebuilds with updates. @@ -380,17 +456,24 @@ jobs: if: ${{ matrix.watchdog-type == 'Basic' }} run: echo "General__UseBasicWatchdog=true" >> $GITHUB_ENV - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Build run: dotnet build -c ${{ matrix.configuration }}NoService tests/Tgstation.Server.Tests/Tgstation.Server.Tests.csproj - - name: Run Integration Test + - name: Run Live Tests run: | cd tests/Tgstation.Server.Tests sleep 10 - dotnet test -c ${{ matrix.configuration }}NoService --logger GitHubActions --no-build --collect:"XPlat Code Coverage" --settings ../../build/coverlet.runsettings --results-directory ../../TestResults + dotnet test -c ${{ matrix.configuration }}NoService --filter FullyQualifiedName~TestLiveServer --logger GitHubActions --no-build --collect:"XPlat Code Coverage" --settings ../../build/coverlet.runsettings --results-directory ../../TestResults - name: Store Code Coverage uses: actions/upload-artifact@v3 @@ -441,8 +524,15 @@ jobs: - name: Install IBM OpenAPI Validator run: npm i -g ibm-openapi-validator@0.51.3 - - name: Checkout + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Retrieve OpenAPI Spec uses: actions/download-artifact@v3 @@ -458,8 +548,15 @@ jobs: needs: [linux-unit-tests, linux-integration-tests, windows-unit-tests, windows-integration-test] runs-on: ubuntu-latest steps: - - name: Checkout for .codecov.yml + - name: Checkout (Branch Push) uses: actions/checkout@v3 + if: github.event_name == 'push' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - name: Retrieve Linux Unit Test Coverage (Debug) uses: actions/download-artifact@v3 @@ -540,42 +637,49 @@ jobs: runs-on: windows-latest if: github.event_name == 'push' && contains(github.event.head_commit.message, '[APIDeploy]') steps: - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Parse API version - shell: powershell - run: | - [XML]$versionXML = Get-Content build/Version.props - $apiVersion = $versionXML.Project.PropertyGroup.TgsApiVersion - echo "TGS_API_VERSION=$apiVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - - name: Retrieve OpenAPI Spec - uses: actions/download-artifact@v3 - with: - name: openapi-spec - path: swagger + - name: Parse API version + shell: powershell + run: | + [XML]$versionXML = Get-Content build/Version.props + $apiVersion = $versionXML.Project.PropertyGroup.TgsApiVersion + echo "TGS_API_VERSION=$apiVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - - name: Create GitHub Release - uses: juitnow/github-action-create-release@v1 - id: create_release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: api-v${{ env.TGS_API_VERSION }} - release_name: tgstation-server API v${{ env.TGS_API_VERSION }} - body: The TGS HTTP API - commitish: ${{ github.event.head_commit.id }} + - name: Retrieve OpenAPI Spec + uses: actions/download-artifact@v3 + with: + name: openapi-spec + path: swagger - - name: Upload OpenApi Spec - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./swagger/swagger.json - asset_name: swagger.json - asset_content_type: application/json + - name: Create GitHub Release + uses: juitnow/github-action-create-release@v1 + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: api-v${{ env.TGS_API_VERSION }} + release_name: tgstation-server API v${{ env.TGS_API_VERSION }} + body: The TGS HTTP API + commitish: ${{ github.event.head_commit.id }} + + - name: Upload OpenApi Spec + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./swagger/swagger.json + asset_name: swagger.json + asset_content_type: application/json deploy-dm: name: Deploy DreamMaker API @@ -583,42 +687,48 @@ jobs: runs-on: windows-latest if: github.event_name == 'push' && contains(github.event.head_commit.message, '[DMDeploy]') steps: + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - - name: Parse DMAPI version - shell: powershell - run: | - [XML]$versionXML = Get-Content build/Version.props - $dmVersion = $versionXML.Project.PropertyGroup.TgsDmapiVersion - echo "TGS_DM_VERSION=$dmVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + - name: Parse DMAPI version + shell: powershell + run: | + [XML]$versionXML = Get-Content build/Version.props + $dmVersion = $versionXML.Project.PropertyGroup.TgsDmapiVersion + echo "TGS_DM_VERSION=$dmVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - - name: Zip DMAPI - shell: powershell - run: | - &"C:/Program Files/7-Zip/7z.exe" a DMAPI.zip ./src/DMAPI/* -tzip + - name: Zip DMAPI + shell: powershell + run: | + &"C:/Program Files/7-Zip/7z.exe" a DMAPI.zip ./src/DMAPI/* -tzip - - name: Create GitHub Release - uses: juitnow/github-action-create-release@v1 - id: create_release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: dmapi-v${{ env.TGS_DM_VERSION }} - release_name: tgstation-server DMAPI v${{ env.TGS_DM_VERSION }} - body: The TGS DMAPI \#tgs-dmapi-release - commitish: ${{ github.event.head_commit.id }} + - name: Create GitHub Release + uses: juitnow/github-action-create-release@v1 + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: dmapi-v${{ env.TGS_DM_VERSION }} + release_name: tgstation-server DMAPI v${{ env.TGS_DM_VERSION }} + body: The TGS DMAPI \#tgs-dmapi-release + commitish: ${{ github.event.head_commit.id }} - - name: Upload DMAPI Artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./DMAPI.zip - asset_name: DMAPI.zip - asset_content_type: application/zip + - name: Upload DMAPI Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./DMAPI.zip + asset_name: DMAPI.zip + asset_content_type: application/zip deploy-client: name: Deploy Nuget Packages @@ -626,29 +736,36 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'push' && contains(github.event.head_commit.message, '[NugetDeploy]') steps: - - name: Setup dotnet - uses: actions/setup-dotnet@v2 - with: - dotnet-version: ${{ env.TGS_DOTNET_VERSION }} + - name: Setup dotnet + uses: actions/setup-dotnet@v2 + with: + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Publish API to NuGet - uses: alirezanet/publish-nuget@v3.0.0 - with: - PROJECT_FILE_PATH: src/Tgstation.Server.Api/Tgstation.Server.Api.csproj - TAG_COMMIT: false - INCLUDE_SYMBOLS: true - NUGET_KEY: ${{ secrets.NUGET_API_KEY }} + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - - name: Publish Client to NuGet - uses: alirezanet/publish-nuget@v3.0.0 - with: - PROJECT_FILE_PATH: src/Tgstation.Server.Client/Tgstation.Server.Client.csproj - TAG_COMMIT: false - INCLUDE_SYMBOLS: true - NUGET_KEY: ${{ secrets.NUGET_API_KEY }} + - name: Publish API to NuGet + uses: alirezanet/publish-nuget@v3.0.0 + with: + PROJECT_FILE_PATH: src/Tgstation.Server.Api/Tgstation.Server.Api.csproj + TAG_COMMIT: false + INCLUDE_SYMBOLS: true + NUGET_KEY: ${{ secrets.NUGET_API_KEY }} + + - name: Publish Client to NuGet + uses: alirezanet/publish-nuget@v3.0.0 + with: + PROJECT_FILE_PATH: src/Tgstation.Server.Client/Tgstation.Server.Client.csproj + TAG_COMMIT: false + INCLUDE_SYMBOLS: true + NUGET_KEY: ${{ secrets.NUGET_API_KEY }} ensure-release: name: Ensure TGS Release is Latest GitHub Release @@ -656,16 +773,23 @@ jobs: runs-on: ubuntu-latest if: always() && github.event_name == 'push' && !contains(github.event.head_commit.message, '[TGSDeploy]') && (contains(github.event.head_commit.message, '[APIDeploy]') || contains(github.event.head_commit.message, '[DMDeploy]')) steps: - - name: Setup dotnet - uses: actions/setup-dotnet@v2 - with: - dotnet-version: ${{ env.TGS_DOTNET_VERSION }} + - name: Setup dotnet + uses: actions/setup-dotnet@v2 + with: + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Run ReleaseNotes with --ensure-release - run: dotnet run -c Release --project tools/ReleaseNotes --ensure-release + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" + + - name: Run ReleaseNotes with --ensure-release + run: dotnet run -c Release --project tools/ReleaseNotes --ensure-release deploy-tgs: name: Deploy TGS @@ -673,136 +797,150 @@ jobs: runs-on: windows-latest if: github.event_name == 'push' && github.event.ref == 'refs/heads/master' && contains(github.event.head_commit.message, '[TGSDeploy]') steps: - - name: Setup dotnet - uses: actions/setup-dotnet@v2 - with: - dotnet-version: ${{ env.TGS_DOTNET_VERSION }} + - name: Setup dotnet + uses: actions/setup-dotnet@v2 + with: + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Parse TGS version - shell: powershell - run: | - [XML]$versionXML = Get-Content build/Version.props - $tgsVersion = $versionXML.Project.PropertyGroup.TgsCoreVersion - echo "TGS_VERSION=$tgsVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - - name: Retrieve Server Service - uses: actions/download-artifact@v3 - with: - name: ServerService - path: ServerService + - name: Parse TGS version + shell: powershell + run: | + [XML]$versionXML = Get-Content build/Version.props + $tgsVersion = $versionXML.Project.PropertyGroup.TgsCoreVersion + echo "TGS_VERSION=$tgsVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - - name: Retrieve Server Console - uses: actions/download-artifact@v3 - with: - name: ServerConsole - path: ServerConsole + - name: Retrieve Server Service + uses: actions/download-artifact@v3 + with: + name: ServerService + path: ServerService - - name: Retrieve Server Update Package - uses: actions/download-artifact@v3 - with: - name: ServerUpdatePackage - path: ServerUpdatePackage + - name: Retrieve Server Console + uses: actions/download-artifact@v3 + with: + name: ServerConsole + path: ServerConsole - - name: Retrieve OpenAPI Spec - uses: actions/download-artifact@v3 - with: - name: openapi-spec - path: swagger + - name: Retrieve Server Update Package + uses: actions/download-artifact@v3 + with: + name: ServerUpdatePackage + path: ServerUpdatePackage - - name: Zip Artifacts - shell: powershell - run: | - &"C:/Program Files/7-Zip/7z.exe" a DMAPI.zip ./src/DMAPI/* -tzip - &"C:/Program Files/7-Zip/7z.exe" a ServerService.zip ./ServerService/* -tzip - &"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 OpenAPI Spec + uses: actions/download-artifact@v3 + with: + name: openapi-spec + path: swagger - - name: Generate Release Notes - run: dotnet run -c Release --project tools/ReleaseNotes ${{ env.TGS_VERSION }} + - name: Zip Artifacts + shell: powershell + run: | + &"C:/Program Files/7-Zip/7z.exe" a DMAPI.zip ./src/DMAPI/* -tzip + &"C:/Program Files/7-Zip/7z.exe" a ServerService.zip ./ServerService/* -tzip + &"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: Create GitHub Release - uses: actions/create-release@v1 - id: create_release - env: - GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} - with: - tag_name: tgstation-server-v${{ env.TGS_VERSION }} - release_name: tgstation-server-v${{ env.TGS_VERSION }} - body_path: release_notes.md - commitish: ${{ github.event.head_commit.id }} + - name: Generate Release Notes + run: dotnet run -c Release --project tools/ReleaseNotes ${{ env.TGS_VERSION }} - - name: Upload Server Console Artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./ServerConsole.zip - asset_name: ServerConsole.zip - asset_content_type: application/zip + - name: Create GitHub Release + uses: actions/create-release@v1 + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + with: + tag_name: tgstation-server-v${{ env.TGS_VERSION }} + release_name: tgstation-server-v${{ env.TGS_VERSION }} + body_path: release_notes.md + commitish: ${{ github.event.head_commit.id }} - - name: Upload Server Service Artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./ServerService.zip - asset_name: ServerService.zip - asset_content_type: application/zip + - name: Upload Server Console Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./ServerConsole.zip + asset_name: ServerConsole.zip + asset_content_type: application/zip - - name: Upload DMAPI Artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./DMAPI.zip - asset_name: DMAPI.zip - asset_content_type: application/zip + - name: Upload Server Service Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./ServerService.zip + asset_name: ServerService.zip + asset_content_type: application/zip - - name: Upload OpenApi Spec Artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./swagger/swagger.json - asset_name: swagger.json - asset_content_type: application/json + - name: Upload DMAPI Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./DMAPI.zip + asset_name: DMAPI.zip + asset_content_type: application/zip - - name: Upload Server Update Package Artifact - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./ServerUpdatePackage.zip - asset_name: ServerUpdatePackage.zip - asset_content_type: application/zip + - name: Upload OpenApi Spec Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./swagger/swagger.json + asset_name: swagger.json + asset_content_type: application/json + + - name: Upload Server Update Package Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./ServerUpdatePackage.zip + asset_name: ServerUpdatePackage.zip + asset_content_type: application/zip deploy-docker: name: Deploy TGS (Docker) needs: [deploy-tgs] runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout (Branch Push) + uses: actions/checkout@v3 + if: github.event_name == 'push' - - name: Parse TGS version - run: | - sudo apt-get update - sudo apt-get install -y xmlstarlet - echo "TGS_VERSION=$(xmlstarlet sel -N X="http://schemas.microsoft.com/developer/msbuild/2003" --template --value-of /X:Project/X:PropertyGroup/X:TgsCoreVersion build/Version.props)" >> $GITHUB_ENV + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' + with: + ref: "refs/pull/${{ github.event.number }}/merge" - - name: Docker Build and Push - uses: elgohr/Publish-Docker-Github-Action@v5 - with: - name: tgstation/server - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - dockerfile: build/Dockerfile - tags: "latest,v${{ env.TGS_VERSION }}" + - name: Parse TGS version + run: | + sudo apt-get update + sudo apt-get install -y xmlstarlet + echo "TGS_VERSION=$(xmlstarlet sel -N X="http://schemas.microsoft.com/developer/msbuild/2003" --template --value-of /X:Project/X:PropertyGroup/X:TgsCoreVersion build/Version.props)" >> $GITHUB_ENV + + - name: Docker Build and Push + uses: elgohr/Publish-Docker-Github-Action@v5 + with: + name: tgstation/server + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + dockerfile: build/Dockerfile + tags: "latest,v${{ env.TGS_VERSION }}" From 6957ccba26ad465b61b2af04387e64f4ac68ae04 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:34:12 -0400 Subject: [PATCH 03/23] Use standard pull_request event when working in the same repo --- .github/workflows/ci-suite.yml | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index c68d96617e..ab628812d8 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -5,6 +5,10 @@ on: branches: - dev - master + pull_request: + branches: + - dev + - master pull_request_target: types: [opened, reopened, labeled, synchronize] branches: @@ -29,26 +33,33 @@ jobs: name: Check CI Clearance needs: security-checkpoint runs-on: ubuntu-latest + if: github.event_name == 'pull_request_target' && github.pull_request.head.repo.id != github.repository.id steps: - name: Comment on new Fork PR - if: github.event_name != 'push' && github.pull_request.head.repo.id != github.repository.id && github.event.action == 'opened' + if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') uses: thollander/actions-comment-pull-request@v2 with: message: Thank you for contributing to tgstation-server! As this pull request is from a fork, we can't allow the CI actions which require repository secrets to run on it without approval. After a brief review to make sure you're not misusing those secrets, a maintainer will add the `CI Cleared` label to allow the CI suite to run. Maintainers, please note that any changes to workflow files will not be reflected in the CI run. - name: "Remove Stale 'CI Cleared' Label" - if: github.event_name != 'push' && github.pull_request.head.repo.id != github.repository.id && (github.event.action == 'synchronize' || github.event.action == 'reopened') + if: github.event.action == 'synchronize' || github.event.action == 'reopened' uses: actions-ecosystem/action-remove-labels@v1 with: labels: CI Cleared - name: Fail Clearance Check if PR has Unlabeled new Commits from Fork - if: github.event_name != 'push' && github.pull_request.head.repo.id != github.repository.id && (github.event.action != 'labeled' || !contains(github.event.pull_request.labels.*.name, 'CI Cleared')) + if: (github.event.action == 'synchronize' || github.event.action == 'reopened') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') run: exit 1 + start-ci-run-gate: + name: Start CI Run Gate + needs: security-checkpoint + runs-on: ubuntu-latest + if: always() && (needs.security-check.result == 'success' || needs.security-check.result == 'skipped') + analyze: name: Code Scanning - needs: security-checkpoint + needs: start-ci-run-gate runs-on: ubuntu-latest steps: - name: Install Node 12.X @@ -85,6 +96,7 @@ jobs: dmapi-build: name: Build DMAPI + needs: start-ci-run-gate env: BYOND_MAJOR: 515 BYOND_MINOR: 1592 @@ -141,6 +153,7 @@ jobs: dox-build: name: Build Doxygen Site runs-on: ubuntu-latest + needs: start-ci-run-gate steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -189,6 +202,7 @@ jobs: docker-build: name: Build Docker Image runs-on: ubuntu-latest + needs: start-ci-run-gate steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -205,7 +219,7 @@ jobs: linux-unit-tests: name: Linux Tests - needs: security-checkpoint + needs: start-ci-run-gate strategy: fail-fast: false matrix: @@ -244,7 +258,7 @@ jobs: windows-unit-tests: name: Windows Tests - needs: security-checkpoint + needs: start-ci-run-gate strategy: fail-fast: false matrix: @@ -283,7 +297,7 @@ jobs: windows-integration-test: name: Windows Live Tests - needs: [security-checkpoint, dmapi-build] + needs: dmapi-build env: TGS_TEST_DATABASE_TYPE: SqlServer TGS_TEST_DUMP_API_SPEC: yes @@ -362,7 +376,7 @@ jobs: linux-integration-tests: name: Linux Live Tests - needs: [security-checkpoint, dmapi-build] + needs: dmapi-build services: # We start all dbs here so we can just code the stuff once postgres: image: cyberboss/postgres-max-connections # Fork of _/postgres:latest with max_connections=500 becuase GitHub actions service containers have no way to set command lines. Rebuilds with updates. From 3eee1a2b032da94bed56ed7c5b5d2cbcd3dcabcf Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:35:32 -0400 Subject: [PATCH 04/23] Fix job self-dependency --- .github/workflows/ci-suite.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index ab628812d8..056ef7021e 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -31,7 +31,6 @@ concurrency: jobs: security-checkpoint: name: Check CI Clearance - needs: security-checkpoint runs-on: ubuntu-latest if: github.event_name == 'pull_request_target' && github.pull_request.head.repo.id != github.repository.id steps: From 1f073e6ca4a638f93002da98fd0d63a140afc112 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:37:11 -0400 Subject: [PATCH 05/23] Fix start-ci-run-gate having no steps --- .github/workflows/ci-suite.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 056ef7021e..11c1dd6b3b 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -55,6 +55,9 @@ jobs: needs: security-checkpoint runs-on: ubuntu-latest if: always() && (needs.security-check.result == 'success' || needs.security-check.result == 'skipped') + steps: + - name: GitHub Requires at Least One Step for a Job + run: exit 0 analyze: name: Code Scanning From 7aaa1619b8a99d53874f35335a940e98c992a30a Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:39:17 -0400 Subject: [PATCH 06/23] Actions doesn't like ternery expressions --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 11c1dd6b3b..a1f111c9ff 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -25,7 +25,7 @@ env: TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} concurrency: - group: "ci-${{ (github.event_name == 'push' ? github.head_ref : github.event.number) }}" + group: "ci-${{ github.event.number || github.head_ref }}" cancel-in-progress: true jobs: From ab9506895a13e996424b5373e2766cdf8cf23463 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:49:54 -0400 Subject: [PATCH 07/23] Fix typo and incorrect conditional in start-ci-run-gate if expression. --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index a1f111c9ff..4456f2f71e 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -54,7 +54,7 @@ jobs: name: Start CI Run Gate needs: security-checkpoint runs-on: ubuntu-latest - if: always() && (needs.security-check.result == 'success' || needs.security-check.result == 'skipped') + if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.pull_request.head.repo.id == github.repository.id))) steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 From b96b1cfc0dcd20944dcf3e1b25d290c8ae374845 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 11:53:35 -0400 Subject: [PATCH 08/23] I think the last job might have gotten cancelled because pull_request_target ran simultaneously? --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 4456f2f71e..26c92ff1e7 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -25,7 +25,7 @@ env: TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} concurrency: - group: "ci-${{ github.event.number || github.head_ref }}" + group: "ci-${{ github.event.number || github.head_ref }}-${{ github.event_name }}" cancel-in-progress: true jobs: From 2aeeff930304fc131e20780c8fefa6ba3977f02a Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 12:03:29 -0400 Subject: [PATCH 09/23] Workaround for GitHub runner bug https://github.com/actions/runner/issues/2205 Also fixes ordering of TGS deploy dependencies. The graph is honestly better this way. --- .github/workflows/ci-suite.yml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 26c92ff1e7..4671343bb1 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -62,6 +62,7 @@ jobs: analyze: name: Code Scanning needs: start-ci-run-gate + if: always() && needs.start-ci-run-gate.result == 'success' runs-on: ubuntu-latest steps: - name: Install Node 12.X @@ -99,6 +100,7 @@ jobs: dmapi-build: name: Build DMAPI needs: start-ci-run-gate + if: always() && needs.start-ci-run-gate.result == 'success' env: BYOND_MAJOR: 515 BYOND_MINOR: 1592 @@ -156,6 +158,7 @@ jobs: name: Build Doxygen Site runs-on: ubuntu-latest needs: start-ci-run-gate + if: always() && needs.start-ci-run-gate.result == 'success' steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -205,6 +208,7 @@ jobs: name: Build Docker Image runs-on: ubuntu-latest needs: start-ci-run-gate + if: always() && needs.start-ci-run-gate.result == 'success' steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -222,6 +226,7 @@ jobs: linux-unit-tests: name: Linux Tests needs: start-ci-run-gate + if: always() && needs.start-ci-run-gate.result == 'success' strategy: fail-fast: false matrix: @@ -261,6 +266,7 @@ jobs: windows-unit-tests: name: Windows Tests needs: start-ci-run-gate + if: always() && needs.start-ci-run-gate.result == 'success' strategy: fail-fast: false matrix: @@ -300,6 +306,7 @@ jobs: windows-integration-test: name: Windows Live Tests needs: dmapi-build + if: always() && needs.dmapi-build.result == 'success' env: TGS_TEST_DATABASE_TYPE: SqlServer TGS_TEST_DUMP_API_SPEC: yes @@ -379,6 +386,7 @@ jobs: linux-integration-tests: name: Linux Live Tests needs: dmapi-build + if: always() && needs.dmapi-build.result == 'success' services: # We start all dbs here so we can just code the stuff once postgres: image: cyberboss/postgres-max-connections # Fork of _/postgres:latest with max_connections=500 becuase GitHub actions service containers have no way to set command lines. Rebuilds with updates. @@ -530,6 +538,7 @@ jobs: validate-openapi-spec: name: OpenAPI Spec Validation needs: windows-integration-test + if: always() && needs.windows-integration-test.result == 'success' runs-on: ubuntu-latest steps: - name: Install Node 12.X @@ -562,6 +571,7 @@ jobs: upload-code-coverage: name: Upload Code Coverage needs: [linux-unit-tests, linux-integration-tests, windows-unit-tests, windows-integration-test] + if: always() && needs.linux-unit-tests.result == 'success' && needs.linux-integration-tests.result == 'success' && needs.windows-unit-tests.result == 'success' && needs.windows-integration-test.result == 'success' runs-on: ubuntu-latest steps: - name: Checkout (Branch Push) @@ -651,7 +661,7 @@ jobs: name: Deploy HTTP API needs: [upload-code-coverage, validate-openapi-spec] runs-on: windows-latest - if: github.event_name == 'push' && contains(github.event.head_commit.message, '[APIDeploy]') + if: always() && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[APIDeploy]') steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -701,7 +711,7 @@ jobs: name: Deploy DreamMaker API needs: [upload-code-coverage, validate-openapi-spec] runs-on: windows-latest - if: github.event_name == 'push' && contains(github.event.head_commit.message, '[DMDeploy]') + if: always() && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[DMDeploy]') steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -750,7 +760,7 @@ jobs: name: Deploy Nuget Packages needs: [upload-code-coverage, validate-openapi-spec] runs-on: ubuntu-latest - if: github.event_name == 'push' && contains(github.event.head_commit.message, '[NugetDeploy]') + if: always() && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[NugetDeploy]') steps: - name: Setup dotnet uses: actions/setup-dotnet@v2 @@ -787,7 +797,7 @@ jobs: name: Ensure TGS Release is Latest GitHub Release needs: [deploy-dm, deploy-http] runs-on: ubuntu-latest - if: always() && github.event_name == 'push' && !contains(github.event.head_commit.message, '[TGSDeploy]') && (contains(github.event.head_commit.message, '[APIDeploy]') || contains(github.event.head_commit.message, '[DMDeploy]')) + if: always() && (needs.deploy-dm.result == 'success' || needs.deploy-http.result == 'success') && !contains(github.event.head_commit.message, '[TGSDeploy]') steps: - name: Setup dotnet uses: actions/setup-dotnet@v2 @@ -809,9 +819,9 @@ jobs: deploy-tgs: name: Deploy TGS - needs: [upload-code-coverage, validate-openapi-spec] + needs: [deploy-dm, deploy-http, upload-code-coverage, validate-openapi-spec] runs-on: windows-latest - if: github.event_name == 'push' && github.event.ref == 'refs/heads/master' && contains(github.event.head_commit.message, '[TGSDeploy]') + if: always() && (needs.upload-code-coverage.result == 'success' || needs.validate-openapi-spec.result == 'success') && github.event_name == 'push' && github.event.ref == 'refs/heads/master' && contains(github.event.head_commit.message, '[TGSDeploy]') steps: - name: Setup dotnet uses: actions/setup-dotnet@v2 @@ -934,6 +944,7 @@ jobs: deploy-docker: name: Deploy TGS (Docker) needs: [deploy-tgs] + if: always() && needs.deploy-tgs.result == 'success' runs-on: ubuntu-latest steps: - name: Checkout (Branch Push) From 125a7cc20c7ba8325c0b37487067cef6d92dfd22 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 12:42:58 -0400 Subject: [PATCH 10/23] Fix for duplicate jobs on forked pull request --- .github/workflows/ci-suite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 4671343bb1..2160c43cfb 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -32,7 +32,7 @@ jobs: security-checkpoint: name: Check CI Clearance runs-on: ubuntu-latest - if: github.event_name == 'pull_request_target' && github.pull_request.head.repo.id != github.repository.id + if: github.event_name == 'pull_request_target' && github.pull_request.head.repo.id != github.pull_request.base.repo.id steps: - name: Comment on new Fork PR if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') @@ -54,7 +54,7 @@ jobs: name: Start CI Run Gate needs: security-checkpoint runs-on: ubuntu-latest - if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.pull_request.head.repo.id == github.repository.id))) + if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.pull_request.head.repo.id == github.pull_request.base.repo.id))) steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 From 886e28768cede512a693ea9fe10a72bb1ebdea69 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 13:01:17 -0400 Subject: [PATCH 11/23] Dump the GitHub context for debugging purposes. --- .github/workflows/ci-suite.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 2160c43cfb..6cab0071cb 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -29,6 +29,13 @@ concurrency: cancel-in-progress: true jobs: + dump-github-context: + name: Dump GitHub Context + runs-on: ubuntu-latest + steps: + - name: Dump GitHub Context + run: echo "${{ toJson(github) }}" + security-checkpoint: name: Check CI Clearance runs-on: ubuntu-latest From 1b49cad43c9b444afe9e8e2b922f4d42f8bc7640 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 13:05:12 -0400 Subject: [PATCH 12/23] Dump the GitHub context properly --- .github/workflows/ci-suite.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 6cab0071cb..5bcf90dc2f 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -34,7 +34,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Dump GitHub Context - run: echo "${{ toJson(github) }}" + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" security-checkpoint: name: Check CI Clearance From b593cb05ee5024842a30fb683f5c956b797fa030 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 13:33:44 -0400 Subject: [PATCH 13/23] The pull request is in the event --- .github/workflows/ci-suite.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 5bcf90dc2f..64bd71cbf6 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -29,19 +29,10 @@ concurrency: cancel-in-progress: true jobs: - dump-github-context: - name: Dump GitHub Context - runs-on: ubuntu-latest - steps: - - name: Dump GitHub Context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - security-checkpoint: name: Check CI Clearance runs-on: ubuntu-latest - if: github.event_name == 'pull_request_target' && github.pull_request.head.repo.id != github.pull_request.base.repo.id + if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id steps: - name: Comment on new Fork PR if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') @@ -63,7 +54,7 @@ jobs: name: Start CI Run Gate needs: security-checkpoint runs-on: ubuntu-latest - if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.pull_request.head.repo.id == github.pull_request.base.repo.id))) + if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id))) steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 From 163a40f4bec504a7cf8e541e7a9fb2c792522123 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 14:08:50 -0400 Subject: [PATCH 14/23] Fail the auto approve workflow if the GitHub API call fails --- .github/workflows/auto-approve-dominions-prs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-approve-dominions-prs.yml b/.github/workflows/auto-approve-dominions-prs.yml index d481b114e3..d2dd311018 100644 --- a/.github/workflows/auto-approve-dominions-prs.yml +++ b/.github/workflows/auto-approve-dominions-prs.yml @@ -22,4 +22,5 @@ jobs: --url https://api.github.com/repos/${{github.repository}}/pulls/${{github.event.number}}/reviews \ --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ --header 'content-type: application/json' \ - -d '{"event":"APPROVE"}' + -d '{"event":"APPROVE"}' \ + --fail From 479301241377ff9578e7348384d5d30deba1b054 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 15:02:26 -0400 Subject: [PATCH 15/23] Test resistance to rate limit issues - Unsealed ApiClient to allow tests to override with rate limit aware client that waits for rate limits to clear. - Allowed injecting into ServerClientFactory's static ApiClientFactory. - Use Octokit provided function for calculating retry delays. - Fixed a rogue newline semicolon. - Moved downgrade migrations test out of live tests. - Admin tests now check for V5 releases. - Fixed not disposing a StringContent in ApiClient. - Bump client patch version. - Added missing .ConfigureAwait(false) to async ApiClient call. --- build/Version.props | 2 +- src/Tgstation.Server.Client/ApiClient.cs | 147 ++++++------- .../ServerClientFactory.cs | 10 +- .../Tgstation.Server.Client.csproj | 2 +- .../Controllers/ApiController.cs | 3 +- .../Controllers/HomeController.cs | 3 +- .../Live/AdministrationTest.cs | 20 +- .../Live/RateLimitRetryingApiClient.cs | 39 ++++ .../Live/RateLimitRetryingApiClientFactory.cs | 19 ++ .../Live/TestLiveServer.cs | 199 +----------------- tests/Tgstation.Server.Tests/TestDatabase.cs | 173 +++++++++++++++ 11 files changed, 333 insertions(+), 284 deletions(-) create mode 100644 tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClient.cs create mode 100644 tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClientFactory.cs create mode 100644 tests/Tgstation.Server.Tests/TestDatabase.cs diff --git a/build/Version.props b/build/Version.props index 86d0b1a547..2f853ecd6c 100644 --- a/build/Version.props +++ b/build/Version.props @@ -7,7 +7,7 @@ 4.6.0 9.10.2 10.4.1 - 11.4.1 + 11.4.2 6.4.2 5.6.0 1.2.2 diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index e48727de1d..11d79a0513 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -25,7 +25,7 @@ using Tgstation.Server.Common; namespace Tgstation.Server.Client { /// - sealed class ApiClient : IApiClient + class ApiClient : IApiClient { /// /// PATCH . @@ -248,7 +248,9 @@ namespace Tgstation.Server.Client using (memoryStream) { +#pragma warning disable CA2000 // Dispose objects before losing scope var streamContent = new StreamContent(uploadStream ?? memoryStream); +#pragma warning restore CA2000 // Dispose objects before losing scope try { await RunRequest( @@ -259,7 +261,7 @@ namespace Tgstation.Server.Client false, cancellationToken) .ConfigureAwait(false); - streamContent = null; // CA2000 + streamContent = null; } finally { @@ -268,75 +270,6 @@ namespace Tgstation.Server.Client } } - /// - /// Attempt to refresh the bearer token in the . - /// - /// The for the operation. - /// A resulting in if the refresh was successful, otherwise. - async Task RefreshToken(CancellationToken cancellationToken) - { - if (tokenRefreshHeaders == null) - return false; - - var startingToken = headers.Token; - await semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - if (startingToken != headers.Token) - return true; - - var token = await RunRequest(Routes.Root, new object(), HttpMethod.Post, null, true, cancellationToken); - headers = new ApiHeaders(headers.UserAgent!, token.Bearer!); - } - catch (ClientException) - { - return false; - } - finally - { - semaphoreSlim.Release(); - } - - return true; - } - - /// - /// Main request method. - /// - /// The body . - /// The resulting POCO type. - /// The route to run. - /// The body of the request. - /// The method of the request. - /// The optional instance for the request. - /// If this is a token refresh operation. - /// The for the operation. - /// A resulting in the response on success. - Task RunRequest( - string route, - TBody? body, - HttpMethod method, - long? instanceId, - bool tokenRefresh, - CancellationToken cancellationToken) - where TBody : class - { - HttpContent? content = null; - if (body != null) - content = new StringContent( - JsonConvert.SerializeObject(body, typeof(TBody), Formatting.None, GetSerializerSettings()), - Encoding.UTF8, - ApiHeaders.ApplicationJsonMime); - - return RunRequest( - route, - content, - method, - instanceId, - tokenRefresh, - cancellationToken); - } - /// /// Main request method. /// @@ -348,7 +281,7 @@ namespace Tgstation.Server.Client /// If this is a token refresh operation. /// The for the operation. /// A resulting in the response on success. - async Task RunRequest( + protected virtual async Task RunRequest( string route, HttpContent? content, HttpMethod method, @@ -427,5 +360,75 @@ namespace Tgstation.Server.Client } } } + + /// + /// Attempt to refresh the bearer token in the . + /// + /// The for the operation. + /// A resulting in if the refresh was successful, otherwise. + async Task RefreshToken(CancellationToken cancellationToken) + { + if (tokenRefreshHeaders == null) + return false; + + var startingToken = headers.Token; + await semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + if (startingToken != headers.Token) + return true; + + var token = await RunRequest(Routes.Root, new object(), HttpMethod.Post, null, true, cancellationToken).ConfigureAwait(false); + headers = new ApiHeaders(headers.UserAgent!, token.Bearer!); + } + catch (ClientException) + { + return false; + } + finally + { + semaphoreSlim.Release(); + } + + return true; + } + + /// + /// Main request method. + /// + /// The body . + /// The resulting POCO type. + /// The route to run. + /// The body of the request. + /// The method of the request. + /// The optional instance for the request. + /// If this is a token refresh operation. + /// The for the operation. + /// A resulting in the response on success. + Task RunRequest( + string route, + TBody? body, + HttpMethod method, + long? instanceId, + bool tokenRefresh, + CancellationToken cancellationToken) + where TBody : class + { + HttpContent? content = null; + if (body != null) + content = new StringContent( + JsonConvert.SerializeObject(body, typeof(TBody), Formatting.None, GetSerializerSettings()), + Encoding.UTF8, + ApiHeaders.ApplicationJsonMime); + + using (content) + return RunRequest( + route, + content, + method, + instanceId, + tokenRefresh, + cancellationToken); + } } } diff --git a/src/Tgstation.Server.Client/ServerClientFactory.cs b/src/Tgstation.Server.Client/ServerClientFactory.cs index 040a3e8fff..b27e4dd9c5 100644 --- a/src/Tgstation.Server.Client/ServerClientFactory.cs +++ b/src/Tgstation.Server.Client/ServerClientFactory.cs @@ -17,13 +17,21 @@ namespace Tgstation.Server.Client /// /// The for the . /// - static readonly IApiClientFactory ApiClientFactory = new ApiClientFactory(); + internal static IApiClientFactory ApiClientFactory { get; set; } /// /// The for the . /// readonly ProductHeaderValue productHeaderValue; + /// + /// Initializes static members of the class. + /// + static ServerClientFactory() + { + ApiClientFactory = new ApiClientFactory(); + } + /// /// Initializes a new instance of the class. /// diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj index 2d279646c1..f681d99d2a 100644 --- a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj +++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj @@ -16,7 +16,7 @@ https://github.com/tgstation/tgstation-server 2018-2023 json web api tgstation-server tgstation ss13 byond client - Updated to latest API library patch. + Added missing Dispose() call to the StringContents for requests with bodies and added missing ConfigureAwait(false) to async call. true snupkg ../../build/analyzers.ruleset diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index f983eaa2b9..c563525265 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -257,7 +257,8 @@ namespace Tgstation.Server.Host.Controllers throw new ArgumentNullException(nameof(rateLimitException)); Logger.LogWarning(rateLimitException, "Exceeded GitHub rate limit!"); - var secondsString = Math.Ceiling((rateLimitException.Reset - DateTimeOffset.UtcNow).TotalSeconds).ToString(CultureInfo.InvariantCulture); + + var secondsString = Math.Ceiling(rateLimitException.GetRetryAfterTimeSpan().TotalSeconds).ToString(CultureInfo.InvariantCulture); Response.Headers.Add(HeaderNames.RetryAfter, secondsString); return StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessageResponse(ErrorCode.GitHubApiRateLimit)); } diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 2bf911a446..958ea12a2b 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -261,8 +261,7 @@ namespace Tgstation.Server.Host.Controllers return BadRequest(new ErrorMessageResponse(ErrorCode.OAuthProviderDisabled)); externalUserId = await validator - .ValidateResponseCode(ApiHeaders.Token, cancellationToken) - ; + .ValidateResponseCode(ApiHeaders.Token, cancellationToken); Logger.LogTrace("External {oAuthProvider} UID: {externalUserId}", oAuthProvider, externalUserId); } diff --git a/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs b/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs index 8d2b6ac4ca..e3253c5c0a 100644 --- a/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs +++ b/tests/Tgstation.Server.Tests/Live/AdministrationTest.cs @@ -53,24 +53,10 @@ namespace Tgstation.Server.Tests.Live async Task TestRead(CancellationToken cancellationToken) { - AdministrationResponse model; - try - { - model = await client.Read(cancellationToken); - } - catch (RateLimitException) - { - if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) - { - Assert.Inconclusive("GitHub rate limit hit while testing administration endpoint. Set environment variable TGS_TEST_GITHUB_TOKEN to fix this!"); - } + var model = await client.Read(cancellationToken); - // CI fails all the time b/c of this, ignore it - return; - } - - //we've released a few 4.x versions now, check the release checker is at least somewhat functional - Assert.IsTrue(4 <= model.LatestVersion.Major); + //we've released a few 5.x versions now, check the release checker is at least somewhat functional + Assert.IsTrue(4 < model.LatestVersion.Major); } } } diff --git a/tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClient.cs b/tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClient.cs new file mode 100644 index 0000000000..c45c12dbf8 --- /dev/null +++ b/tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClient.cs @@ -0,0 +1,39 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +using Tgstation.Server.Api; +using Tgstation.Server.Client; +using Tgstation.Server.Common; + +namespace Tgstation.Server.Tests.Live +{ + sealed class RateLimitRetryingApiClient : ApiClient + { + public RateLimitRetryingApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders, ApiHeaders tokenRefreshHeaders, bool authless) + : base(httpClient, url, apiHeaders, tokenRefreshHeaders, authless) + { + } + + protected override async Task RunRequest(string route, HttpContent content, HttpMethod method, long? instanceId, bool tokenRefresh, CancellationToken cancellationToken) + { + var hasGitHubToken = !String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN")); + while (true) + try + { + return await base.RunRequest(route, content, method, instanceId, tokenRefresh, cancellationToken); + } + catch (RateLimitException ex) when (hasGitHubToken && ex.RetryAfter.HasValue) + { + var now = DateTimeOffset.UtcNow; + + Console.WriteLine($"TEST ERROR RATE LIMITED: {ex}"); + + var sleepTime = ex.RetryAfter.Value - now; + Console.WriteLine($"Sleeping for {sleepTime.TotalMinutes} minutes and retrying..."); + await Task.Delay(sleepTime, cancellationToken); + } + } + } +} diff --git a/tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClientFactory.cs b/tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClientFactory.cs new file mode 100644 index 0000000000..4c063e672f --- /dev/null +++ b/tests/Tgstation.Server.Tests/Live/RateLimitRetryingApiClientFactory.cs @@ -0,0 +1,19 @@ +using System; + +using Tgstation.Server.Api; +using Tgstation.Server.Client; +using Tgstation.Server.Common; + +namespace Tgstation.Server.Tests.Live +{ + sealed class RateLimitRetryingApiClientFactory : IApiClientFactory + { + public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders tokenRefreshHeaders, bool authless) + => new RateLimitRetryingApiClient( + new HttpClient(), + url, + apiHeaders, + tokenRefreshHeaders, + authless); + } +} diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index e76e552077..3f19087080 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -12,9 +11,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -29,8 +25,6 @@ using Tgstation.Server.Client; using Tgstation.Server.Client.Components; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.Database; -using Tgstation.Server.Host.Database.Migrations; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.System; using Tgstation.Server.Tests.Live.Instance; @@ -77,6 +71,12 @@ namespace Tgstation.Server.Tests.Live } } + [TestInitialize] + public void Initialize() + { + ServerClientFactory.ApiClientFactory = new RateLimitRetryingApiClientFactory(); + } + [TestMethod] public async Task TestUpdateProtocolAndDisabledOAuth() { @@ -128,13 +128,6 @@ namespace Tgstation.Server.Tests.Live var updatedAssemblyVersion = FileVersionInfo.GetVersionInfo(updatedAssemblyPath); Assert.AreEqual(testUpdateVersion, Version.Parse(updatedAssemblyVersion.FileVersion).Semver()); } - catch (RateLimitException ex) - { - if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) - throw; - - Assert.Inconclusive("GitHub rate limit hit: {0}", ex); - } finally { serverCts.Cancel(); @@ -170,7 +163,7 @@ namespace Tgstation.Server.Tests.Live }, false, 5011)) { using var serverCts = new CancellationTokenSource(); - serverCts.CancelAfter(TimeSpan.FromMinutes(3)); + serverCts.CancelAfter(TimeSpan.FromHours(3)); var cancellationToken = serverCts.Token; var serverTask = controller.Run(cancellationToken); @@ -217,13 +210,6 @@ namespace Tgstation.Server.Tests.Live CheckServerUpdated(controller); } - catch (RateLimitException ex) - { - if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) - throw; - - Assert.Inconclusive("GitHub rate limit hit: {0}", ex); - } finally { serverCts.Cancel(); @@ -452,13 +438,6 @@ namespace Tgstation.Server.Tests.Live CheckServerUpdated(node1); CheckServerUpdated(node2); } - catch (RateLimitException ex) - { - if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) - throw; - - Assert.Inconclusive("GitHub rate limit hit: {0}", ex); - } finally { serverCts.Cancel(); @@ -636,13 +615,6 @@ namespace Tgstation.Server.Tests.Live Assert.AreEqual(2, node2Info.SwarmServers.Count); Assert.IsNotNull(node2Info.SwarmServers.SingleOrDefault(x => x.Identifier == "controller")); } - catch (RateLimitException ex) - { - if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN"))) - throw; - - Assert.Inconclusive("GitHub rate limit hit: {0}", ex); - } finally { serverCts.Cancel(); @@ -653,157 +625,6 @@ namespace Tgstation.Server.Tests.Live new LiveTestingServer(null, false).Dispose(); } - [TestMethod] - public async Task TestDownMigrations() - { - var connectionString = Environment.GetEnvironmentVariable("TGS_TEST_CONNECTION_STRING"); - - if (string.IsNullOrEmpty(connectionString)) - Assert.Inconclusive("No connection string configured in env var TGS_TEST_CONNECTION_STRING!"); - - var databaseTypeString = Environment.GetEnvironmentVariable("TGS_TEST_DATABASE_TYPE"); - if (!Enum.TryParse(databaseTypeString, out var databaseType)) - Assert.Inconclusive("No/invalid database type configured in env var TGS_TEST_DATABASE_TYPE!"); - - string migrationName = null; - DatabaseContext CreateContext() - { - string serverVersion = Environment.GetEnvironmentVariable($"{DatabaseConfiguration.Section}__{nameof(DatabaseConfiguration.ServerVersion)}"); - if (string.IsNullOrWhiteSpace(serverVersion)) - serverVersion = null; - switch (databaseType) - { - case DatabaseType.MySql: - case DatabaseType.MariaDB: - migrationName = nameof(MYInitialCreate); - return new MySqlDatabaseContext( - Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( - databaseType, - connectionString, - serverVersion)); - case DatabaseType.PostgresSql: - migrationName = nameof(PGCreate); - return new PostgresSqlDatabaseContext( - Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( - databaseType, - connectionString, - serverVersion)); - case DatabaseType.SqlServer: - migrationName = nameof(MSInitialCreate); - return new SqlServerDatabaseContext( - Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( - databaseType, - connectionString, - serverVersion)); - case DatabaseType.Sqlite: - migrationName = nameof(SLRebuild); - return new SqliteDatabaseContext( - Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( - databaseType, - connectionString, - serverVersion)); - } - - return null; - } - - using var context = CreateContext(); - await context.Database.EnsureDeletedAsync(); - await context.Database.MigrateAsync(default); - - // add usergroups and dummy instances for testing purposes - var group = new Host.Models.UserGroup - { - PermissionSet = new Host.Models.PermissionSet - { - AdministrationRights = AdministrationRights.ChangeVersion, - InstanceManagerRights = InstanceManagerRights.GrantPermissions - }, - Name = "TestGroup", - }; - - const string TestUserName = "TestUser42"; - var user = new Host.Models.User - { - Name = TestUserName, - CreatedAt = DateTimeOffset.UtcNow, - OAuthConnections = new List(), - CanonicalName = Host.Models.User.CanonicalizeName(TestUserName), - Enabled = false, - Group = group, - PasswordHash = "_", - }; - - var instance = new Host.Models.Instance - { - AutoUpdateInterval = 0, - ChatBotLimit = 1, - ChatSettings = new List(), - ConfigurationType = ConfigurationType.HostWrite, - DreamDaemonSettings = new Host.Models.DreamDaemonSettings - { - AllowWebClient = false, - AutoStart = false, - HeartbeatSeconds = 0, - DumpOnHeartbeatRestart = false, - Port = 1447, - SecurityLevel = DreamDaemonSecurity.Safe, - Visibility = DreamDaemonVisibility.Public, - StartupTimeout = 1000, - TopicRequestTimeout = 1000, - AdditionalParameters = string.Empty, - StartProfiler = false, - LogOutput = true, - }, - DreamMakerSettings = new Host.Models.DreamMakerSettings - { - ApiValidationPort = 1557, - ApiValidationSecurityLevel = DreamDaemonSecurity.Trusted, - RequireDMApiValidation = false, - Timeout = TimeSpan.FromSeconds(13), - }, - InstancePermissionSets = new List - { - new Host.Models.InstancePermissionSet - { - ByondRights = ByondRights.InstallCustomVersion, - ChatBotRights = ChatBotRights.None, - ConfigurationRights = ConfigurationRights.Read, - DreamDaemonRights = DreamDaemonRights.ReadRevision, - DreamMakerRights = DreamMakerRights.SetApiValidationPort, - InstancePermissionSetRights = InstancePermissionSetRights.Write, - PermissionSet = group.PermissionSet, - RepositoryRights = RepositoryRights.SetReference - } - }, - Name = "sfdsadfsa", - Online = false, - Path = "/a/b/c/d", - RepositorySettings = new Host.Models.RepositorySettings - { - AutoUpdatesKeepTestMerges = false, - AutoUpdatesSynchronize = false, - CommitterEmail = "email@eample.com", - CommitterName = "blubluh", - CreateGitHubDeployments = false, - PostTestMergeComment = false, - PushTestMergeCommits = false, - ShowTestMergeCommitters = false, - UpdateSubmodules = false, - }, - }; - - context.Users.Add(user); - context.Groups.Add(group); - context.Instances.Add(instance); - await context.Save(default); - - var dbServiceProvider = ((IInfrastructure)context.Database).Instance; - var migrator = dbServiceProvider.GetRequiredService(); - await migrator.MigrateAsync(migrationName, default); - await context.Database.EnsureDeletedAsync(); - } - [TestMethod] public async Task TestStandardTgsOperation() { @@ -817,7 +638,7 @@ namespace Tgstation.Server.Tests.Live using var server = new LiveTestingServer(null, true); - const int MaximumTestMinutes = 20; + const int MaximumTestMinutes = 180; using var hardTimeoutCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(MaximumTestMinutes)); var hardCancellationToken = hardTimeoutCancellationTokenSource.Token; using var serverCts = CancellationTokenSource.CreateLinkedTokenSource(hardCancellationToken); @@ -1066,12 +887,12 @@ namespace Tgstation.Server.Tests.Live } catch (ApiException ex) { - System.Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex.ErrorCode}: {ex.Message}\n{ex.AdditionalServerData}"); + Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex.ErrorCode}: {ex.Message}\n{ex.AdditionalServerData}"); throw; } catch (Exception ex) { - System.Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}"); + Console.WriteLine($"[{DateTimeOffset.UtcNow}] TEST ERROR: {ex}"); throw; } finally diff --git a/tests/Tgstation.Server.Tests/TestDatabase.cs b/tests/Tgstation.Server.Tests/TestDatabase.cs new file mode 100644 index 0000000000..12361f0949 --- /dev/null +++ b/tests/Tgstation.Server.Tests/TestDatabase.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.Database.Migrations; +using Tgstation.Server.Host.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Tgstation.Server.Tests +{ + [TestClass] + public sealed class TestDatabase + { + [TestMethod] + public async Task TestDownMigrations() + { + var connectionString = Environment.GetEnvironmentVariable("TGS_TEST_CONNECTION_STRING"); + + if (string.IsNullOrEmpty(connectionString)) + Assert.Inconclusive("No connection string configured in env var TGS_TEST_CONNECTION_STRING!"); + + var databaseTypeString = Environment.GetEnvironmentVariable("TGS_TEST_DATABASE_TYPE"); + if (!Enum.TryParse(databaseTypeString, out var databaseType)) + Assert.Inconclusive("No/invalid database type configured in env var TGS_TEST_DATABASE_TYPE!"); + + string migrationName = null; + DatabaseContext CreateContext() + { + string serverVersion = Environment.GetEnvironmentVariable($"{DatabaseConfiguration.Section}__{nameof(DatabaseConfiguration.ServerVersion)}"); + if (string.IsNullOrWhiteSpace(serverVersion)) + serverVersion = null; + switch (databaseType) + { + case DatabaseType.MySql: + case DatabaseType.MariaDB: + migrationName = nameof(MYInitialCreate); + return new MySqlDatabaseContext( + Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( + databaseType, + connectionString, + serverVersion)); + case DatabaseType.PostgresSql: + migrationName = nameof(PGCreate); + return new PostgresSqlDatabaseContext( + Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( + databaseType, + connectionString, + serverVersion)); + case DatabaseType.SqlServer: + migrationName = nameof(MSInitialCreate); + return new SqlServerDatabaseContext( + Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( + databaseType, + connectionString, + serverVersion)); + case DatabaseType.Sqlite: + migrationName = nameof(SLRebuild); + return new SqliteDatabaseContext( + Host.Database.Design.DesignTimeDbContextFactoryHelpers.CreateDatabaseContextOptions( + databaseType, + connectionString, + serverVersion)); + } + + return null; + } + + using var context = CreateContext(); + await context.Database.EnsureDeletedAsync(); + await context.Database.MigrateAsync(default); + + // add usergroups and dummy instances for testing purposes + var group = new Host.Models.UserGroup + { + PermissionSet = new Host.Models.PermissionSet + { + AdministrationRights = AdministrationRights.ChangeVersion, + InstanceManagerRights = InstanceManagerRights.GrantPermissions + }, + Name = "TestGroup", + }; + + const string TestUserName = "TestUser42"; + var user = new Host.Models.User + { + Name = TestUserName, + CreatedAt = DateTimeOffset.UtcNow, + OAuthConnections = new List(), + CanonicalName = Host.Models.User.CanonicalizeName(TestUserName), + Enabled = false, + Group = group, + PasswordHash = "_", + }; + + var instance = new Host.Models.Instance + { + AutoUpdateInterval = 0, + ChatBotLimit = 1, + ChatSettings = new List(), + ConfigurationType = ConfigurationType.HostWrite, + DreamDaemonSettings = new Host.Models.DreamDaemonSettings + { + AllowWebClient = false, + AutoStart = false, + HeartbeatSeconds = 0, + DumpOnHeartbeatRestart = false, + Port = 1447, + SecurityLevel = DreamDaemonSecurity.Safe, + Visibility = DreamDaemonVisibility.Public, + StartupTimeout = 1000, + TopicRequestTimeout = 1000, + AdditionalParameters = string.Empty, + StartProfiler = false, + LogOutput = true, + }, + DreamMakerSettings = new Host.Models.DreamMakerSettings + { + ApiValidationPort = 1557, + ApiValidationSecurityLevel = DreamDaemonSecurity.Trusted, + RequireDMApiValidation = false, + Timeout = TimeSpan.FromSeconds(13), + }, + InstancePermissionSets = new List + { + new Host.Models.InstancePermissionSet + { + ByondRights = ByondRights.InstallCustomVersion, + ChatBotRights = ChatBotRights.None, + ConfigurationRights = ConfigurationRights.Read, + DreamDaemonRights = DreamDaemonRights.ReadRevision, + DreamMakerRights = DreamMakerRights.SetApiValidationPort, + InstancePermissionSetRights = InstancePermissionSetRights.Write, + PermissionSet = group.PermissionSet, + RepositoryRights = RepositoryRights.SetReference + } + }, + Name = "sfdsadfsa", + Online = false, + Path = "/a/b/c/d", + RepositorySettings = new Host.Models.RepositorySettings + { + AutoUpdatesKeepTestMerges = false, + AutoUpdatesSynchronize = false, + CommitterEmail = "email@eample.com", + CommitterName = "blubluh", + CreateGitHubDeployments = false, + PostTestMergeComment = false, + PushTestMergeCommits = false, + ShowTestMergeCommitters = false, + UpdateSubmodules = false, + }, + }; + + context.Users.Add(user); + context.Groups.Add(group); + context.Instances.Add(instance); + await context.Save(default); + + var dbServiceProvider = ((IInfrastructure)context.Database).Instance; + var migrator = dbServiceProvider.GetRequiredService(); + await migrator.MigrateAsync(migrationName, default); + await context.Database.EnsureDeletedAsync(); + } + } +} From 5226a704450618b0e04b9dd1bc2035e88d86886f Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 15:23:33 -0400 Subject: [PATCH 16/23] Use a custom token for live tests GitHub Actions has a 5x less rate limit than a personal access token. Use it as little as possible --- .github/workflows/ci-suite.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 64bd71cbf6..afad0941db 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -21,7 +21,7 @@ env: TGS_TEST_DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} TGS_TEST_IRC_CHANNEL: ${{ secrets.IRC_CHANNEL }} TGS_TEST_IRC_CONNECTION_STRING: ${{ secrets.IRC_CONNECTION_STRING }} - TGS_TEST_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TGS_TEST_GITHUB_TOKEN: ${{ secrets.LIVE_TESTS_TOKEN }} TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} concurrency: @@ -690,7 +690,7 @@ jobs: uses: juitnow/github-action-create-release@v1 id: create_release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} with: tag_name: api-v${{ env.TGS_API_VERSION }} release_name: tgstation-server API v${{ env.TGS_API_VERSION }} @@ -700,7 +700,7 @@ jobs: - name: Upload OpenApi Spec uses: actions/upload-release-asset@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./swagger/swagger.json @@ -739,7 +739,7 @@ jobs: uses: juitnow/github-action-create-release@v1 id: create_release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} with: tag_name: dmapi-v${{ env.TGS_DM_VERSION }} release_name: tgstation-server DMAPI v${{ env.TGS_DM_VERSION }} @@ -749,7 +749,7 @@ jobs: - name: Upload DMAPI Artifact uses: actions/upload-release-asset@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ./DMAPI.zip From 63e00fd0112ed87585a2669a43542f8cf740a68d Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 15:34:27 -0400 Subject: [PATCH 17/23] Fix security-checkpoint job again --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index afad0941db..8686fb7e91 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -47,7 +47,7 @@ jobs: labels: CI Cleared - name: Fail Clearance Check if PR has Unlabeled new Commits from Fork - if: (github.event.action == 'synchronize' || github.event.action == 'reopened') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') + if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared')) run: exit 1 start-ci-run-gate: From bff25c7d5b905a614a57cdb885fc763d80f10cc9 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 15:49:25 -0400 Subject: [PATCH 18/23] Do not trigger on pull_request:labeled --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 8686fb7e91..57aa02726e 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -54,7 +54,7 @@ jobs: name: Start CI Run Gate needs: security-checkpoint runs-on: ubuntu-latest - if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id))) + if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || (github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event.action != 'labeled')))) steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 From adbc603c80cb89c9fc80976ce34d8e2ebe77d71c Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 16:04:10 -0400 Subject: [PATCH 19/23] Fix always() always never respecting a goddamn cancel command! --- .github/workflows/ci-suite.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 57aa02726e..9f70810b80 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -54,7 +54,7 @@ jobs: name: Start CI Run Gate needs: security-checkpoint runs-on: ubuntu-latest - if: always() && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || (github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event.action != 'labeled')))) + if: "!(cancelled() || failure()) && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || (github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event.action != 'labeled'))))" steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 @@ -62,7 +62,7 @@ jobs: analyze: name: Code Scanning needs: start-ci-run-gate - if: always() && needs.start-ci-run-gate.result == 'success' + if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" runs-on: ubuntu-latest steps: - name: Install Node 12.X @@ -100,7 +100,7 @@ jobs: dmapi-build: name: Build DMAPI needs: start-ci-run-gate - if: always() && needs.start-ci-run-gate.result == 'success' + if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" env: BYOND_MAJOR: 515 BYOND_MINOR: 1592 @@ -158,7 +158,7 @@ jobs: name: Build Doxygen Site runs-on: ubuntu-latest needs: start-ci-run-gate - if: always() && needs.start-ci-run-gate.result == 'success' + if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -208,7 +208,7 @@ jobs: name: Build Docker Image runs-on: ubuntu-latest needs: start-ci-run-gate - if: always() && needs.start-ci-run-gate.result == 'success' + if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -226,7 +226,7 @@ jobs: linux-unit-tests: name: Linux Tests needs: start-ci-run-gate - if: always() && needs.start-ci-run-gate.result == 'success' + if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" strategy: fail-fast: false matrix: @@ -266,7 +266,7 @@ jobs: windows-unit-tests: name: Windows Tests needs: start-ci-run-gate - if: always() && needs.start-ci-run-gate.result == 'success' + if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" strategy: fail-fast: false matrix: @@ -306,7 +306,7 @@ jobs: windows-integration-test: name: Windows Live Tests needs: dmapi-build - if: always() && needs.dmapi-build.result == 'success' + if: "!(cancelled() || failure()) && needs.dmapi-build.result == 'success'" env: TGS_TEST_DATABASE_TYPE: SqlServer TGS_TEST_DUMP_API_SPEC: yes @@ -386,7 +386,7 @@ jobs: linux-integration-tests: name: Linux Live Tests needs: dmapi-build - if: always() && needs.dmapi-build.result == 'success' + if: "!(cancelled() || failure()) && needs.dmapi-build.result == 'success'" services: # We start all dbs here so we can just code the stuff once postgres: image: cyberboss/postgres-max-connections # Fork of _/postgres:latest with max_connections=500 becuase GitHub actions service containers have no way to set command lines. Rebuilds with updates. @@ -538,7 +538,7 @@ jobs: validate-openapi-spec: name: OpenAPI Spec Validation needs: windows-integration-test - if: always() && needs.windows-integration-test.result == 'success' + if: "!(cancelled() || failure()) && needs.windows-integration-test.result == 'success'" runs-on: ubuntu-latest steps: - name: Install Node 12.X @@ -571,7 +571,7 @@ jobs: upload-code-coverage: name: Upload Code Coverage needs: [linux-unit-tests, linux-integration-tests, windows-unit-tests, windows-integration-test] - if: always() && needs.linux-unit-tests.result == 'success' && needs.linux-integration-tests.result == 'success' && needs.windows-unit-tests.result == 'success' && needs.windows-integration-test.result == 'success' + if: "!(cancelled() || failure()) && needs.linux-unit-tests.result == 'success' && needs.linux-integration-tests.result == 'success' && needs.windows-unit-tests.result == 'success' && needs.windows-integration-test.result == 'success'" runs-on: ubuntu-latest steps: - name: Checkout (Branch Push) @@ -661,7 +661,7 @@ jobs: name: Deploy HTTP API needs: [upload-code-coverage, validate-openapi-spec] runs-on: windows-latest - if: always() && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[APIDeploy]') + if: "!(cancelled() || failure()) && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[APIDeploy]')" steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -711,7 +711,7 @@ jobs: name: Deploy DreamMaker API needs: [upload-code-coverage, validate-openapi-spec] runs-on: windows-latest - if: always() && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[DMDeploy]') + if: "!(cancelled() || failure()) && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[DMDeploy]')" steps: - name: Checkout (Branch Push) uses: actions/checkout@v3 @@ -760,7 +760,7 @@ jobs: name: Deploy Nuget Packages needs: [upload-code-coverage, validate-openapi-spec] runs-on: ubuntu-latest - if: always() && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[NugetDeploy]') + if: "!(cancelled() || failure()) && needs.upload-code-coverage.result == 'success' && needs.validate-openapi-spec.result == 'success' && github.event_name == 'push' && contains(github.event.head_commit.message, '[NugetDeploy]')" steps: - name: Setup dotnet uses: actions/setup-dotnet@v2 @@ -797,7 +797,7 @@ jobs: name: Ensure TGS Release is Latest GitHub Release needs: [deploy-dm, deploy-http] runs-on: ubuntu-latest - if: always() && (needs.deploy-dm.result == 'success' || needs.deploy-http.result == 'success') && !contains(github.event.head_commit.message, '[TGSDeploy]') + if: "!(cancelled() || failure()) && (needs.deploy-dm.result == 'success' || needs.deploy-http.result == 'success') && !contains(github.event.head_commit.message, '[TGSDeploy]')" steps: - name: Setup dotnet uses: actions/setup-dotnet@v2 @@ -821,7 +821,7 @@ jobs: name: Deploy TGS needs: [deploy-dm, deploy-http, upload-code-coverage, validate-openapi-spec] runs-on: windows-latest - if: always() && (needs.upload-code-coverage.result == 'success' || needs.validate-openapi-spec.result == 'success') && github.event_name == 'push' && github.event.ref == 'refs/heads/master' && contains(github.event.head_commit.message, '[TGSDeploy]') + if: "!(cancelled() || failure()) && (needs.upload-code-coverage.result == 'success' || needs.validate-openapi-spec.result == 'success') && github.event_name == 'push' && github.event.ref == 'refs/heads/master' && contains(github.event.head_commit.message, '[TGSDeploy]')" steps: - name: Setup dotnet uses: actions/setup-dotnet@v2 @@ -944,7 +944,7 @@ jobs: deploy-docker: name: Deploy TGS (Docker) needs: [deploy-tgs] - if: always() && needs.deploy-tgs.result == 'success' + if: "!(cancelled() || failure()) && needs.deploy-tgs.result == 'success'" runs-on: ubuntu-latest steps: - name: Checkout (Branch Push) From 96d3269836ea4ac5cc58f2334e54d4fdaa65dae6 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 16:18:41 -0400 Subject: [PATCH 20/23] Fix using without await --- src/Tgstation.Server.Client/ApiClient.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index 11d79a0513..649745b609 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -405,7 +405,7 @@ namespace Tgstation.Server.Client /// If this is a token refresh operation. /// The for the operation. /// A resulting in the response on success. - Task RunRequest( + async Task RunRequest( string route, TBody? body, HttpMethod method, @@ -422,13 +422,14 @@ namespace Tgstation.Server.Client ApiHeaders.ApplicationJsonMime); using (content) - return RunRequest( + return await RunRequest( route, content, method, instanceId, tokenRefresh, - cancellationToken); + cancellationToken) + .ConfigureAwait(false); } } } From 746e79fed0b2429a843899d0410612fbe7fbec75 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 16:34:53 -0400 Subject: [PATCH 21/23] Do not run pull_request_target if the CI gate was skipped! --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 9f70810b80..71cfcface4 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -54,7 +54,7 @@ jobs: name: Start CI Run Gate needs: security-checkpoint runs-on: ubuntu-latest - if: "!(cancelled() || failure()) && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || (github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event.action != 'labeled'))))" + if: "!(cancelled() || failure()) && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || (github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event_name != 'pull_request_target'))))" steps: - name: GitHub Requires at Least One Step for a Job run: exit 0 From 3b4cfe18cfd4474d46c62cebd009dd9f6c3092af Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 17:08:58 -0400 Subject: [PATCH 22/23] Do not trigger CI when labelling a closed PR --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 71cfcface4..bad7fe6e79 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -32,7 +32,7 @@ jobs: security-checkpoint: name: Check CI Clearance runs-on: ubuntu-latest - if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id + if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id && github.event.pull_request.state == 'open' steps: - name: Comment on new Fork PR if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') From 34f59d815dc12c59e0ad24a49d89f08769468c94 Mon Sep 17 00:00:00 2001 From: Dominion Date: Sat, 29 Apr 2023 17:19:09 -0400 Subject: [PATCH 23/23] Allow auto-approve workflow to work if I PR from my fork --- .github/workflows/auto-approve-dominions-prs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/auto-approve-dominions-prs.yml b/.github/workflows/auto-approve-dominions-prs.yml index d2dd311018..b9fd7da731 100644 --- a/.github/workflows/auto-approve-dominions-prs.yml +++ b/.github/workflows/auto-approve-dominions-prs.yml @@ -4,8 +4,7 @@ name: "Auto-Approve Dominion's PRs" on: - pull_request: - types: [opened, synchronize] + pull_request_target: branches: - dev - master