blob: b13c58df1e53845b50966011984ffdd688eeff8d [file] [log] [blame] [edit]
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:io/io.dart';
import 'package:path/path.dart' as path;
import '../utils.dart';
const _runIdArg = 'run-id';
/// A command for downloading and applying golden fixes, when they are broken.
class FixGoldensCommand extends Command {
FixGoldensCommand() {
argParser.addOption(
_runIdArg,
help: 'The ID of the workflow run where the goldens are failing. '
'e.g.https://github.com/flutter/devtools/actions/runs/<run-id>/job/16691428186',
valueHelp: '12345',
mandatory: true,
);
}
@override
String get description =>
'A command for downloading and applying golden fixes when they are broken on the CI.';
@override
String get name => 'fix-goldens';
final processManager = ProcessManager();
@override
FutureOr? run() async {
// Change the CWD to the repo root
Directory.current = pathFromRepoRoot("");
final runId = argResults![_runIdArg] as String;
final tmpDownloadDir = await Directory.systemTemp.createTemp();
try {
print('Downloading the artifacts to ${tmpDownloadDir.path}');
await processManager.runProcess(
CliCommand(
'gh',
[
'run',
'download',
runId,
'-p',
'*golden_image_failures*',
'-R',
'github.com/flutter/devtools',
'-D',
tmpDownloadDir.path,
],
),
);
final downloadedGoldens = tmpDownloadDir
.listSync(recursive: true)
.where((e) => e.path.endsWith('testImage.png'));
final allLocalGoldenPngs =
Directory(pathFromRepoRoot("packages/devtools_app/test/"))
.listSync(recursive: true)
..where((e) => e.path.endsWith('.png'));
for (final downloadedGolden in downloadedGoldens) {
final downloadedGoldenBaseName = path.basename(downloadedGolden.path);
final expectedGoldenFileName =
'${RegExp(r'(^.*)_testImage.png').firstMatch(downloadedGoldenBaseName)?.group(1)}.png';
final fileMatches = allLocalGoldenPngs.where(
(e) => e.path.endsWith(expectedGoldenFileName),
);
final String destinationPath;
if (fileMatches.isEmpty) {
throw 'Could not find a golden Image for $downloadedGoldenBaseName using $expectedGoldenFileName as '
'the name of the search.';
} else if (fileMatches.length == 1) {
destinationPath = fileMatches.first.path;
} else {
print("Multiple goldens found for ${downloadedGolden.path}");
print("Select which golden should be overridden:");
for (int i = 0; i < fileMatches.length; i++) {
final fileMatch = fileMatches.elementAt(i);
print('${i + 1}) ${fileMatch.path}');
}
final userSelection = int.parse(stdin.readLineSync()!);
destinationPath = fileMatches.elementAt(userSelection - 1).path;
}
await downloadedGolden.rename(destinationPath);
print("Fixed: $destinationPath");
}
print('Done updating ${downloadedGoldens.length} goldens');
} finally {
tmpDownloadDir.deleteSync(recursive: true);
}
}
}