Skip to content

Commit d2e695b

Browse files
Split tests off of self hosted runner (#1901)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent bc0fd67 commit d2e695b

136 files changed

Lines changed: 1675 additions & 1102 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codecov.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
codecov:
22
branch: main
3+
notify:
4+
manual_trigger: true
35

46
coverage:
57
status: false
68

9+
# Carry forward self-hosted, as these are not run on external PRs.
10+
flag_management:
11+
individual_flags:
12+
- name: SelfHosted-Linux
13+
carryforward: true
14+
- name: SelfHosted-macos
15+
carryforward: true
16+
717
comment:
818
require_head: false
919
require_base: false

.github/workflows/autofix.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: autofix.ci
2+
on:
3+
pull_request:
4+
5+
permissions: {}
6+
7+
jobs:
8+
lint:
9+
timeout-minutes: 1
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read # For checkout
13+
env:
14+
UV_NO_SYNC: "1" # Disable installing default packages on `uv run`
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v6
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v6
21+
with:
22+
enable-cache: true
23+
- name: Install dependencies
24+
run: uv sync --only-group autofix --frozen
25+
- name: Ruff fix
26+
run: uv run ruff check --fix-only --unsafe-fixes
27+
- name: Ruff format
28+
run: uv run ruff format
29+
- name: uv lock
30+
run: uv lock
31+
- name: autofix.ci
32+
uses: autofix-ci/action@v1

.github/workflows/ci.yml

Lines changed: 242 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,68 +5,278 @@ on:
55
paths-ignore:
66
- '**.md'
77
pull_request:
8-
types: [opened, synchronize, reopened, ready_for_review]
98
paths-ignore:
109
- '**.md'
1110

12-
permissions:
13-
contents: read
14-
packages: read
15-
id-token: write
11+
concurrency:
12+
# Cancels runs from previous pushes in a PR.
13+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
14+
cancel-in-progress: true
15+
16+
permissions: {}
1617

1718
jobs:
18-
ci-complete:
19-
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
20-
timeout-minutes: 60
21-
runs-on: [self-hosted, Linux]
22-
container:
23-
image: ghcr.io/dimensionalos/ros-dev:dev
24-
env:
25-
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
26-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
27-
ALIBABA_API_KEY: ${{ secrets.ALIBABA_API_KEY }}
19+
lint:
20+
timeout-minutes: 10
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read # For checkout
24+
env:
25+
UV_NO_SYNC: "1" # Disable installing default packages on `uv run`
2826

2927
steps:
30-
- uses: actions/checkout@v5
28+
- name: Checkout
29+
uses: actions/checkout@v6
30+
- name: Install uv
31+
uses: astral-sh/setup-uv@v6
3132
with:
32-
clean: false
33+
enable-cache: true
34+
- name: Install lint dependencies
35+
run: uv sync --only-group lint --frozen
36+
- name: Mypy
37+
run: uv run mypy
38+
- name: Run pre-commit
39+
uses: pre-commit/action@v3.0.1
3340

34-
- name: Fix permissions
35-
run: |
36-
git config --global --add safe.directory '*'
37-
git clean -ffdx -e .venv
41+
md-babel:
42+
timeout-minutes: 10
43+
runs-on: ubuntu-latest
44+
permissions:
45+
contents: read # For checkout
3846

47+
steps:
48+
- uses: actions/checkout@v6
49+
with:
50+
lfs: true
51+
# Docs decode JPEG from SQLite via PyTurboJPEG; pyaudio needs portaudio.
52+
- name: Install system dependencies
53+
run: |
54+
sudo apt-get update
55+
sudo apt-get install -y libturbojpeg portaudio19-dev
56+
- name: Install uv
57+
uses: astral-sh/setup-uv@v6
58+
with:
59+
enable-cache: true
60+
- uses: actions/setup-node@v4
61+
with:
62+
node-version: 'lts/*'
3963
- name: Install Python dependencies
40-
run: uv sync --extra all --frozen
64+
run: uv sync --group tests --frozen
65+
- name: Execute documentation code blocks
66+
run: ./bin/run-doc-codeblocks --ci --no-cache
4167

42-
- name: Remove pydrake stubs
43-
run: |
44-
find .venv/lib/*/site-packages/pydrake -name '*.pyi' -delete 2>/dev/null || true
68+
tests:
69+
timeout-minutes: 20
70+
strategy:
71+
matrix:
72+
pyver: ['3.10', '3.11', '3.12', '3.13', '3.14']
73+
os: [ubuntu]
74+
experimental: [false]
75+
include:
76+
- os: ubuntu
77+
pyver: "3.14t"
78+
experimental: false
79+
- os: ubuntu
80+
pyver: "3.15"
81+
experimental: true
82+
fail-fast: true
83+
runs-on: ${{ matrix.os }}-latest
84+
continue-on-error: ${{ matrix.experimental }}
85+
permissions:
86+
contents: read # For checkout
87+
id-token: write # For codecov-action's OIDC upload
4588

46-
- name: Run tests
89+
steps:
90+
- name: Checkout
91+
uses: actions/checkout@v6
92+
- name: Install uv
93+
uses: astral-sh/setup-uv@v6
94+
with:
95+
enable-cache: true
96+
- name: Setup Python
97+
uses: actions/setup-python@v6
98+
with:
99+
allow-prereleases: true
100+
python-version: ${{ matrix.pyver }}
101+
- name: Install dependency for pyaudio (Ubuntu)
102+
if: matrix.os == 'ubuntu'
47103
run: |
48-
/entrypoint.sh bash -c "source .venv/bin/activate && _DIMOS_COV=1 coverage run -m pytest --junitxml=junit.xml --durations=0 -m 'not (tool or mujoco)' && coverage combine && coverage xml"
49-
50-
- name: Run mypy
104+
sudo apt-get update
105+
sudo apt-get install -y portaudio19-dev
106+
- name: Install dependency for pyaudio (macOS)
107+
if: matrix.os == 'macos'
108+
run: brew install portaudio
109+
- name: Remove git LFS to avoid accidental large downloads
110+
run: sudo rm -f /usr/bin/git-lfs
111+
- name: Set PYTHON_GIL=0 for free-threading builds
112+
if: ${{ endsWith(matrix.pyver, 't') }}
113+
run: echo "PYTHON_GIL=0" >> $GITHUB_ENV
114+
- name: Run tests
115+
run: uv run pytest --numprocesses=3 --cov=dimos/ --junitxml=junit.xml -m 'not (tool or self_hosted or mujoco)'
116+
- name: Re-run the failing tests with maximum verbosity
117+
if: failure()
118+
env:
119+
COLOR: yes
120+
run: >- # `exit 1` makes sure that the job remains red with flaky runs
121+
uv run pytest --no-cov -vvvvv --lf -m 'not (tool or self_hosted or mujoco)' && exit 1
122+
shell: bash
123+
- name: Turn coverage into xml
124+
run: uv run python -m coverage xml
125+
- name: Upload coverage
126+
uses: codecov/codecov-action@v6
127+
with:
128+
disable_search: true
129+
fail_ci_if_error: true
130+
files: ./coverage.xml
131+
flags: OS-${{ matrix.os }},Py-${{ matrix.pyver }}
132+
use_oidc: true
133+
- name: Upload test results to Codecov
51134
if: ${{ !cancelled() }}
52-
run: |
53-
/entrypoint.sh bash -c "source .venv/bin/activate && MYPYPATH=/opt/ros/humble/lib/python3.10/site-packages mypy dimos"
135+
uses: codecov/codecov-action@v6
136+
with:
137+
report_type: test_results
138+
use_oidc: true
139+
140+
self-hosted-tests:
141+
# Skip on draft PRs and on PRs from forks — the latter would expose the
142+
# self-hosted runner to untrusted code from external contributors.
143+
if: |
144+
github.event_name == 'push' || (
145+
github.event.pull_request.draft == false &&
146+
github.event.pull_request.head.repo.full_name == github.repository
147+
)
148+
env:
149+
UV_NO_SYNC: "1" # Disable installing default packages on `uv run`
150+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
151+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
152+
ALIBABA_API_KEY: ${{ secrets.ALIBABA_API_KEY }}
153+
timeout-minutes: 30
154+
strategy:
155+
fail-fast: false
156+
matrix:
157+
include:
158+
- runner: [self-hosted, Linux]
159+
# GitHub Actions only honours `container:` on Linux runners.
160+
container:
161+
image: ghcr.io/dimensionalos/ros-dev:dev
162+
markers: "self_hosted or skipif_no_ros"
163+
experimental: false
164+
- runner: [self-hosted, macos, arm64]
165+
container: null # run on host — `container:` is Linux-only
166+
markers: "self_hosted"
167+
experimental: true
168+
runs-on: ${{ matrix.runner }}
169+
continue-on-error: ${{ matrix.experimental }}
170+
permissions:
171+
contents: read # For checkout
172+
packages: read # For pulling the ros-dev container from ghcr.io
173+
id-token: write # For codecov-action's OIDC upload
174+
container: ${{ matrix.container }}
54175

176+
steps:
177+
- uses: actions/checkout@v5
178+
with:
179+
clean: false
180+
# If we ever allow external PRs on custom runner, persisting credentials
181+
# could be abused by attackers.
182+
persist-credentials: false
183+
- name: Fix permissions
184+
run: |
185+
git config --global --add safe.directory '*'
186+
git clean -ffdx
187+
- name: Install uv
188+
uses: astral-sh/setup-uv@v6
189+
with:
190+
enable-cache: true
191+
- name: Install dependencies
192+
run: uv sync --group tests-self-hosted --frozen
193+
- name: Build C++ extensions in-place
194+
run: uv run python setup.py build_ext --inplace
195+
- name: Source ROS environment
196+
# The uv venv is sealed (include-system-site-packages = false), so
197+
# `import rclpy` / `ament_index_python` would fail. Sourcing the ROS
198+
# setup script and exporting PYTHONPATH/AMENT_PREFIX_PATH/etc into
199+
# GITHUB_ENV makes them importable from `uv run`.
200+
if: contains(toJSON(matrix.runner), 'Linux')
201+
shell: bash
202+
run: |
203+
source /opt/ros/humble/setup.bash
204+
{
205+
echo "PYTHONPATH=$PYTHONPATH"
206+
echo "AMENT_PREFIX_PATH=$AMENT_PREFIX_PATH"
207+
echo "CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH"
208+
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
209+
echo "ROS_DISTRO=$ROS_DISTRO"
210+
echo "ROS_VERSION=$ROS_VERSION"
211+
echo "ROS_PYTHON_VERSION=$ROS_PYTHON_VERSION"
212+
} >> "$GITHUB_ENV"
213+
- name: Run tests
214+
run: uv run pytest --cov=dimos/ --junitxml=junit.xml -m '(${{ matrix.markers }}) and not (tool or mujoco)'
215+
- name: Re-run the failing tests with maximum verbosity
216+
if: failure()
217+
env:
218+
COLOR: yes
219+
run: >- # `exit 1` makes sure that the job remains red with flaky runs
220+
uv run pytest --no-cov -vvvvv --lf -m '(${{ matrix.markers }}) and not (tool or mujoco)' && exit 1
221+
shell: bash
222+
- name: Turn coverage into xml
223+
run: uv run python -m coverage xml
55224
- name: Upload coverage
56225
uses: codecov/codecov-action@v6
57226
with:
58227
disable_search: true
59228
fail_ci_if_error: true
60229
files: ./coverage.xml
230+
flags: SelfHosted-${{ matrix.runner[1] }}
61231
use_oidc: true
62232
- name: Upload test results to Codecov
63233
if: ${{ !cancelled() }}
64234
uses: codecov/codecov-action@v6
65235
with:
66236
report_type: test_results
67237
use_oidc: true
68-
69238
- name: Check disk space
70239
if: failure()
71240
run: |
72241
df -h
242+
243+
# Cross-job fail-fast: GitHub Actions only fail-fasts within a matrix,
244+
# not across sibling jobs. This watcher fires the moment `tests` fails
245+
# and cancels the whole workflow run.
246+
fail-fast:
247+
if: failure()
248+
needs: [tests]
249+
runs-on: ubuntu-latest
250+
permissions:
251+
actions: write # For `gh run cancel`
252+
steps:
253+
- name: Cancel workflow run
254+
env:
255+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
256+
run: gh run cancel ${{ github.run_id }} --repo ${{ github.repository }}
257+
258+
ci-complete: # This is used for branch protection.
259+
if: always()
260+
261+
needs:
262+
- lint
263+
- md-babel
264+
- tests
265+
- self-hosted-tests
266+
267+
runs-on: ubuntu-latest
268+
permissions:
269+
id-token: write # For codecov-action's OIDC notify
270+
271+
steps:
272+
- name: Decide whether the needed jobs succeeded or failed
273+
uses: re-actors/alls-green@release/v1
274+
with:
275+
allowed-skips: self-hosted-tests
276+
jobs: ${{ toJSON(needs) }}
277+
- name: Trigger Codecov notifications
278+
uses: codecov/codecov-action@v6
279+
with:
280+
run_command: send-notifications
281+
use_oidc: true
282+
fail_ci_if_error: true

0 commit comments

Comments
 (0)