diff --git a/.github/workflows/auto_changelog.yml b/.github/workflows/auto_changelog.yml index a7280acd15f9..1469a0f983dc 100644 --- a/.github/workflows/auto_changelog.yml +++ b/.github/workflows/auto_changelog.yml @@ -14,7 +14,7 @@ jobs: if: github.event.pull_request.merged == true steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run auto changelog uses: actions/github-script@v6 with: diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml index 91ab12cdb19f..740b5d7d9eb2 100644 --- a/.github/workflows/autowiki.yml +++ b/.github/workflows/autowiki.yml @@ -20,13 +20,13 @@ jobs: echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.secrets_set.outputs.SECRETS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Restore BYOND cache if: steps.secrets_set.outputs.SECRETS_ENABLED - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/BYOND - key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }} + key: ${{ runner.os }}-byond-${{ hashFiles('dependencies.sh') }} - name: Install rust-g if: steps.secrets_set.outputs.SECRETS_ENABLED run: | diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml index 22abc209c990..46d72c298a2c 100644 --- a/.github/workflows/ci_suite.yml +++ b/.github/workflows/ci_suite.yml @@ -13,98 +13,125 @@ on: merge_group: branches: - master + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: run_linters: if: ( !contains(github.event.head_commit.message, '[ci skip]') ) name: Run Linters runs-on: ubuntu-22.04 - concurrency: - group: run_linters-${{ github.head_ref || github.run_id }} - cancel-in-progress: true + timeout-minutes: 5 + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore SpacemanDMM cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/SpacemanDMM - key: ${{ runner.os }}-spacemandmm + key: ${{ runner.os }}-spacemandmm-${{ hashFiles('dependencies.sh') }} + restore-keys: | + ${{ runner.os }}-spacemandmm- - name: Restore Yarn cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tgui/.yarn/cache key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }} restore-keys: | - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ runner.os }}-yarn- + - name: Restore Node cache + uses: actions/cache@v4 + with: + path: ~/.nvm + key: ${{ runner.os }}-node-${{ hashFiles('dependencies.sh') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Restore Bootstrap cache + uses: actions/cache@v4 + with: + path: tools/bootstrap/.cache + key: ${{ runner.os }}-bootstrap-${{ hashFiles('tools/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-bootstrap- - name: Restore Rust cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo - key: ${{ runner.os }}-rust + key: ${{ runner.os }}-rust-${{ hashFiles('tools/ci/ci_dependencies.sh')}} restore-keys: | - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ runner.os }}-rust- + - name: Install OpenDream + uses: robinraju/release-downloader@v1.9 + with: + repository: "OpenDreamProject/OpenDream" + tag: "latest" + fileName: "DMCompiler_linux-x64.tar.gz" + extract: true - name: Install Tools run: | pip3 install setuptools bash tools/ci/install_node.sh bash tools/ci/install_spaceman_dmm.sh dreamchecker - cargo install ripgrep --features pcre2 + bash tools/ci/install_ripgrep.sh tools/bootstrap/python -c '' - - name: Run Linters + - name: Give Linters A Go + id: linter-setup + run: ':' + - name: Run Grep Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_grep.sh + - name: Ticked File Enforcement + if: steps.linter-setup.conclusion == 'success' && !cancelled() run: | - bash tools/ci/check_filedirs.sh tgstation.dme - bash tools/ci/check_changelogs.sh - bash tools/ci/check_grep.sh - bash tools/ci/check_misc.sh tools/bootstrap/python tools/ticked_file_enforcement/ticked_file_enforcement.py < tools/ticked_file_enforcement/schemas/tgstation_dme.json tools/bootstrap/python tools/ticked_file_enforcement/ticked_file_enforcement.py < tools/ticked_file_enforcement/schemas/unit_tests.json - tools/bootstrap/python -m tools.maplint.source --github - tools/build/build --ci lint tgui-test - tools/bootstrap/python -m define_sanity.check - tools/bootstrap/python -m dmi.test - tools/bootstrap/python -m mapmerge2.dmm_test - ~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1 - - name: Annotate Lints - uses: yogstation13/DreamAnnotate@v2 - if: success() || failure() - with: - outputFile: output-annotations.txt - - odlint: - if: ( !contains(github.event.head_commit.message, '[ci skip]') ) - name: "Lint with OpenDream" - runs-on: ubuntu-22.04 - concurrency: - group: odlint-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - uses: robinraju/release-downloader@v1.9 - with: - repository: "OpenDreamProject/OpenDream" - tag: "latest" - fileName: "DMCompiler_linux-x64.tar.gz" - extract: true + - name: Check Define Sanity + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m define_sanity.check + - name: Run DreamChecker + if: steps.linter-setup.conclusion == 'success' && !cancelled() + shell: bash + run: ~/dreamchecker 2>&1 | bash tools/ci/annotate_dm.sh - name: Run OpenDream + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: ./DMCompiler_linux-x64/DMCompiler tgstation.dme --suppress-unimplemented --define=CIBUILDING | bash tools/ci/annotate_od.sh + - name: Run Map Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() run: | - ./DMCompiler_linux-x64/DMCompiler tgstation.dme --suppress-unimplemented --define=CIBUILDING + tools/bootstrap/python -m mapmerge2.dmm_test + tools/bootstrap/python -m tools.maplint.source + - name: Run DMI Tests + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/bootstrap/python -m dmi.test + - name: Check File Directories + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_filedirs.sh tgstation.dme + - name: Check Changelogs + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_changelogs.sh + - name: Check Miscellaneous Files + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: bash tools/ci/check_misc.sh + - name: Run TGUI Checks + if: steps.linter-setup.conclusion == 'success' && !cancelled() + run: tools/build/build --ci lint tgui-test compile_all_maps: if: ( !contains(github.event.head_commit.message, '[ci skip]') ) name: Compile Maps needs: [collect_data] - runs-on: ubuntu-20.04 - concurrency: - group: compile_all_maps-${{ github.head_ref || github.run_id }} - cancel-in-progress: true + runs-on: ubuntu-22.04 + timeout-minutes: 5 + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore BYOND cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/BYOND - key: ${{ runner.os }}-byond + key: ${{ runner.os }}-byond-${{ hashFiles('dependencies.sh') }} - name: Compile All Maps run: | bash tools/ci/install_byond.sh @@ -119,16 +146,15 @@ jobs: collect_data: if: ( !contains(github.event.head_commit.message, '[ci skip]') ) name: Collect data for other tasks - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + timeout-minutes: 5 outputs: maps: ${{ steps.map_finder.outputs.maps }} alternate_tests: ${{ steps.alternate_test_finder.outputs.alternate_tests }} max_required_byond_client: ${{ steps.max_required_byond_client.outputs.max_required_byond_client }} - concurrency: - group: find_all_maps-${{ github.head_ref || github.run_id }} - cancel-in-progress: true + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Find Maps id: map_finder run: | @@ -151,29 +177,26 @@ jobs: if: ( !contains(github.event.head_commit.message, '[ci skip]') ) name: Integration Tests needs: [collect_data] + strategy: fail-fast: false matrix: map: ${{ fromJSON(needs.collect_data.outputs.maps).paths }} - concurrency: - group: run_all_tests-${{ github.head_ref || github.run_id }}-${{ matrix.map }} - cancel-in-progress: true + uses: ./.github/workflows/run_integration_tests.yml with: map: ${{ matrix.map }} max_required_byond_client: ${{needs.collect_data.outputs.max_required_byond_client}} run_alternate_tests: - if: "!contains(github.event.head_commit.message, '[ci skip]') && needs.find_all_maps.outputs.alternate_tests != '[]'" + if: ( !contains(github.event.head_commit.message, '[ci skip]') && needs.collect_data.outputs.alternate_tests != '[]' ) name: Alternate Tests needs: [collect_data] strategy: fail-fast: false matrix: setup: ${{ fromJSON(needs.collect_data.outputs.alternate_tests) }} - concurrency: - group: run_all_tests-${{ github.head_ref || github.run_id }}-${{ matrix.setup.major }}.${{ matrix.setup.minor }}-${{ matrix.setup.map }} - cancel-in-progress: true + uses: ./.github/workflows/run_integration_tests.yml with: map: ${{ matrix.setup.map }} @@ -182,23 +205,27 @@ jobs: max_required_byond_client: ${{needs.collect_data.outputs.max_required_byond_client}} check_alternate_tests: - if: "!contains(github.event.head_commit.message, '[ci skip]') && needs.find_all_maps.outputs.alternate_tests != '[]'" + if: ( !contains(github.event.head_commit.message, '[ci skip]') && needs.collect_data.outputs.alternate_tests != '[]' ) name: Check Alternate Tests needs: [run_alternate_tests] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + timeout-minutes: 5 steps: - run: echo Alternate tests passed. compare_screenshots: - if: "!contains(github.event.head_commit.message, '[ci skip]') && (success() || failure())" + if: ( !contains(github.event.head_commit.message, '[ci skip]') && (success() || failure()) ) needs: [run_all_tests, run_alternate_tests] name: Compare Screenshot Tests - runs-on: ubuntu-20.04 + timeout-minutes: 15 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Setup directory + run: mkdir -p artifacts # If we ever add more artifacts, this is going to break, but it'll be obvious. - name: Download screenshot tests - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: artifacts - name: ls -R @@ -219,7 +246,7 @@ jobs: echo ${{ github.event.pull_request.number }} > artifacts/screenshot_comparisons/pull_request_number.txt - name: Upload bad screenshots if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: bad-screenshots path: artifacts/screenshot_comparisons @@ -229,19 +256,17 @@ jobs: name: Windows Build needs: [collect_data] runs-on: windows-latest - concurrency: - group: test_windows-${{ github.head_ref || github.run_id }} - cancel-in-progress: true + timeout-minutes: 5 + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore Yarn cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: tgui/.yarn/cache key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }} restore-keys: | - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ runner.os }}-yarn- - name: Compile run: pwsh tools/ci/build.ps1 env: diff --git a/.github/workflows/codeowner_reviews.yml b/.github/workflows/codeowner_reviews.yml index ed06f9b8a99d..e6cfb9802790 100644 --- a/.github/workflows/codeowner_reviews.yml +++ b/.github/workflows/codeowner_reviews.yml @@ -9,10 +9,11 @@ jobs: assign-users: runs-on: ubuntu-latest + timeout-minutes: 5 steps: # Checks-out your repository under $GITHUB_WORKSPACE, so the job can access it - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 #Parse the Codeowner file on non draft PRs - name: CodeOwnersParser diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index fefd9de30ea2..dd60b07b86bd 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -31,7 +31,7 @@ jobs: sudo apt-get install dos2unix - name: "Checkout" if: steps.value_holder.outputs.ACTIONS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 25 persist-credentials: false diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 529a706ff6da..6daec1ded105 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -5,10 +5,10 @@ on: workflow_dispatch: jobs: publish: - if: "!contains(github.event.head_commit.message, '[ci skip]')" + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build and Publish Docker Image to Registry uses: elgohr/Publish-Docker-Github-Action@v5 diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index 5f13472ca524..0dc79584b9e4 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -9,13 +9,13 @@ jobs: generate_documentation: permissions: contents: write # for JamesIves/github-pages-deploy-action to push changes in repo - if: "!contains(github.event.head_commit.message, '[ci skip]')" + if: ( !contains(github.event.head_commit.message, '[ci skip]') ) runs-on: ubuntu-20.04 concurrency: gen-docs steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/SpacemanDMM key: ${{ runner.os }}-spacemandmm-${{ secrets.CACHE_PURGE_KEY }} diff --git a/.github/workflows/pr_emoji.yml b/.github/workflows/pr_emoji.yml index 219b319298ad..a9d35d2456fb 100644 --- a/.github/workflows/pr_emoji.yml +++ b/.github/workflows/pr_emoji.yml @@ -10,7 +10,7 @@ jobs: title_and_changelog: runs-on: ubuntu-20.04 steps: - - uses: Wayland-Smithy/emoji-stripper-action@8f4c2fe9748bb9b02f105be4e72a1a42b0f34d84 + - uses: Wayland-Smithy/emoji-stripper-action@de0c1d158edee50700583d6454aa5f5117337599 with: repo-token: ${{ secrets.GITHUB_TOKEN }} title: true diff --git a/.github/workflows/remove_guide_comments.yml b/.github/workflows/remove_guide_comments.yml index d5d405909e21..e3a4ac3feda0 100644 --- a/.github/workflows/remove_guide_comments.yml +++ b/.github/workflows/remove_guide_comments.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Remove guide comments uses: actions/github-script@v6 with: diff --git a/.github/workflows/rerun_flaky_tests.yml b/.github/workflows/rerun_flaky_tests.yml index 24c3ec95197d..7f498de14430 100644 --- a/.github/workflows/rerun_flaky_tests.yml +++ b/.github/workflows/rerun_flaky_tests.yml @@ -10,7 +10,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Rerun flaky tests uses: actions/github-script@v6 with: @@ -22,7 +22,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.run_attempt == 2 }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Report flaky tests uses: actions/github-script@v6 with: diff --git a/.github/workflows/run_integration_tests.yml b/.github/workflows/run_integration_tests.yml index 49978ca25a3f..bcbcde942407 100644 --- a/.github/workflows/run_integration_tests.yml +++ b/.github/workflows/run_integration_tests.yml @@ -16,9 +16,11 @@ on: max_required_byond_client: required: true type: string + jobs: run_integration_tests: runs-on: ubuntu-latest + timeout-minutes: 15 services: mysql: image: mysql:latest @@ -28,12 +30,12 @@ jobs: - 3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Restore BYOND cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/BYOND - key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }} + key: ${{ runner.os }}-byond-${{ hashFiles('dependencies.sh') }} - name: Setup database run: | sudo systemctl start mysql @@ -64,9 +66,9 @@ jobs: bash tools/ci/run_server.sh ${{ inputs.map }} - name: Upload screenshot tests if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: test_artifacts_${{ inputs.map }} + name: test_artifacts_${{ inputs.map }}_${{ inputs.major }}_${{ inputs.minor }} path: data/screenshots_new/ retention-days: 1 - name: Check client Compatibility diff --git a/.github/workflows/show_screenshot_test_results.yml b/.github/workflows/show_screenshot_test_results.yml index 8e463e46c05f..70eed354d5c6 100644 --- a/.github/workflows/show_screenshot_test_results.yml +++ b/.github/workflows/show_screenshot_test_results.yml @@ -11,7 +11,7 @@ on: - completed jobs: show_screenshot_test_results: - if: "!contains(github.event.head_commit.message, '[ci skip]') && github.event.workflow_run.run_attempt == 1" + if: ( !contains(github.event.head_commit.message, '[ci skip]') && github.event.workflow_run.run_attempt == 1 ) name: Show Screenshot Test Results runs-on: ubuntu-20.04 steps: @@ -25,7 +25,7 @@ jobs: echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.secrets_set.outputs.SECRETS_ENABLED - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare module if: steps.secrets_set.outputs.SECRETS_ENABLED run: | diff --git a/.github/workflows/test_merge_bot.yml b/.github/workflows/test_merge_bot.yml index ebf8928f435d..224ff0707514 100644 --- a/.github/workflows/test_merge_bot.yml +++ b/.github/workflows/test_merge_bot.yml @@ -23,7 +23,7 @@ jobs: echo "GET_TEST_MERGES_URL=$SECRET_EXISTS" >> $GITHUB_OUTPUT - name: Checkout if: steps.secrets_set.outputs.GET_TEST_MERGES_URL - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare module if: steps.secrets_set.outputs.GET_TEST_MERGES_URL run: | diff --git a/tools/ci/annotate_dm.py b/tools/ci/annotate_dm.py new file mode 100644 index 000000000000..2fbe7375d9de --- /dev/null +++ b/tools/ci/annotate_dm.py @@ -0,0 +1,51 @@ +import sys +import re +import os.path as path + +# Usage: python3 annotate_dm.py [filename] +# If filename is not provided, stdin is checked instead + +def red(text): + return "\033[31m" + str(text) + "\033[0m" + +def green(text): + return "\033[32m" + str(text) + "\033[0m" + +def yellow(text): + return "\033[33m" + str(text) + "\033[0m" + +def annotate(raw_output): + # Remove ANSI escape codes + raw_output = re.sub(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]', '', raw_output) + + print("::group::DreamChecker Output") + print(raw_output) + print("::endgroup::") + + annotation_regex = r'(?P.*?), line (?P\d+), column (?P\d+):\s{1,2}(?Perror|warning): (?P.*)' + has_issues = False + + print("DM Code Annotations:") + for annotation in re.finditer(annotation_regex, raw_output): + print(f"::{annotation['type']} file={annotation['filename']},line={annotation['line']},col={annotation['column']}::{annotation['message']}") + has_issues = True + + if not has_issues: + print(green("No DM issues found")) + +def main(): + if len(sys.argv) > 1: + if not path.exists(sys.argv[1]): + print(red(f"Error: Annotations file '{sys.argv[1]}' does not exist")) + sys.exit(1) + with open(sys.argv[1], 'r') as f: + annotate(f.read()) + elif not sys.stdin.isatty(): + annotate(sys.stdin.read()) + else: + print(red("Error: No input provided")) + print("Usage: tools/ci/annotate_dm.sh [filename]") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/tools/ci/annotate_dm.sh b/tools/ci/annotate_dm.sh new file mode 100755 index 000000000000..aae883f71f09 --- /dev/null +++ b/tools/ci/annotate_dm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +set -euo pipefail +tools/bootstrap/python tools/ci/annotate_dm.py "$@" diff --git a/tools/ci/annotate_od.sh b/tools/ci/annotate_od.sh new file mode 100644 index 000000000000..12390908074c --- /dev/null +++ b/tools/ci/annotate_od.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +set -euo pipefail +tools/bootstrap/python -m od_annotator "$@" diff --git a/tools/ci/ci_dependencies.sh b/tools/ci/ci_dependencies.sh new file mode 100644 index 000000000000..fd1bee5ea88a --- /dev/null +++ b/tools/ci/ci_dependencies.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +#Project dependencies file +#Contains versions of programs that we might need to install for CI purposes - do not add anything here that is REQUIRED to run the project, this is just for CI. + +export RIPGREP_VERSION=14.0.3 diff --git a/tools/ci/install_ripgrep.sh b/tools/ci/install_ripgrep.sh new file mode 100644 index 000000000000..455027d93a6e --- /dev/null +++ b/tools/ci/install_ripgrep.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -euo pipefail + +source tools/ci/ci_dependencies.sh + +cargo install ripgrep --features pcre2 --version $RIPGREP_VERSION diff --git a/tools/od_annotator/__main__.py b/tools/od_annotator/__main__.py new file mode 100644 index 000000000000..357adccfe913 --- /dev/null +++ b/tools/od_annotator/__main__.py @@ -0,0 +1,50 @@ +import sys +import re + +def green(text): + return "\033[32m" + str(text) + "\033[0m" + +def red(text): + return "\033[31m" + str(text) + "\033[0m" + +def annotate(raw_output): + # Remove ANSI escape codes + raw_output = re.sub(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]', '', raw_output) + + print("::group::OpenDream Output") + print(raw_output) + print("::endgroup::") + + annotation_regex = r'((?PError|Warning) (?POD(?P\d{4})) at (?P(?P.+):(?P\d+):(?P\d+)|): (?P.+))' + failures_detected = False + expected_failure_case_detected = False # this is just here so this script breaks if we forget to set it to True when we expect a failure. remove this when we have handled the expected failure + + print("OpenDream Code Annotations:") + for annotation in re.finditer(annotation_regex, raw_output): + message = annotation['message'] + if message == "Unimplemented proc & var warnings are currently suppressed": # this happens every single run, it's important to know about it but we don't need to throw an error + message += " (This is expected and can be ignored)" # also there's no location for it to annotate to since it's an failure. + expected_failure_case_detected = True + + if annotation['type'] == "Error": + failures_detected = True + + error_string = f"{annotation['errorcode']}: {message}" + + if annotation['location'] == "": + print(f"::{annotation['type']} file=,line=,col=::{error_string}") + else: + print(f"::{annotation['type']} file={annotation['filename']},line={annotation['line']},col={annotation['column']}::{error_string}") + + if failures_detected: + sys.exit(1) + return + + if not expected_failure_case_detected: + print(red("Failed to detect the expected failure case! If you have recently changed how we work with OpenDream Pragmas, please fix the od_annotator script!")) + sys.exit(1) + return + + print(green("No OpenDream issues found!")) + +annotate(sys.stdin.read())