@@ -65,36 +65,77 @@ jobs:
6565 - name : Build
6666 run : npm run build
6767
68- - name : Run core integration tests (retry once on failure)
68+ - name : Run core integration tests (retry once on failure + runtime budget )
6969 id : integration_core
70+ shell : bash
71+ timeout-minutes : 12
7072 run : |
7173 set +e
72- npm run test:integration:core
74+ budget_seconds=120
75+
76+ run_core() {
77+ local attempt="$1"
78+ local log_file="integration-core-${attempt}.log"
79+ local started_at ended_at status
80+
81+ started_at=$(date +%s)
82+ npm run test:integration:core 2>&1 | tee "$log_file"
83+ status=${PIPESTATUS[0]}
84+ ended_at=$(date +%s)
85+ duration_seconds=$((ended_at - started_at))
86+
87+ echo "### Core integration (${attempt})" >> "$GITHUB_STEP_SUMMARY"
88+ echo "- status: ${status}" >> "$GITHUB_STEP_SUMMARY"
89+ echo "- duration: ${duration_seconds}s (budget ${budget_seconds}s)" >> "$GITHUB_STEP_SUMMARY"
90+ return "$status"
91+ }
92+
93+ run_core "run1"
7394 first_exit=$?
74- if [ "$first_exit" -eq 0 ]; then
75- echo "flaky=false" >> "$GITHUB_OUTPUT"
76- exit 0
77- fi
95+ first_duration=$duration_seconds
96+
97+ if [ "$first_exit" -ne 0 ]; then
98+ echo "First core integration run failed. Retrying once..."
99+ run_core "run2"
100+ second_exit=$?
101+ second_duration=$duration_seconds
102+ if [ "$second_exit" -ne 0 ]; then
103+ echo "flaky=false" >> "$GITHUB_OUTPUT"
104+ exit "$second_exit"
105+ fi
78106
79- echo "First core integration run failed. Retrying once..."
80- npm run test:integration:core
81- second_exit=$?
82- if [ "$second_exit" -eq 0 ]; then
83107 echo "flaky=true" >> "$GITHUB_OUTPUT"
84- exit 0
108+ final_duration=$second_duration
109+ else
110+ echo "flaky=false" >> "$GITHUB_OUTPUT"
111+ final_duration=$first_duration
85112 fi
86113
87- echo "flaky=false" >> "$GITHUB_OUTPUT"
88- exit "$second_exit"
114+ echo "final_duration=${final_duration}" >> "$GITHUB_OUTPUT"
115+ if [ "$final_duration" -gt "$budget_seconds" ]; then
116+ echo "Core integration exceeded runtime budget (${final_duration}s > ${budget_seconds}s)."
117+ exit 1
118+ fi
89119
90120 - name : Annotate flaky core integration
91121 if : steps.integration_core.outputs.flaky == 'true'
92122 run : |
93123 echo "### Flaky integration detected (core)" >> "$GITHUB_STEP_SUMMARY"
94124 echo "- First run failed, retry passed." >> "$GITHUB_STEP_SUMMARY"
95125
126+ - name : Upload core integration logs on failure
127+ if : failure()
128+ uses : actions/upload-artifact@v4
129+ with :
130+ name : integration-core-logs
131+ path : integration-core-*.log
132+ retention-days : 7
133+
96134 test-integration-cli :
97135 runs-on : ubuntu-latest
136+ strategy :
137+ matrix :
138+ suite : [smoke, core, aux]
98139 env :
99140 HUSKY : 0
100141
@@ -113,34 +154,82 @@ jobs:
113154 - name : Build
114155 run : npm run build
115156
116- - name : Run CLI integration tests (retry once on failure)
157+ - name : Run CLI integration suite (retry once on failure + runtime budget )
117158 id : integration_cli
159+ shell : bash
160+ timeout-minutes : 12
118161 run : |
119162 set +e
120- npm run test:integration:cli
163+ case "${{ matrix.suite }}" in
164+ smoke)
165+ budget_seconds=90
166+ ;;
167+ core|aux)
168+ budget_seconds=180
169+ ;;
170+ *)
171+ budget_seconds=180
172+ ;;
173+ esac
174+
175+ run_suite() {
176+ local attempt="$1"
177+ local log_file="integration-cli-${{ matrix.suite }}-${attempt}.log"
178+ local started_at ended_at status
179+
180+ started_at=$(date +%s)
181+ npm run "test:integration:cli:${{ matrix.suite }}" 2>&1 | tee "$log_file"
182+ status=${PIPESTATUS[0]}
183+ ended_at=$(date +%s)
184+ duration_seconds=$((ended_at - started_at))
185+
186+ echo "### CLI integration (${{ matrix.suite }} / ${attempt})" >> "$GITHUB_STEP_SUMMARY"
187+ echo "- status: ${status}" >> "$GITHUB_STEP_SUMMARY"
188+ echo "- duration: ${duration_seconds}s (budget ${budget_seconds}s)" >> "$GITHUB_STEP_SUMMARY"
189+ return "$status"
190+ }
191+
192+ run_suite "run1"
121193 first_exit=$?
122- if [ "$first_exit" -eq 0 ]; then
123- echo "flaky=false" >> "$GITHUB_OUTPUT"
124- exit 0
125- fi
194+ first_duration=$duration_seconds
195+
196+ if [ "$first_exit" -ne 0 ]; then
197+ echo "First CLI integration run failed for suite '${{ matrix.suite }}'. Retrying once..."
198+ run_suite "run2"
199+ second_exit=$?
200+ second_duration=$duration_seconds
201+ if [ "$second_exit" -ne 0 ]; then
202+ echo "flaky=false" >> "$GITHUB_OUTPUT"
203+ exit "$second_exit"
204+ fi
126205
127- echo "First CLI integration run failed. Retrying once..."
128- npm run test:integration:cli
129- second_exit=$?
130- if [ "$second_exit" -eq 0 ]; then
131206 echo "flaky=true" >> "$GITHUB_OUTPUT"
132- exit 0
207+ final_duration=$second_duration
208+ else
209+ echo "flaky=false" >> "$GITHUB_OUTPUT"
210+ final_duration=$first_duration
133211 fi
134212
135- echo "flaky=false" >> "$GITHUB_OUTPUT"
136- exit "$second_exit"
213+ echo "final_duration=${final_duration}" >> "$GITHUB_OUTPUT"
214+ if [ "$final_duration" -gt "$budget_seconds" ]; then
215+ echo "CLI integration suite '${{ matrix.suite }}' exceeded runtime budget (${final_duration}s > ${budget_seconds}s)."
216+ exit 1
217+ fi
137218
138219 - name : Annotate flaky CLI integration
139220 if : steps.integration_cli.outputs.flaky == 'true'
140221 run : |
141- echo "### Flaky integration detected (cli)" >> "$GITHUB_STEP_SUMMARY"
222+ echo "### Flaky integration detected (cli / ${{ matrix.suite }} )" >> "$GITHUB_STEP_SUMMARY"
142223 echo "- First run failed, retry passed." >> "$GITHUB_STEP_SUMMARY"
143224
225+ - name : Upload CLI integration logs on failure
226+ if : failure()
227+ uses : actions/upload-artifact@v4
228+ with :
229+ name : integration-cli-logs-${{ matrix.suite }}
230+ path : integration-cli-${{ matrix.suite }}-*.log
231+ retention-days : 7
232+
144233 lint :
145234 runs-on : ubuntu-latest
146235 env :
0 commit comments