mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-27 17:41:50 +00:00
Bumps [robinraju/release-downloader](https://github.com/robinraju/release-downloader) from 1.9 to 1.11. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/robinraju/release-downloader/releases">robinraju/release-downloader's releases</a>.</em></p> <blockquote> <h2>Release Downloader v1.11</h2> <h2>What's Changed</h2> <ul> <li>Fix <code>No assets found in release</code> error by <a href="https://github.com/robinraju"><code>@robinraju</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/722">robinraju/release-downloader#722</a></li> <li>Update project config and dependencies by <a href="https://github.com/robinraju"><code>@robinraju</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/723">robinraju/release-downloader#723</a></li> <li>Bump <code>@types/tar</code> from 6.1.12 to 6.1.13 in the npm-development group by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/724">robinraju/release-downloader#724</a></li> <li>Bump the npm-development group with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/725">robinraju/release-downloader#725</a></li> <li>Bump the npm-development group across 1 directory with 4 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/730">robinraju/release-downloader#730</a></li> <li>Bump tar from 7.0.1 to 7.1.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/727">robinraju/release-downloader#727</a></li> <li>Bump the npm-development group across 1 directory with 7 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/742">robinraju/release-downloader#742</a></li> <li>Bump braces from 3.0.2 to 3.0.3 in the npm_and_yarn group by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/737">robinraju/release-downloader#737</a></li> <li>Bump tar from 7.1.0 to 7.4.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/741">robinraju/release-downloader#741</a></li> <li>Bump typed-rest-client from 1.8.11 to 2.0.1 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/744">robinraju/release-downloader#744</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/robinraju/release-downloader/compare/v1.10...v1.11">https://github.com/robinraju/release-downloader/compare/v1.10...v1.11</a></p> <h2>Release Downloader v1.10</h2> <h2>What's Changed</h2> <ul> <li>Update README.md by <a href="https://github.com/robinraju"><code>@robinraju</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/680">robinraju/release-downloader#680</a></li> <li>Bump actions/checkout from 3 to 4 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/687">robinraju/release-downloader#687</a></li> <li>Bump actions/setup-node from 3 to 4 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/686">robinraju/release-downloader#686</a></li> <li>Bump nock from 13.3.8 to 13.5.1 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/685">robinraju/release-downloader#685</a></li> <li>Bump <code>@types/node</code> from 20.11.8 to 20.11.10 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/682">robinraju/release-downloader#682</a></li> <li>Bump typescript from 5.0.4 to 5.3.3 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/683">robinraju/release-downloader#683</a></li> <li>Bump <code>@vercel/ncc</code> from 0.36.1 to 0.38.1 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/684">robinraju/release-downloader#684</a></li> <li>Bump eslint-plugin-prettier from 5.0.1 to 5.1.3 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/681">robinraju/release-downloader#681</a></li> <li>fix: change zip library to fix garbled extract file under node 20 by <a href="https://github.com/jackie-linz"><code>@jackie-linz</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/707">robinraju/release-downloader#707</a></li> <li>Fix tests on windows, and package bundled js by <a href="https://github.com/robinraju"><code>@robinraju</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/710">robinraju/release-downloader#710</a></li> <li>Bump undici from 5.27.2 to 5.28.4 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/705">robinraju/release-downloader#705</a></li> <li>Bump tar from 6.2.0 to 6.2.1 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/711">robinraju/release-downloader#711</a></li> <li>Grouped dependancy updates by <a href="https://github.com/robinraju"><code>@robinraju</code></a> in <a href="https://redirect.github.com/robinraju/release-downloader/pull/713">robinraju/release-downloader#713</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/jackie-linz"><code>@jackie-linz</code></a> made their first contribution in <a href="https://redirect.github.com/robinraju/release-downloader/pull/707">robinraju/release-downloader#707</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/robinraju/release-downloader/compare/v1.9...v1.10">https://github.com/robinraju/release-downloader/compare/v1.9...v1.10</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="a96f54c1b5"><code>a96f54c</code></a> Bump typed-rest-client from 1.8.11 to 2.0.1 (<a href="https://redirect.github.com/robinraju/release-downloader/issues/744">#744</a>)</li> <li><a href="821aec8754"><code>821aec8</code></a> Bump tar from 7.1.0 to 7.4.0 (<a href="https://redirect.github.com/robinraju/release-downloader/issues/741">#741</a>)</li> <li><a href="8e9e67a554"><code>8e9e67a</code></a> Bump braces from 3.0.2 to 3.0.3 in the npm_and_yarn group (<a href="https://redirect.github.com/robinraju/release-downloader/issues/737">#737</a>)</li> <li><a href="a3cbfc11ee"><code>a3cbfc1</code></a> Bump the npm-development group across 1 directory with 7 updates (<a href="https://redirect.github.com/robinraju/release-downloader/issues/742">#742</a>)</li> <li><a href="2c0dbf1dec"><code>2c0dbf1</code></a> Bump tar from 7.0.1 to 7.1.0 (<a href="https://redirect.github.com/robinraju/release-downloader/issues/727">#727</a>)</li> <li><a href="8ecfadfd54"><code>8ecfadf</code></a> Bump the npm-development group across 1 directory with 4 updates (<a href="https://redirect.github.com/robinraju/release-downloader/issues/730">#730</a>)</li> <li><a href="8d13112ee2"><code>8d13112</code></a> Bump the npm-development group with 3 updates (<a href="https://redirect.github.com/robinraju/release-downloader/issues/725">#725</a>)</li> <li><a href="8e6a4e0026"><code>8e6a4e0</code></a> Bump <code>@types/tar</code> from 6.1.12 to 6.1.13 in the npm-development group (<a href="https://redirect.github.com/robinraju/release-downloader/issues/724">#724</a>)</li> <li><a href="6c76fddc2c"><code>6c76fdd</code></a> Update project config and dependencies (<a href="https://redirect.github.com/robinraju/release-downloader/issues/723">#723</a>)</li> <li><a href="cb096d8fa1"><code>cb096d8</code></a> Fix <code>No assets found in release</code> error (<a href="https://redirect.github.com/robinraju/release-downloader/issues/722">#722</a>)</li> <li>Additional commits viewable in <a href="https://github.com/robinraju/release-downloader/compare/v1.9...v1.11">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
297 lines
11 KiB
YAML
297 lines
11 KiB
YAML
name: CI Suite
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
- 'project/**'
|
|
- 'gh-readonly-queue/master/**'
|
|
- 'gh-readonly-queue/project/**'
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
- 'project/**'
|
|
merge_group:
|
|
branches:
|
|
- master
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
start_gate:
|
|
if: ( !contains(github.event.head_commit.message, '[ci skip]') )
|
|
name: Start Gate
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Mandatory Empty Step
|
|
run: exit 0
|
|
|
|
run_linters:
|
|
name: Run Linters
|
|
needs: start_gate
|
|
runs-on: ubuntu-22.04
|
|
timeout-minutes: 5
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Restore SpacemanDMM cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/SpacemanDMM
|
|
key: ${{ runner.os }}-spacemandmm-${{ hashFiles('dependencies.sh') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-spacemandmm-
|
|
- name: Restore Yarn cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: tgui/.yarn/cache
|
|
key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }}
|
|
restore-keys: |
|
|
${{ 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@v4
|
|
with:
|
|
path: ~/.cargo
|
|
key: ${{ runner.os }}-rust-${{ hashFiles('tools/ci/ci_dependencies.sh')}}
|
|
restore-keys: |
|
|
${{ runner.os }}-rust-
|
|
- name: Restore Cutter cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: tools/icon_cutter/cache
|
|
key: ${{ runner.os }}-cutter-${{ hashFiles('dependencies.sh') }}
|
|
- name: Install OpenDream
|
|
uses: robinraju/release-downloader@v1.11
|
|
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
|
|
bash tools/ci/install_ripgrep.sh
|
|
tools/bootstrap/python -c ''
|
|
- 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: |
|
|
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
|
|
- name: Check Define Sanity
|
|
if: steps.linter-setup.conclusion == 'success' && !cancelled()
|
|
run: tools/bootstrap/python -m define_sanity.check
|
|
- name: Check Trait Validity
|
|
if: steps.linter-setup.conclusion == 'success' && !cancelled()
|
|
run: tools/bootstrap/python -m trait_validity.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: |
|
|
tools/bootstrap/python -m mapmerge2.dmm_test
|
|
tools/bootstrap/python -m tools.maplint.source
|
|
- name: Check Cutter
|
|
if: steps.linter-setup.conclusion == 'success' && !cancelled()
|
|
run: tools/bootstrap/python -m tools.icon_cutter.check
|
|
- 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:
|
|
name: Compile Maps
|
|
needs: collect_data
|
|
runs-on: ubuntu-22.04
|
|
timeout-minutes: 5
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Restore BYOND cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/BYOND
|
|
key: ${{ runner.os }}-byond-${{ hashFiles('dependencies.sh') }}
|
|
- name: Compile All Maps
|
|
run: |
|
|
bash tools/ci/install_byond.sh
|
|
source $HOME/BYOND/byond/bin/byondsetup
|
|
tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS
|
|
- name: Check client Compatibility
|
|
uses: tgstation/byond-client-compatibility-check@v3
|
|
with:
|
|
dmb-location: tgstation.dmb
|
|
max-required-client-version: ${{needs.collect_data.outputs.max_required_byond_client}}
|
|
|
|
collect_data:
|
|
name: Collect data for other tasks
|
|
needs: start_gate
|
|
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 }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Find Maps
|
|
id: map_finder
|
|
run: |
|
|
echo "$(ls -mw0 _maps/*.json)" > maps_output.txt
|
|
sed -i -e s+_maps/+\"+g -e s+.json+\"+g maps_output.txt
|
|
echo "Maps: $(cat maps_output.txt)"
|
|
echo "maps={\"paths\":[$(cat maps_output.txt)]}" >> $GITHUB_OUTPUT
|
|
- name: Find Alternate Tests
|
|
id: alternate_test_finder
|
|
run: |
|
|
ALTERNATE_TESTS_JSON=$(jq -nRc '[inputs | capture("^(?<major>[0-9]+)\\.(?<minor>[0-9]+): (?<map>.+)$")]' .github/alternate_byond_versions.txt)
|
|
echo "alternate_tests=$ALTERNATE_TESTS_JSON" >> $GITHUB_OUTPUT
|
|
- name: Collect byond client version configuration
|
|
id: max_required_byond_client
|
|
#the regex here does not filter out non-numbers because error messages about no input are less helpful then error messages about bad input (which includes the bad input)
|
|
run: |
|
|
echo "max_required_byond_client=$(grep -Ev '^[[:blank:]]{0,}#{1,}|^[[:blank:]]{0,}$' .github/max_required_byond_client.txt | tail -n1)" >> $GITHUB_OUTPUT
|
|
|
|
run_all_tests:
|
|
name: Integration Tests
|
|
needs: collect_data
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
map: ${{ fromJSON(needs.collect_data.outputs.maps).paths }}
|
|
|
|
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: 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) }}
|
|
|
|
uses: ./.github/workflows/run_integration_tests.yml
|
|
with:
|
|
map: ${{ matrix.setup.map }}
|
|
major: ${{ matrix.setup.major }}
|
|
minor: ${{ matrix.setup.minor }}
|
|
max_required_byond_client: ${{needs.collect_data.outputs.max_required_byond_client}}
|
|
|
|
compare_screenshots:
|
|
if: needs.collect_data.outputs.alternate_tests == '[]' || needs.run_alternate_tests.result == 'success'
|
|
needs: [ collect_data, run_all_tests, run_alternate_tests ]
|
|
name: Compare Screenshot Tests
|
|
timeout-minutes: 15
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- 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@v4
|
|
with:
|
|
path: artifacts
|
|
- name: ls -R
|
|
run: ls -R artifacts
|
|
- name: Setup screenshot comparison
|
|
run: npm i
|
|
working-directory: tools/screenshot-test-comparison
|
|
- name: Run screenshot comparison
|
|
run: node tools/screenshot-test-comparison/index.js artifacts code/modules/unit_tests/screenshots artifacts/screenshot_comparisons
|
|
# workflow_run does not give you the PR it ran on,
|
|
# even through the thing literally named "matching pull requests".
|
|
# However, in GraphQL, you can check if the check suite was ran
|
|
# by a specific PR, so trusting the (user controlled) action here is okay,
|
|
# as long as we check it later in show_screenshot_test_results
|
|
- name: Save PR ID
|
|
if: failure() && github.event.pull_request
|
|
run: |
|
|
echo ${{ github.event.pull_request.number }} > artifacts/screenshot_comparisons/pull_request_number.txt
|
|
- name: Upload bad screenshots
|
|
if: failure()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: bad-screenshots
|
|
path: artifacts/screenshot_comparisons
|
|
|
|
test_windows:
|
|
name: Windows Build
|
|
needs: [collect_data]
|
|
runs-on: windows-latest
|
|
timeout-minutes: 5
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Restore Yarn cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: tgui/.yarn/cache
|
|
key: ${{ runner.os }}-yarn-${{ hashFiles('tgui/yarn.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-yarn-
|
|
- name: Compile
|
|
run: pwsh tools/ci/build.ps1
|
|
env:
|
|
DM_EXE: "C:\\byond\\bin\\dm.exe"
|
|
- name: Check client Compatibility
|
|
uses: tgstation/byond-client-compatibility-check@v3
|
|
with:
|
|
dmb-location: tgstation.dmb
|
|
max-required-client-version: ${{needs.collect_data.outputs.max_required_byond_client}}
|
|
|
|
completion_gate: # Serves as a non-moving target for branch rulesets
|
|
if: always() && !cancelled()
|
|
name: Completion Gate
|
|
needs: [ test_windows, compare_screenshots, compile_all_maps, run_linters ]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Decide whether the needed jobs succeeded or failed
|
|
uses: re-actors/alls-green@release/v1
|
|
with:
|
|
jobs: ${{ toJSON(needs) }}
|