9
9
pull_request :
10
10
paths :
11
11
- ' docs/tutorials/**/*.py'
12
- - ' .github/workflows/run -tutorials.yml'
12
+ - ' .github/workflows/test -tutorials.yml'
13
13
14
14
# Allow manual trigger
15
15
workflow_dispatch :
18
18
test-tutorials :
19
19
runs-on : ubuntu-latest
20
20
strategy :
21
- fail-fast : false
21
+ fail-fast : false # This ensures all matrix combinations run even if one fails
22
22
matrix :
23
23
python-version : ["3.9"]
24
24
tutorial-group :
@@ -34,23 +34,21 @@ jobs:
34
34
uses : actions/setup-python@v5
35
35
with :
36
36
python-version : ${{ matrix.python-version }}
37
- cache : ' pip'
38
37
39
38
- name : Install dependencies
40
39
run : |
41
- python -m pip install --upgrade pip
42
- # Install Gymnasium and its dependencies
43
- pip install -e .
44
- # Install additional dependencies for tutorials
45
- pip install torch torchvision tqdm matplotlib seaborn pandas pygame
46
- # Install MuJoCo dependencies if needed
47
40
sudo apt-get update
48
- sudo apt-get install -y patchelf libosmesa6- dev libgl1-mesa-glx libglfw3 libglew -dev
41
+ sudo apt-get install -y libglu1-mesa- dev libgl1-mesa-dev libosmesa6 -dev xvfb unzip patchelf ffmpeg cmake swig
49
42
50
- - name : Install MuJoCo (for MuJoCo tutorials)
43
+ - name : Install Gymnasium basics tutorial requirements
44
+ if : matrix.tutorial-group == 'gymnasium_basics'
45
+ run : |
46
+ pip install ".[mujoco, box2d]"
47
+
48
+ - name : Install Training Agents tutorial requirements
51
49
if : matrix.tutorial-group == 'training_agents'
52
50
run : |
53
- pip install mujoco gymnasium [mujoco]
51
+ pip install ". [mujoco, toy_text, box2d]" torch seaborn matplotlib pandas tqdm
54
52
55
53
- name : Get changed files
56
54
id : changed-files
59
57
files : docs/tutorials/**/*.py
60
58
if : github.event_name == 'pull_request'
61
59
60
+ - name : Patch tutorials
61
+ run : |
62
+ # Patch load_quadruped_model.py to use the built-in ant.xml instead of the missing mujoco_menagerie file
63
+ if [ -f "docs/tutorials/gymnasium_basics/load_quadruped_model.py" ]; then
64
+ sed -i 's|"./mujoco_menagerie/unitree_go1/scene.xml"|"ant.xml"|g' docs/tutorials/gymnasium_basics/load_quadruped_model.py
65
+ fi
66
+
67
+ # Patch mujoco_reinforce.py to reduce the number of episodes for CI
68
+ if [ -f "docs/tutorials/training_agents/mujoco_reinforce.py" ]; then
69
+ sed -i 's/total_num_episodes = int(5e3)/total_num_episodes = int(1e2)/g' docs/tutorials/training_agents/mujoco_reinforce.py
70
+ fi
71
+
72
+ # Patch frozenlake_q_learning.py to reduce the number of episodes and runs
73
+ if [ -f "docs/tutorials/training_agents/frozenlake_q_learning.py" ]; then
74
+ sed -i 's/total_episodes=2000/total_episodes=200/g' docs/tutorials/training_agents/frozenlake_q_learning.py
75
+ sed -i 's/n_runs=20/n_runs=3/g' docs/tutorials/training_agents/frozenlake_q_learning.py
76
+ sed -i 's/map_sizes = \[4, 7, 9, 11\]/map_sizes = \[4, 7\]/g' docs/tutorials/training_agents/frozenlake_q_learning.py
77
+ fi
78
+
79
+ # Patch vector_a2c.py to reduce the number of updates and environments
80
+ if [ -f "docs/tutorials/training_agents/vector_a2c.py" ]; then
81
+ sed -i 's/n_envs = 10/n_envs = 4/g' docs/tutorials/training_agents/vector_a2c.py
82
+ sed -i 's/n_updates = 1000/n_updates = 100/g' docs/tutorials/training_agents/vector_a2c.py
83
+ fi
84
+
85
+ # Make sure we use 'rgb_array' render mode instead of 'human' everywhere to avoid display issues
86
+ find docs/tutorials -name "*.py" -type f -exec sed -i 's/render_mode="human"/render_mode="rgb_array"/g' {} \;
87
+
62
88
- name : Test tutorials (${{ matrix.tutorial-group }})
63
89
id : run-tutorials
64
90
run : |
67
93
68
94
# Determine which tutorials to test
69
95
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
70
- echo "PR detected - testing only modified tutorials"
71
96
# Get the list of modified tutorial files in this group
72
97
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
73
98
if [[ $file == docs/tutorials/${{ matrix.tutorial-group }}/* && $file == *.py ]]; then
@@ -77,14 +102,10 @@ jobs:
77
102
78
103
# If no tutorials in this group were modified, skip this job
79
104
if [ ! -f tutorial_files.txt ] || [ ! -s tutorial_files.txt ]; then
80
- echo "No tutorials modified in ${{ matrix.tutorial-group }} - skipping tests"
81
- echo "total=0" >> $GITHUB_OUTPUT
82
- echo "passed=0" >> $GITHUB_OUTPUT
83
- echo "failed=0" >> $GITHUB_OUTPUT
105
+ echo "No tutorials modified in ${{ matrix.tutorial-group }} - skipping"
84
106
exit 0
85
107
fi
86
108
else
87
- echo "Main branch or manual run - testing all tutorials"
88
109
# Find all Python files in the tutorial group
89
110
find docs/tutorials/${{ matrix.tutorial-group }} -name "*.py" -type f | sort > tutorial_files.txt
90
111
fi
@@ -94,81 +115,64 @@ jobs:
94
115
passed=0
95
116
failed=0
96
117
97
- # Run each tutorial with timeout
118
+ # Run each tutorial with timeout - continue even if one fails
98
119
while IFS= read -r tutorial; do
120
+ # Clear separator for better readability
121
+ echo ""
122
+ echo "========================================================"
99
123
echo "Running tutorial: $tutorial"
124
+ echo "========================================================"
100
125
total=$((total+1))
101
126
102
127
# Set max time based on complexity (can be adjusted)
103
128
max_time=300 # 5 minutes default
104
129
105
- # Create a marker to skip rendering for headless environment
106
- sed -i 's/render_mode="human"/render_mode="rgb_array"/g' "$tutorial" || true
130
+ # Create log file path
131
+ log_file="test-results/$(basename "$tutorial").log"
107
132
108
133
# Run the tutorial with timeout and record results
109
134
start_time=$(date +%s)
110
- timeout $max_time python "$tutorial" > "test-results/$(basename "$tutorial").log" 2>&1
135
+ # Use set +e so the script continues even if the command fails
136
+ set +e
137
+ timeout $max_time python "$tutorial" > "$log_file" 2>&1
111
138
exit_code=$?
139
+ set -e
112
140
end_time=$(date +%s)
113
141
execution_time=$((end_time-start_time))
114
142
143
+ # Output results to console immediately
115
144
if [ $exit_code -eq 0 ]; then
116
- echo "✅ Passed : $tutorial (${execution_time}s)"
145
+ echo "✅ PASSED : $tutorial (${execution_time}s)"
117
146
passed=$((passed+1))
118
147
echo "$tutorial,pass,$execution_time" >> test-results/summary.csv
119
148
elif [ $exit_code -eq 124 ]; then
120
- echo "⚠️ Timeout : $tutorial (exceeded ${max_time}s)"
149
+ echo "⚠️ TIMEOUT : $tutorial (exceeded ${max_time}s)"
121
150
failed=$((failed+1))
122
151
echo "$tutorial,timeout,$max_time" >> test-results/summary.csv
152
+ # Show the last output before timeout
153
+ echo "Last output before timeout:"
154
+ echo "----------------------------------------"
155
+ tail -n 20 "$log_file"
156
+ echo "----------------------------------------"
123
157
else
124
- echo "❌ Failed : $tutorial (${execution_time}s)"
158
+ echo "❌ FAILED : $tutorial (${execution_time}s)"
125
159
failed=$((failed+1))
126
160
echo "$tutorial,fail,$execution_time" >> test-results/summary.csv
161
+ # Show the error details
162
+ echo "Error details:"
163
+ echo "----------------------------------------"
164
+ cat "$log_file"
165
+ echo "----------------------------------------"
127
166
fi
128
167
129
- echo "----------------------------------------"
130
168
done < tutorial_files.txt
131
169
132
- echo "::endgroup::"
133
-
134
- # Set output variables
170
+ # Export the counters as outputs for later steps
135
171
echo "total=$total" >> $GITHUB_OUTPUT
136
172
echo "passed=$passed" >> $GITHUB_OUTPUT
137
173
echo "failed=$failed" >> $GITHUB_OUTPUT
138
174
139
- # Generate summary
140
- echo "### Tutorial Test Results for ${{ matrix.tutorial-group }} 📊" >> $GITHUB_STEP_SUMMARY
141
- echo "" >> $GITHUB_STEP_SUMMARY
142
-
143
- if [[ "${{ github.event_name }}" == "pull_request" ]]; then
144
- echo "**Mode:** Testing only modified tutorials in PR #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
145
- else
146
- echo "**Mode:** Testing all tutorials (main branch or manual run)" >> $GITHUB_STEP_SUMMARY
147
- fi
148
- echo "" >> $GITHUB_STEP_SUMMARY
149
-
150
- echo "| Metric | Count |" >> $GITHUB_STEP_SUMMARY
151
- echo "| ------ | ----- |" >> $GITHUB_STEP_SUMMARY
152
- echo "| ✅ Passed | $passed |" >> $GITHUB_STEP_SUMMARY
153
- echo "| ❌ Failed | $failed |" >> $GITHUB_STEP_SUMMARY
154
- echo "| 📚 Total | $total |" >> $GITHUB_STEP_SUMMARY
155
-
156
- # List all tested tutorials
157
- if [ $total -gt 0 ]; then
158
- echo "" >> $GITHUB_STEP_SUMMARY
159
- echo "### Tested Tutorials 📝" >> $GITHUB_STEP_SUMMARY
160
- echo "" >> $GITHUB_STEP_SUMMARY
161
-
162
- while IFS=, read -r file status time; do
163
- if [ "$status" == "pass" ]; then
164
- echo "- ✅ $file (${time}s)" >> $GITHUB_STEP_SUMMARY
165
- elif [ "$status" == "timeout" ]; then
166
- echo "- ⚠️ $file (timeout after ${time}s)" >> $GITHUB_STEP_SUMMARY
167
- else
168
- echo "- ❌ $file (failed after ${time}s)" >> $GITHUB_STEP_SUMMARY
169
- fi
170
- done < test-results/summary.csv
171
- fi
175
+ echo "::endgroup::"
172
176
173
177
- name : Upload test results
174
178
if : always()
@@ -184,8 +188,11 @@ jobs:
184
188
if [ "${{ steps.run-tutorials.outputs.total }}" -eq 0 ]; then
185
189
echo "::notice::No tutorials were tested in this group."
186
190
elif [ "${{ steps.run-tutorials.outputs.failed }}" -gt 0 ]; then
187
- echo "::error::${{ steps.run-tutorials.outputs.failed }} out of ${{ steps.run-tutorials.outputs.total }} tutorials failed."
188
- exit 1
191
+ echo "::warning::${{ steps.run-tutorials.outputs.failed }} out of ${{ steps.run-tutorials.outputs.total }} tutorials failed. Read in Test tutorials and click `Running tutorials...` to see the error messages."
189
192
else
190
193
echo "::notice::All ${{ steps.run-tutorials.outputs.total }} tutorials passed."
191
194
fi
195
+
196
+ # This ensures the job reports failure if any tutorials failed,
197
+ # but without stopping other jobs in the matrix
198
+ [ "${{ steps.run-tutorials.outputs.failed }}" -eq 0 ]
0 commit comments