Skip to content

Clown Dragon

Clown Dragon #22694

name: Build & Test Debug
env:
INTEGRATION_TEST_SHARD_COUNT: 4
# Cancel older jobs on PRs when new changes are pushed
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
on:
push:
branches: [ master, staging, stable, Starlight, starlight-dev, upstream-devtest, fix/tests ]
merge_group:
pull_request:
types: [ opened, reopened, synchronize, ready_for_review ]
branches: [ master, staging, stable, Starlight, starlight-dev, upstream-devtest, main ]
workflow_dispatch:
workflow_call:
inputs:
harvest:
description: "Run as a timings harvest: skip retries and publish the shard manifest."
type: boolean
default: false
# Fork pull requests run this workflow with no repository token privileges beyond
# read-only source access. PR comments are handled by test-report-comment.yml.
permissions: {}
jobs:
build:
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
integration_test_filters: ${{ steps.integration-test-shards.outputs.filters }}
integration_test_shards: ${{ steps.integration-test-shards.outputs.shards }}
steps:
- name: Checkout Master
uses: actions/checkout@v7
with:
fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
- name: Setup Submodule
run: |
git submodule update --init --recursive
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- name: Update Engine Submodules
run: |
cd RobustToolbox/
git submodule update --init --recursive
- name: Setup .NET
uses: actions/setup-dotnet@v6
- name: Setup Reviewdog
if: github.event_name == 'pull_request'
continue-on-error: true
uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0
with:
reviewdog_version: v0.21.0
- name: Build Project
if: github.event_name != 'pull_request'
run: dotnet build --configuration DebugOpt /m
- name: Build Project and collect C# diagnostics
if: github.event_name == 'pull_request'
run: dotnet build --configuration DebugOpt -p:Reviewdog=true /m
- name: Report C# diagnostics on changed lines
if: github.event_name == 'pull_request'
continue-on-error: true
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mapfile -d '' -t reports < <(find . -type f -name reviewdog.sarif -print0)
if (( ${#reports[@]} == 0 )); then
echo "::warning::The build did not produce any C# diagnostic reports."
exit 0
fi
jq -s '{version: "2.1.0", runs: [.[].runs[]
| .results = ((.results // [])
| map(select((.level // "warning") == "warning" or (.level // "warning") == "error")))]}' \
"${reports[@]}" > /tmp/csharp-diagnostics.sarif
reviewdog \
-f=sarif \
-name="C# diagnostics" \
-reporter=github-pr-annotations \
-filter-mode=added \
-fail-level=none \
< /tmp/csharp-diagnostics.sarif
- name: Discover Integration Tests
id: integration-test-shards
run: |
bin/Content.IntegrationTests/Content.IntegrationTests --list-tests json | \
python3 Tools/_Starlight/partition_tests.py generate "$INTEGRATION_TEST_SHARD_COUNT" .integration-filters
echo "filters=$(jq -c '.filters' .integration-filters/manifest.json)" >> "$GITHUB_OUTPUT"
echo "shards=$(jq -c '.filters | keys' .integration-filters/manifest.json)" >> "$GITHUB_OUTPUT"
- name: Upload shard manifest
if: inputs.harvest
uses: actions/upload-artifact@v6
with:
name: shard-manifest
path: .integration-filters/manifest.json
retention-days: 1
- name: Archive test binaries
run: tar --use-compress-program='zstd -T0' -cf /tmp/test-binaries.tar.zst bin
- name: Upload test binaries
uses: actions/upload-artifact@v6
with:
name: test-binaries
path: /tmp/test-binaries.tar.zst
retention-days: 1
compression-level: 0
content-tests:
needs: build
name: Content Tests
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup .NET
uses: actions/setup-dotnet@v6
- name: Download test binaries
uses: actions/download-artifact@v8
with:
name: test-binaries
- name: Extract test binaries
run: tar --use-compress-program='zstd -d -T0' -xf test-binaries.tar.zst && rm test-binaries.tar.zst
- name: Run Content.Tests
env:
HARVEST: ${{ inputs.harvest }}
RUN_DEBUG: ${{ runner.debug }}
run: |
PROGRESS=()
if [[ "$HARVEST" = "true" || "$RUN_DEBUG" = "1" ]]; then
PROGRESS=(--progress on --output Detailed )
fi
VERBOSITY=()
if [ "$RUN_DEBUG" = "1" ]; then
VERBOSITY=(--show-stdout All --show-stderr All)
fi
bin/Content.Tests/Content.Tests \
--retry-failed-tests 2 \
"${PROGRESS[@]}" \
"${VERBOSITY[@]}" \
--report-ctrf \
--report-ctrf-filename ctrf/ctrf-report.json \
--results-directory /tmp/test-results \
--settings .github/ci.runsettings
- name: Upload Content.Tests results
uses: actions/upload-artifact@v6
if: always()
continue-on-error: true
with:
name: test-nunit-content
path: |
/tmp/test-results/ctrf/ctrf-report.json
/tmp/test-results/Retries/**/ctrf/ctrf-report.json
retention-days: 7
if-no-files-found: error
integration-tests:
needs: build
strategy:
fail-fast: false
matrix:
shard: ${{ fromJSON(needs.build.outputs.integration_test_shards) }}
name: "Integration Tests (shard ${{ matrix.shard }})"
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 40
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Submodule
run: |
git submodule update --init --recursive
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- name: Update Engine Submodules
run: |
cd RobustToolbox/
git submodule update --init --recursive
- name: Setup .NET
uses: actions/setup-dotnet@v6
- name: Download test binaries
uses: actions/download-artifact@v8
with:
name: test-binaries
- name: Extract test binaries
run: tar --use-compress-program='zstd -d -T0' -xf test-binaries.tar.zst && rm test-binaries.tar.zst
- name: Run Content.IntegrationTests
env:
HARVEST: ${{ inputs.harvest }}
RUN_DEBUG: ${{ runner.debug }}
TEST_FILTER: ${{ fromJSON(needs.build.outputs.integration_test_filters)[matrix.shard] }}
run: |
if [ -z "$TEST_FILTER" ]; then
echo "No tests assigned to shard ${{ matrix.shard }}"
exit 0
fi
PROGRESS=()
if [[ "$HARVEST" = "true" || $RUN_DEBUG == 1 ]]; then
PROGRESS=(--progress on --output Detailed)
while :; do
free --human --line --seconds 1 --count 1 | tr -d '\r\n'
echo
sleep 10
done &
MEMWATCH=$!
trap 'kill "$MEMWATCH" 2>/dev/null || true' EXIT
fi
RETRY=(--retry-failed-tests 2)
if [ "$HARVEST" = "true" ]; then
RETRY=()
fi
VERBOSITY=()
if [ "$RUN_DEBUG" = "1" ]; then
VERBOSITY=(--show-stdout All --show-stderr All)
fi
bin/Content.IntegrationTests/Content.IntegrationTests \
--filter "$TEST_FILTER" \
--minimum-expected-tests 1 \
"${RETRY[@]}" \
"${PROGRESS[@]}" \
"${VERBOSITY[@]}" \
--hangdump \
--hangdump-timeout 5m \
--hangdump-filename "hang-dump-shard-${{ matrix.shard }}-{pname}-{pid}-{time}.dmp" \
--report-ctrf \
--report-ctrf-filename "shard-${{ matrix.shard }}-ctrf-report-{time}.json" \
--results-directory /tmp/test-results/ \
--settings .github/ci.runsettings
- name: Upload test results
uses: actions/upload-artifact@v6
if: always()
continue-on-error: true
with:
name: test-nunit-shard-${{ matrix.shard }}
path: |
/tmp/test-results/shard-*-ctrf-report-*.json
/tmp/test-results/Retries/**/shard-*-ctrf-report-*.json
retention-days: 7
if-no-files-found: ignore
- name: Upload hang dump
id: upload-hang-dump
uses: actions/upload-artifact@v6
if: always()
continue-on-error: true
with:
name: hang-dump-shard-${{ matrix.shard }}
path: |
/tmp/test-results/**/hang-dump-shard-*.dmp
/tmp/test-results/**/hang-dump-shard-*.log
retention-days: 3
if-no-files-found: ignore
- name: Report hung tests
if: always()
continue-on-error: true
env:
HANG_DUMP_ARTIFACT_URL: ${{ steps.upload-hang-dump.outputs.artifact-url }}
run: |
shopt -s nullglob globstar
dumps=(/tmp/test-results/**/hang-dump-shard-*.dmp)
logs=(/tmp/test-results/**/hang-dump-shard-*.log)
if [ ${#dumps[@]} -eq 0 ] && [ ${#logs[@]} -eq 0 ]; then
exit 0
fi
echo "## :warning: Hung tests detected (shard ${{ matrix.shard }})" >> "$GITHUB_STEP_SUMMARY"
if [ ${#logs[@]} -gt 0 ]; then
echo '```text' >> "$GITHUB_STEP_SUMMARY"
for log in "${logs[@]}"; do
cat "$log" >> "$GITHUB_STEP_SUMMARY"
done
echo '```' >> "$GITHUB_STEP_SUMMARY"
fi
if [ -n "$HANG_DUMP_ARTIFACT_URL" ]; then
echo ":floppy_disk: [Download hang dump]($HANG_DUMP_ARTIFACT_URL)" >> "$GITHUB_STEP_SUMMARY"
fi
yaml-linter:
name: YAML Linter
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
- name: Setup Submodule
run: |
git submodule update --init --recursive
- name: Pull engine updates
uses: space-wizards/submodule-dependency@v0.1.5
- name: Update Engine Submodules
run: |
cd RobustToolbox/
git submodule update --init --recursive
- name: Setup .NET
uses: actions/setup-dotnet@v6
- name: Setup Reviewdog
if: github.event_name == 'pull_request'
uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1.5.0
with:
reviewdog_version: v0.21.0
- name: Download test binaries
uses: actions/download-artifact@v8
with:
name: test-binaries
- name: Extract test binaries
run: tar --use-compress-program='zstd -d -T0' -xf test-binaries.tar.zst && rm test-binaries.tar.zst
- name: Run Linter
if: github.event_name != 'pull_request'
run: bin/Content.YAMLLinter/Content.YAMLLinter
- name: Run Linter and report changed YAML lines
if: github.event_name == 'pull_request'
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set +e
bin/Content.YAMLLinter/Content.YAMLLinter > /tmp/yaml-linter.log 2>&1
linter_status=$?
set -e
cat /tmp/yaml-linter.log
set +e
reviewdog \
-efm='%f:%l:%c: %t%*[^:]: %m' \
-name="YAML linter" \
-reporter=github-pr-annotations \
-filter-mode=added \
-fail-level=error \
< /tmp/yaml-linter.log
reviewdog_status=$?
set -e
if (( linter_status > 2 )); then
echo "::error::YAML linter crashed with exit code ${linter_status}."
exit "${linter_status}"
fi
if (( linter_status == 2 )); then
echo "::error::YAML field validation failed; see the log for non-localized errors."
exit 1
fi
exit "${reviewdog_status}"
ci-success:
name: Debug CI Required
if: ${{ always() }}
needs:
- build
- content-tests
- integration-tests
- yaml-linter
runs-on: ubuntu-slim
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Download CTRF test results
continue-on-error: true
uses: actions/download-artifact@v8
with:
pattern: test-nunit-*
path: /tmp/ctrf-results
- name: Generate Content Tests report
if: always()
continue-on-error: true
uses: ctrf-io/github-test-reporter@v1
with:
title: Content Test Results
report-path: '/tmp/ctrf-results/test-nunit-content/**/*.json'
artifact-name: ct-report
upload-artifact: true
github-report: true
failed-folded-report: true
- name: Generate Integration Tests report
if: always()
continue-on-error: true
uses: ctrf-io/github-test-reporter@v1
with:
report-path: '/tmp/ctrf-results/test-nunit-shard-*/**/*.json'
title: Integration Test Results
github-report: true
failed-folded-report: true
artifact-name: it-report
upload-artifact: true
- name: Fail if any dependency was not successful
if: ${{ needs.build.result != 'success' || needs['content-tests'].result != 'success' || needs['integration-tests'].result != 'success' || needs['yaml-linter'].result != 'success' }}
run: |
echo "build=${{ needs.build.result }}"
echo "content-tests=${{ needs['content-tests'].result }}"
echo "integration-tests=${{ needs['integration-tests'].result }}"
echo "yaml-linter=${{ needs['yaml-linter'].result }}"
exit 1
- name: CI succeeded
run: exit 0
cleanup:
name: Cleanup Artifacts
needs:
- content-tests
- integration-tests
- yaml-linter
- ci-success
runs-on: ubuntu-slim
steps:
- name: Delete test binaries
if: ${{ !(runner.debug == '1' || inputs.harvest == true || needs.ci-success.result == 'success') }}
continue-on-error: true
uses: geekyeggo/delete-artifact@v6
with:
name: |
test-*