Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# store every text file with Unix line endings
# to avoid whitespace diffs between windows and linux
* text=auto eol=lf

# mark uv.lock as machine generated to hide in diff
uv.lock linguist-generated=true
18 changes: 10 additions & 8 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
## Summary

-
<!-- add a brief summary of your changes including testing -->

## Validation

- [ ] `scripts/quality-check.sh`
- [ ] Manual testing, if applicable:
- [ ] `scripts/quality-check.sh` passes locally
- [ ] Appropriate `/test` commands were run and are passing (e.g. `/test gpu`)
- [ ] Unit tests were added / e2e tests were added where applicable
- [ ] Manual testing, if applicable (describe further):

## Checklist

- [ ] The change is focused and easy to review.
- [ ] Tests were added or updated for behavior changes.
- [ ] Documentation was updated for user-facing or setup changes.
- [ ] No secrets, generated artifacts, or local-only files are included.
- [ ] Tests were added or updated for behaviour changes.
- [ ] No secrets, generated files, or files that only make sense on my machine are included.
- [ ] Relevant documentation is updated.

## Notes for Reviewers
## Additional Comments

-
<!-- Leave blank if none -->
20 changes: 20 additions & 0 deletions .github/scripts/check-commenter.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
#
# check if user has permission to run test command
# needs to be, owner, member, or collaborator

set -euo pipefail

case "$ASSOCIATION" in
OWNER | MEMBER | COLLABORATOR)
echo "$COMMENTER is allowed to run commands ($ASSOCIATION)."
;;
*)
gh api --method POST \
"/repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/comments" \
-f body="Sorry @$COMMENTER, only people with access to this repository can run \`/test\`."

echo "$COMMENTER is not allowed to run commands ($ASSOCIATION)." >&2
exit 1
;;
esac
17 changes: 17 additions & 0 deletions .github/scripts/find-commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
#
# find the latest commit of the branch a PR comment is made on

set -euo pipefail

sha="$(gh pr view "$ISSUE_NUMBER" \
--repo "$GITHUB_REPOSITORY" \
--json headRefOid \
--jq .headRefOid)"

echo "Testing commit $sha"

{
echo "sha=$sha"
echo "short_sha=${sha:0:7}"
} >> "$GITHUB_OUTPUT"
91 changes: 91 additions & 0 deletions .github/scripts/parse-command.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
#
# Reads the comment and works out what the workflow should execute
#
# /test run all tests
# /test all run all tests
# /test <python|slow|gpu|web> run specific tests
# /test help the list of commands
#
# Extra args are passed to pytest
#
# It writes four values for the later jobs to read:
#
# mode none, help or tests
# matrix the groups to run, as the JSON a job matrix expects
# groups the same list in words, for the reply comment
# pytest_args extra arguments to hand to pytest

set -euo pipefail

UBUNTU='"ubuntu-latest"'
# TODO: set up real gpu machine
GPU_MACHINE='["self-hosted","gpu"]'

# only read the first line
first_line="$(printf '%s' "$BODY" | head -n 1 | tr -d '\r')"
command="$(printf '%s' "$first_line" | awk '{print $1}')"
rest="$(printf '%s' "$first_line" | sed -E 's|^[[:space:]]*/test[[:space:]]*||; s|[[:space:]]*$||')"

mode=none
groups=()
pytest_args=""

if [[ "$command" != "/test" ]]; then
echo "Not a /test command, stopping here."
else
first_word="$(printf '%s' "$rest" | awk '{print $1}')"
case "$first_word" in
help | all | python | slow | gpu | web)
pytest_args="$(printf '%s' "$rest" | sed -E 's|^[^[:space:]]+[[:space:]]*||')"
;;
*)
first_word=all
pytest_args="$rest"
;;
esac

case "$first_word" in
help) mode=help ;;
# TODO: add gpu back to this list once the machine from the TODO above is
# registered, otherwise will hang for 24h
all) mode=tests; groups=(python slow web) ;;
*) mode=tests; groups=("$first_word") ;;
esac

# pass rest of line as pytest args
if [[ -n "$pytest_args" ]] && ! printf '%s' "$pytest_args" | grep -Eq '^[A-Za-z0-9 _./=:@-]+$'; then
echo "These arguments contain characters that are not allowed: $pytest_args" >&2
exit 1
fi
fi

# build a strategy.matrix for github actions to consume
entries=()
for group in ${groups[@]+"${groups[@]}"}; do
case "$group" in
gpu) runs_on="$GPU_MACHINE" ;;
*) runs_on="$UBUNTU" ;;
esac
escaped="${runs_on//\"/\\\"}"
entries+=("{\"group\":\"$group\",\"runs_on\":\"$escaped\"}")
done

matrix="{\"include\":[$(
IFS=,
echo "${entries[*]-}"
)]}"
group_list="$(
IFS=,
echo "${groups[*]-}"
)"
group_list="${group_list//,/, }"

echo "Mode '$mode', groups: ${group_list:-none}, pytest arguments: ${pytest_args:-none}"

{
echo "mode=$mode"
echo "matrix=$matrix"
echo "groups=$group_list"
echo "pytest_args=$pytest_args"
} >> "$GITHUB_OUTPUT"
29 changes: 29 additions & 0 deletions .github/scripts/post-help.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
#
# Replies to `/test help` with the list of commands.

set -euo pipefail

body="$(
cat << 'EOF'
**Commands available on this pull request**

| Comment | Description |
| --- | --- |
| `/test` or `/test all` | Every group below except `gpu`, at the same time |
| `/test python` | The quick tests. |
| `/test slow` | The tests marked `slow` |
| `/test gpu` | The tests marked `gpu`, on the project GPU machine |
| `/test web` | The 3D visualizer |
| `/test help` | Shows this message |

Anything after the group name is passed to pytest, so `/test python -k sampler`
runs the quick tests whose names contain "sampler".

These commands are for tests that are too slow or need hardware to run every time.
EOF
)"

gh api --method POST \
"/repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/comments" \
-f body="$body"
28 changes: 28 additions & 0 deletions .github/scripts/post-result.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
#
# Replies with the result of the tests once they have finished

set -euo pipefail

if [[ "$RESULT" == "success" ]]; then
heading="Tests passed"
else
heading="Tests did not pass ($RESULT)"
fi

body="$(
cat << EOF
**$heading**

| | |
| --- | --- |
| Groups run | \`$TEST_GROUPS\` |
| Commit tested | \`$SHORT_SHA\` (\`$SHA\`) |
| Full log | [Actions run]($RUN_URL) |

EOF
)"

gh api --method POST \
"/repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/comments" \
-f body="$body"
9 changes: 9 additions & 0 deletions .github/scripts/react-to-comment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
#
# Puts a 👀 reaction on the comment that asked for the tests

set -euo pipefail

gh api --method POST \
"/repos/$GITHUB_REPOSITORY/issues/comments/$COMMENT_ID/reactions" \
-f content=eyes > /dev/null
97 changes: 97 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: CI

# Runs automatically on every pull request and on every push to main.
# The `format` and `test` jobs are the ones a pull request must pass before it
# is allowed to merge
#
# See docs/branch-protection.md for more info
#
# Jobs are split into web and python checks

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# check for code formatting
format:
name: format
runs-on: ubuntu-latest

steps:
- name: Check out repo
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Install Python dependencies
run: scripts/install-python-dependencies.sh

- name: Check formatting and code style
run: scripts/format-check.sh

# check quick test are passing
# gpu / other slow tests run with pr command
test:
name: test
runs-on: ubuntu-latest

steps:
- name: Check out repo
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Install Python dependencies
run: scripts/install-python-dependencies.sh

- name: Run the quick tests
run: scripts/test-check.sh python

# TODO: update script for web side when added
web:
name: web
runs-on: ubuntu-latest

steps:
- name: Check out code
uses: actions/checkout@v4

- name: Look for a web/ folder
id: detect
run: |
if [[ -f web/package.json ]]; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "No web/ folder yet, so there is nothing to check."
fi

- name: Set up Node.js
if: steps.detect.outputs.present == 'true'
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install web dependencies
if: steps.detect.outputs.present == 'true'
run: scripts/install-web-dependencies.sh

- name: Run the web checks
if: steps.detect.outputs.present == 'true'
run: scripts/web-check.sh
42 changes: 0 additions & 42 deletions .github/workflows/ci.yml

This file was deleted.

Loading
Loading