-
Notifications
You must be signed in to change notification settings - Fork 2k
338 lines (308 loc) · 13 KB
/
test-ui.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
name: test-ui
on:
pull_request:
paths:
- "ui/**"
push:
branches:
- main
- release/**
- test-ui
paths:
- "ui/**"
jobs:
pre-test:
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: ui
outputs:
nonce: ${{ steps.nonce.outputs.nonce }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ./.github/actions/setup-js
- name: lint:js
run: yarn run lint:js
- name: lint:hbs
run: yarn run lint:hbs
- id: nonce
name: nonce
run: echo "nonce=${{ github.run_id }}-$(date +%s)" >> "$GITHUB_OUTPUT"
tests:
needs:
- pre-test
runs-on: ${{ endsWith(github.repository, '-enterprise') && fromJSON('["self-hosted", "ondemand", "linux", "type=m7a.2xlarge;m6a.2xlarge"]') || 'ubuntu-latest' }}
timeout-minutes: 30
continue-on-error: true
defaults:
run:
working-directory: ui
strategy:
matrix:
partition: [1, 2, 3, 4]
split: [4]
# Note: If we ever change the number of partitions, we'll need to update the
# finalize.combine step to match
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ./.github/actions/setup-js
- uses: browser-actions/setup-chrome@facf10a55b9caf92e0cc749b4f82bf8220989148 # v1.7.2
- name: Retrieve Vault-hosted Secrets
if: endsWith(github.repository, '-enterprise')
id: vault
uses: hashicorp/vault-action@d1720f055e0635fd932a1d2a48f87a666a57906c # v3.0.0
with:
url: ${{ vars.CI_VAULT_URL }}
method: ${{ vars.CI_VAULT_METHOD }}
path: ${{ vars.CI_VAULT_PATH }}
jwtGithubAudience: ${{ vars.CI_VAULT_AUD }}
secrets: |-
kv/data/teams/nomad/ui PERCY_TOKEN ;
- name: ember exam
id: exam
env:
PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }}
PERCY_PARALLEL_NONCE: ${{ needs.pre-test.outputs.nonce }}
run: |
yarn exam:parallel --split=${{ matrix.split }} --partition=${{ matrix.partition }} --json-report=test-results/test-results.json
# We have continue-on-error set to true, but we still want to alert the author if
# there are test failures or timeouts. Without it, we'll get errors in our output,
# but the workflow will still succeed / have a green checkmark.
- name: Express timeout failure
if: ${{ failure() }}
run: exit 1
- name: Check test status
if: steps.exam.outcome != 'success'
run: |
echo "Tests failed or timed out in partition ${{ matrix.partition }}"
exit 1
- name: Upload partition test results
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'pull_request'
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: test-results-${{ matrix.partition }}
path: ui/test-results/test-results.json
retention-days: 90
finalize:
needs:
- pre-test
- tests
runs-on: ${{ endsWith(github.repository, '-enterprise') && fromJSON('["self-hosted", "ondemand", "linux", "type=m7a.2xlarge;m6a.2xlarge"]') || 'ubuntu-latest' }}
timeout-minutes: 30
defaults:
run:
working-directory: ui
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ./.github/actions/setup-js
- name: Retrieve Vault-hosted Secrets
if: endsWith(github.repository, '-enterprise')
id: vault
uses: hashicorp/vault-action@d1720f055e0635fd932a1d2a48f87a666a57906c # v3.0.0
with:
url: ${{ vars.CI_VAULT_URL }}
method: ${{ vars.CI_VAULT_METHOD }}
path: ${{ vars.CI_VAULT_PATH }}
jwtGithubAudience: ${{ vars.CI_VAULT_AUD }}
secrets: |-
kv/data/teams/nomad/ui PERCY_TOKEN ;
- name: Download all test results
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'pull_request'
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
pattern: test-results-*
path: test-results
- name: Combine test results for comparison
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'pull_request'
run: node ../scripts/combine-ui-test-results.js
- name: Upload combined results for comparison
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: test-results-${{ github.sha }}
path: ui/combined-test-results.json
retention-days: 90
- name: Delete partition test results
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'pull_request'
uses: geekyeggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b # v5.1.0
with:
name: test-results-*
- name: Upload Current PR results
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: pr-test-results-${{ github.sha }} # Prefix with "pr-" to avoid comparing with main during analyze step
path: ui/combined-test-results.json
retention-days: 1
- name: finalize
env:
PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }}
PERCY_PARALLEL_NONCE: ${{ needs.pre-test.outputs.nonce }}
run: yarn percy build:finalize
analyze-times:
# TODO: temporary comment-out with hardcoded sha
# needs: [tests, finalize]
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
defaults:
run:
working-directory: ui
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Debug step to show environment
- name: Debug environment
run: |
echo "GITHUB_SHA: ${{ github.sha }}"
echo "GITHUB_EVENT_NAME: ${{ github.event_name }}"
echo "GITHUB_REF: ${{ github.ref }}"
echo "RUN_ID: ${{ github.run_id }}"
# Try to list available artifacts first
- name: List artifacts
uses: actions/github-script@v7
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
// run_id: context.runId
run_id: 12163157778
});
console.log('Available artifacts:');
console.log(JSON.stringify(artifacts.data, null, 2));
- name: Download current PR results
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
# name: test-results-${{ github.sha }}
name: pr-test-results-fe7ca11e9afc42bc98d79fe521155a37634bd232 # TODO: temporary hardcoded sha from previous run
path: ui
run-id: 12163157778
github-token: ${{ secrets.GITHUB_TOKEN }}
# - name: Download historical results
# uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
# with:
# pattern: test-results-*
# path: historical-results
# merge-multiple: true
# Download historical results from previous main branch runs
- name: Download historical results
uses: actions/github-script@v7
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
const fs = require('fs');
const path = require('path');
const historicalDir = path.join('ui', 'historical-results');
// Clean up any existing directory
if (fs.existsSync(historicalDir)) {
fs.rmSync(historicalDir, { recursive: true, force: true });
}
fs.mkdirSync(historicalDir, { recursive: true });
const artifacts = await github.rest.actions.listArtifactsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
});
// Log out the names of each artifact
console.log('Available artifacts:');
artifacts.data.artifacts.forEach(artifact => {
console.log(`- ${artifact.name}`);
});
const testArtifacts = artifacts.data.artifacts.filter(artifact =>
artifact.name.startsWith('pr-test-results-')
);
console.log(`Found ${testArtifacts.length} test result artifacts`);
for (const artifact of testArtifacts) {
try {
console.log(`Downloading ${artifact.name}`);
// Create a temporary directory for this artifact
const tempDir = path.join(historicalDir, `temp-${artifact.id}`);
fs.mkdirSync(tempDir, { recursive: true });
try {
// Download to temp directory
await exec.exec('gh', [
'run',
'download',
'-n',
artifact.name,
'--repo',
`${context.repo.owner}/${context.repo.repo}`,
'--dir',
tempDir,
artifact.workflow_run.id.toString()
]);
// Move the JSON file to the historical directory with a unique name
const jsonFile = path.join(tempDir, 'combined-test-results.json');
if (fs.existsSync(jsonFile)) {
fs.renameSync(
jsonFile,
path.join(historicalDir, `${artifact.name}-${artifact.id}.json`)
);
console.log(`Successfully processed ${artifact.name}`);
} else {
const files = fs.readdirSync(tempDir);
console.log(`Warning: No test results JSON found in ${artifact.name}. Found files:`, files);
console.log(`Warning: No combined-test-results.json found in ${artifact.name}`);
}
} finally {
// Always clean up temp directory
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
}
} catch (error) {
console.log(`Error processing ${artifact.name}:`, error.message);
// Continue with next artifact
}
}
# Debug what we got
- name: Debug directories
run: |
echo "Current directory structure:"
ls -la
printf "\nHistorical results directory:\n"
ls -la historical-results || echo "historical-results directory not found"
cd historical-results
echo -e "\nContents of each file (first 10 lines):"
for file in *.json; do
if [ -f "$file" ]; then
echo -e "\n=== $file ==="
head -n 10 "$file"
fi
done
- name: Analyze test times
run: node ../scripts/analyze-ui-test-times.js
- name: Comment PR
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const fs = require('fs');
const analysis = JSON.parse(fs.readFileSync('ui/test-time-analysis.json'));
let body = `### Test Time Analysis\n\n`;
body += `- Total Tests: ${analysis.summary.totalTests}\n`;
body += `- Significantly Slower: ${analysis.summary.significantlySlower}\n`;
body += `- Significantly Faster: ${analysis.summary.significantlyFaster}\n\n`;
if (analysis.testComparisons.length > 0) {
body += `#### Most Significant Changes:\n\n`;
analysis.testComparisons
.filter(comp => comp.percentDiff != null) // Skip invalid comparisons
.slice(0, 5)
.forEach(comp => {
body += `**${comp.name}**\n`;
body += `- Current: ${comp.currentDuration}ms\n`;
body += `- Historical Avg: ${comp.historicalAverage}ms\n`;
body += `- Change: ${comp.percentDiff?.toFixed(1) || 'N/A'}%\n\n`;
body += `- Trend: ${comp.trend}\n\n`;
});
}
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body
});
permissions:
contents: read
id-token: write
pull-requests: write