-
-
Notifications
You must be signed in to change notification settings - Fork 988
206 lines (173 loc) · 7.78 KB
/
run-tutorial.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
name: Test Gymnasium Tutorials
on:
# Run all tests when merging to main
push:
branches: [ main ]
# Run tests only for modified tutorials in PRs
pull_request:
paths:
- 'docs/tutorials/**/*.py'
- '.github/workflows/test-tutorials.yml'
# Allow manual trigger
workflow_dispatch:
jobs:
test-tutorials:
runs-on: ubuntu-latest
strategy:
fail-fast: false # This ensures all matrix combinations run even if one fails
matrix:
python-version: ["3.9"]
tutorial-group:
- gymnasium_basics
- training_agents
timeout-minutes: 30 # Set a reasonable timeout for all tests
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libglu1-mesa-dev libgl1-mesa-dev libosmesa6-dev xvfb unzip patchelf ffmpeg cmake swig
- name: Install Gymnasium basics tutorial requirements
if: matrix.tutorial-group == 'gymnasium_basics'
run: |
pip install ".[mujoco, box2d]"
- name: Install Training Agents tutorial requirements
if: matrix.tutorial-group == 'training_agents'
run: |
pip install ".[mujoco, toy_text, box2d]" torch seaborn matplotlib pandas tqdm
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v46
with:
files: docs/tutorials/**/*.py
if: github.event_name == 'pull_request'
- name: Patch tutorials
run: |
# Patch load_quadruped_model.py to use the built-in ant.xml instead of the missing mujoco_menagerie file
if [ -f "docs/tutorials/gymnasium_basics/load_quadruped_model.py" ]; then
sed -i 's|"./mujoco_menagerie/unitree_go1/scene.xml"|"ant.xml"|g' docs/tutorials/gymnasium_basics/load_quadruped_model.py
fi
# Patch mujoco_reinforce.py to reduce the number of episodes for CI
if [ -f "docs/tutorials/training_agents/mujoco_reinforce.py" ]; then
sed -i 's/total_num_episodes = int(5e3)/total_num_episodes = int(1e2)/g' docs/tutorials/training_agents/mujoco_reinforce.py
fi
# Patch frozenlake_q_learning.py to reduce the number of episodes and runs
if [ -f "docs/tutorials/training_agents/frozenlake_q_learning.py" ]; then
sed -i 's/total_episodes=2000/total_episodes=200/g' docs/tutorials/training_agents/frozenlake_q_learning.py
sed -i 's/n_runs=20/n_runs=3/g' docs/tutorials/training_agents/frozenlake_q_learning.py
sed -i 's/map_sizes = \[4, 7, 9, 11\]/map_sizes = \[4, 7\]/g' docs/tutorials/training_agents/frozenlake_q_learning.py
fi
# Patch vector_a2c.py to reduce the number of updates and environments
if [ -f "docs/tutorials/training_agents/vector_a2c.py" ]; then
sed -i 's/n_envs = 10/n_envs = 4/g' docs/tutorials/training_agents/vector_a2c.py
sed -i 's/n_updates = 1000/n_updates = 100/g' docs/tutorials/training_agents/vector_a2c.py
fi
# Make sure we use 'rgb_array' render mode instead of 'human' everywhere to avoid display issues
find docs/tutorials -name "*.py" -type f -exec sed -i 's/render_mode="human"/render_mode="rgb_array"/g' {} \;
- name: Test tutorials (${{ matrix.tutorial-group }})
id: run-tutorials
run: |
echo "::group::Running tutorials in ${{ matrix.tutorial-group }}"
mkdir -p test-results
# Determine which tutorials to test
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# Get the list of modified tutorial files in this group
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
if [[ $file == docs/tutorials/${{ matrix.tutorial-group }}/* && $file == *.py ]]; then
echo $file >> tutorial_files.txt
fi
done
# If no tutorials in this group were modified, skip this job
if [ ! -f tutorial_files.txt ] || [ ! -s tutorial_files.txt ]; then
echo "No tutorials modified in ${{ matrix.tutorial-group }} - skipping"
exit 0
fi
else
# Find all Python files in the tutorial group
find docs/tutorials/${{ matrix.tutorial-group }} -name "*.py" -type f | sort > tutorial_files.txt
fi
# Initialize counters
total=0
passed=0
failed=0
# Run each tutorial with timeout - continue even if one fails
while IFS= read -r tutorial; do
# Clear separator for better readability
echo ""
echo "========================================================"
echo "Running tutorial: $tutorial"
echo "========================================================"
total=$((total+1))
# Set max time based on complexity (can be adjusted)
max_time=300 # 5 minutes default
# Create log file path
log_file="test-results/$(basename "$tutorial").log"
# Run the tutorial with timeout and record results
start_time=$(date +%s)
# Use set +e so the script continues even if the command fails
set +e
timeout $max_time python "$tutorial" > "$log_file" 2>&1
exit_code=$?
set -e
end_time=$(date +%s)
execution_time=$((end_time-start_time))
# Output results to console immediately
if [ $exit_code -eq 0 ]; then
echo "✅ PASSED: $tutorial (${execution_time}s)"
passed=$((passed+1))
echo "$tutorial,pass,$execution_time" >> test-results/summary.csv
elif [ $exit_code -eq 124 ]; then
echo "⚠️ TIMEOUT: $tutorial (exceeded ${max_time}s)"
failed=$((failed+1))
echo "$tutorial,timeout,$max_time" >> test-results/summary.csv
# Show the last output before timeout
echo "Last output before timeout:"
echo "----------------------------------------"
tail -n 20 "$log_file"
echo "----------------------------------------"
else
echo "❌ FAILED: $tutorial (${execution_time}s)"
failed=$((failed+1))
echo "$tutorial,fail,$execution_time" >> test-results/summary.csv
# Show the error details
echo "Error details:"
echo "----------------------------------------"
cat "$log_file"
echo "----------------------------------------"
fi
done < tutorial_files.txt
# Export the counters as outputs for later steps
echo "total=$total" >> $GITHUB_OUTPUT
echo "passed=$passed" >> $GITHUB_OUTPUT
echo "failed=$failed" >> $GITHUB_OUTPUT
echo "::endgroup::"
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: tutorial-test-results-${{ matrix.tutorial-group }}
path: test-results/
retention-days: 7
- name: Check test results
if: always()
run: |
# Read the outputs from the test step
total="${{ steps.run-tutorials.outputs.total }}"
failed="${{ steps.run-tutorials.outputs.failed }}"
# Default to 0 if empty to avoid bash errors
total=${total:-0}
failed=${failed:-0}
if [ "$total" -eq 0 ]; then
echo "No tutorials were tested in this group."
elif [ "$failed" -gt 0 ]; then
echo "Warning: $failed out of $total tutorials failed."
echo "Check the test results in the uploaded artifacts to see detailed error messages."
else
echo "Success: All $total tutorials passed."
fi
# Return exit code based on failures
exit $failed