mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-31 00:10:04 +08:00
Fail screenshot CI if fixtures with tag blocks-ci change
This commit is contained in:
committed by
Henning Dieterichs
parent
77387b3815
commit
e2a4bce63e
54
.github/workflows/screenshot-test.yml
vendored
54
.github/workflows/screenshot-test.yml
vendored
@@ -55,6 +55,52 @@ jobs:
|
||||
- name: Capture screenshots
|
||||
run: ./node_modules/.bin/component-explorer render --project ./test/componentFixtures/component-explorer.json
|
||||
|
||||
- name: Check blocks-ci screenshots
|
||||
id: blocks-ci
|
||||
run: |
|
||||
node build/lib/screenshotBlocksCi.ts \
|
||||
test/componentFixtures/.screenshots/current/manifest.json \
|
||||
test/componentFixtures/blocks-ci-screenshots.md \
|
||||
https://hediet-screenshots.azurewebsites.net \
|
||||
--json \
|
||||
> /tmp/blocks-ci-diff.json 2>/tmp/blocks-ci-stderr.txt \
|
||||
&& echo "match=true" >> "$GITHUB_OUTPUT" \
|
||||
|| {
|
||||
echo "match=false" >> "$GITHUB_OUTPUT"
|
||||
cat /tmp/blocks-ci-stderr.txt >&2
|
||||
DIFF=$(cat /tmp/blocks-ci-diff.json)
|
||||
echo "diff<<BLOCKS_CI_EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$DIFF" >> "$GITHUB_OUTPUT"
|
||||
echo "BLOCKS_CI_EOF" >> "$GITHUB_OUTPUT"
|
||||
}
|
||||
|
||||
- name: Suggest blocks-ci screenshot update
|
||||
if: github.event_name == 'pull_request' && steps.blocks-ci.outputs.match == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const diff = JSON.parse(process.env.DIFF_JSON);
|
||||
const filePath = 'test/componentFixtures/blocks-ci-screenshots.md';
|
||||
const marker = '<!-- blocks-ci-screenshot-suggestion -->';
|
||||
|
||||
await github.rest.pulls.createReview({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
commit_id: context.payload.pull_request.head.sha,
|
||||
event: 'COMMENT',
|
||||
body: `${marker}\n### blocks-ci screenshots changed\n\nApply the suggestion below to update the committed screenshots.`,
|
||||
comments: [{
|
||||
path: filePath,
|
||||
start_line: diff.startLine < diff.endLine ? diff.startLine : undefined,
|
||||
line: diff.endLine,
|
||||
side: 'RIGHT',
|
||||
body: `${marker}\nScreenshots for \`blocks-ci\` fixtures have changed. Apply this suggestion to accept:\n\n\`\`\`suggestion\n${diff.replacement}\n\`\`\``,
|
||||
}],
|
||||
});
|
||||
env:
|
||||
DIFF_JSON: ${{ steps.blocks-ci.outputs.diff }}
|
||||
|
||||
- name: Upload screenshots
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
@@ -70,7 +116,7 @@ jobs:
|
||||
echo "::add-mask::$TOKEN"
|
||||
echo "token=$TOKEN" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload screenshots
|
||||
- name: Upload screenshots to service
|
||||
run: |
|
||||
cd test/componentFixtures/.screenshots/current
|
||||
zip -qr "$GITHUB_WORKSPACE/screenshots.zip" .
|
||||
@@ -135,6 +181,12 @@ jobs:
|
||||
env:
|
||||
COMMENT_BODY: ${{ steps.diff.outputs.body }}
|
||||
|
||||
- name: Fail if blocks-ci hashes changed
|
||||
if: steps.blocks-ci.outputs.match == 'false'
|
||||
run: |
|
||||
echo "::error::blocks-ci screenshot hashes do not match committed file. See PR review suggestion to update."
|
||||
exit 1
|
||||
|
||||
# - name: Compare screenshots
|
||||
# id: compare
|
||||
# run: |
|
||||
|
||||
99
build/lib/screenshotBlocksCi.ts
Normal file
99
build/lib/screenshotBlocksCi.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Compares blocks-ci fixture screenshots from a rendered manifest against the committed markdown.
|
||||
// Usage: node build/lib/screenshotBlocksCi.ts <manifest-path> <markdown-path> <service-url> [--json]
|
||||
//
|
||||
// Exit codes:
|
||||
// 0 — all screenshots match (or no blocks-ci fixtures)
|
||||
// 1 — mismatches found
|
||||
//
|
||||
// Without --json: prints the full updated markdown to stdout.
|
||||
// With --json: prints a JSON object with { startLine, endLine, replacement } for a GitHub suggestion.
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
const HEADER = '<!-- auto-generated by CI — do not edit manually -->';
|
||||
|
||||
interface ManifestFixture {
|
||||
readonly fixtureId: string;
|
||||
readonly imageHash: string;
|
||||
readonly labels?: readonly string[];
|
||||
}
|
||||
|
||||
interface Manifest {
|
||||
readonly fixtures: readonly ManifestFixture[];
|
||||
}
|
||||
|
||||
function generateMarkdown(fixtures: readonly ManifestFixture[], serviceUrl: string): string {
|
||||
const lines: string[] = [HEADER, ''];
|
||||
const seen = new Set<string>();
|
||||
const sorted = [...fixtures].sort((a, b) => a.fixtureId.localeCompare(b.fixtureId));
|
||||
for (const f of sorted) {
|
||||
if (f.labels?.includes('blocks-ci') && !seen.has(f.fixtureId)) {
|
||||
seen.add(f.fixtureId);
|
||||
lines.push(`#### ${f.fixtureId}`);
|
||||
lines.push(``);
|
||||
lines.push('');
|
||||
}
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function computeDiff(committedLines: string[], updatedLines: string[]): { startLine: number; endLine: number; replacement: string } {
|
||||
let firstDiff = 0;
|
||||
while (firstDiff < committedLines.length && firstDiff < updatedLines.length && committedLines[firstDiff] === updatedLines[firstDiff]) {
|
||||
firstDiff++;
|
||||
}
|
||||
|
||||
let committedEnd = committedLines.length - 1;
|
||||
let updatedEnd = updatedLines.length - 1;
|
||||
while (committedEnd > firstDiff && updatedEnd > firstDiff && committedLines[committedEnd] === updatedLines[updatedEnd]) {
|
||||
committedEnd--;
|
||||
updatedEnd--;
|
||||
}
|
||||
|
||||
return {
|
||||
startLine: firstDiff + 1, // 1-indexed
|
||||
endLine: committedEnd + 1, // 1-indexed
|
||||
replacement: updatedLines.slice(firstDiff, updatedEnd + 1).join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const args = process.argv.slice(2);
|
||||
const jsonMode = args.includes('--json');
|
||||
const positional = args.filter(a => a !== '--json');
|
||||
const [manifestPath, markdownPath, serviceUrl] = positional;
|
||||
|
||||
if (!manifestPath || !markdownPath || !serviceUrl) {
|
||||
console.error('Usage: node build/lib/screenshotBlocksCi.ts <manifest-path> <markdown-path> <service-url> [--json]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const manifest: Manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
||||
const actual = generateMarkdown(manifest.fixtures, serviceUrl);
|
||||
|
||||
let committed = '';
|
||||
if (fs.existsSync(markdownPath)) {
|
||||
committed = fs.readFileSync(markdownPath, 'utf8');
|
||||
}
|
||||
|
||||
if (actual === committed) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.error('blocks-ci screenshots have changed.');
|
||||
|
||||
if (jsonMode) {
|
||||
const diff = computeDiff(committed.split('\n'), actual.split('\n'));
|
||||
process.stdout.write(JSON.stringify(diff) + '\n');
|
||||
} else {
|
||||
process.stdout.write(actual);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
main();
|
||||
31
test/componentFixtures/blocks-ci-screenshots.md
Normal file
31
test/componentFixtures/blocks-ci-screenshots.md
Normal file
@@ -0,0 +1,31 @@
|
||||
<!-- auto-generated by CI — do not edit manually -->
|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTabNarrow/Dark
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTabNarrow/Light
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/LocalHarness/Dark
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/LocalHarness/Light
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTab/Dark
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTab/Light
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTabNarrow/Dark
|
||||

|
||||
|
||||
#### chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTabNarrow/Light
|
||||

|
||||
|
||||
#### editor/codeEditor/CodeEditor/Dark
|
||||

|
||||
|
||||
#### editor/codeEditor/CodeEditor/Light
|
||||

|
||||
Reference in New Issue
Block a user