Unverified Commit 2b3cd7f4 authored by Victoria Ashworth's avatar Victoria Ashworth Committed by GitHub

Replace rsync when unzipping artifacts on a Mac (#126703)

Instead of using rsync, which has caused errors in the past (https://github.com/flutter/flutter/issues/99785), delete the file/directory/link prior to moving it.

Hopefully should let us stop double zipping the FlutterMacOS.framework in the engine: https://github.com/flutter/engine/pull/41306/files

Part of https://github.com/flutter/flutter/issues/126016.
parent a3b38aa8
...@@ -8,6 +8,7 @@ import 'package:meta/meta.dart'; ...@@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'common.dart'; import 'common.dart';
import 'error_handling_io.dart';
import 'file_system.dart'; import 'file_system.dart';
import 'io.dart'; import 'io.dart';
import 'logger.dart'; import 'logger.dart';
...@@ -423,7 +424,11 @@ class _MacOSUtils extends _PosixUtils { ...@@ -423,7 +424,11 @@ class _MacOSUtils extends _PosixUtils {
return _hostPlatform!; return _hostPlatform!;
} }
// unzip, then rsync /// Unzip into a temporary directory.
///
/// For every file/directory/link in the unzipped file, delete the
/// corresponding entity in the [targetDirectory] before moving from the
/// temporary directory to the [targetDirectory].
@override @override
void unzip(File file, Directory targetDirectory) { void unzip(File file, Directory targetDirectory) {
if (!_processManager.canRun('unzip')) { if (!_processManager.canRun('unzip')) {
...@@ -431,35 +436,35 @@ class _MacOSUtils extends _PosixUtils { ...@@ -431,35 +436,35 @@ class _MacOSUtils extends _PosixUtils {
// error in bin/internal/update_dart_sdk.sh // error in bin/internal/update_dart_sdk.sh
throwToolExit('Missing "unzip" tool. Unable to extract ${file.path}.\nConsider running "brew install unzip".'); throwToolExit('Missing "unzip" tool. Unable to extract ${file.path}.\nConsider running "brew install unzip".');
} }
if (_processManager.canRun('rsync')) { final Directory tempDirectory = _fileSystem.systemTempDirectory.createTempSync('flutter_${file.basename}.');
final Directory tempDirectory = _fileSystem.systemTempDirectory.createTempSync('flutter_${file.basename}.'); try {
try { // Unzip to a temporary directory.
// Unzip to a temporary directory.
_processUtils.runSync(
<String>['unzip', '-o', '-q', file.path, '-d', tempDirectory.path],
throwOnError: true,
verboseExceptions: true,
);
for (final FileSystemEntity unzippedFile in tempDirectory.listSync(followLinks: false)) {
// rsync --delete the unzipped files so files removed from the archive are also removed from the target.
// Add the '-8' parameter to avoid mangling filenames with encodings that do not match the current locale.
_processUtils.runSync(
<String>['rsync', '-8', '-av', '--delete', unzippedFile.path, targetDirectory.path],
throwOnError: true,
verboseExceptions: true,
);
}
} finally {
tempDirectory.deleteSync(recursive: true);
}
} else {
// Fall back to just unzipping.
_logger.printTrace('Unable to find rsync, falling back to direct unzipping.');
_processUtils.runSync( _processUtils.runSync(
<String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path], <String>['unzip', '-o', '-q', file.path, '-d', tempDirectory.path],
throwOnError: true, throwOnError: true,
verboseExceptions: true, verboseExceptions: true,
); );
for (final FileSystemEntity unzippedFile in tempDirectory.listSync(followLinks: false)) {
final FileSystemEntityType fileType = targetDirectory.fileSystem.typeSync(
targetDirectory.fileSystem.path.join(targetDirectory.path, unzippedFile.basename),
followLinks: false,
);
final FileSystemEntity fileToReplace;
if (fileType == FileSystemEntityType.directory) {
fileToReplace = targetDirectory.childDirectory(unzippedFile.basename);
} else if (fileType == FileSystemEntityType.link) {
fileToReplace = targetDirectory.childLink(unzippedFile.basename);
} else {
fileToReplace = targetDirectory.childFile(unzippedFile.basename);
}
// Delete existing version before moving.
ErrorHandlingFileSystem.deleteIfExists(fileToReplace, recursive: true);
unzippedFile.renameSync(fileToReplace.path);
}
} on FileSystemException catch (e) {
_logger.printTrace('${e.message}: ${e.osError}');
} finally {
tempDirectory.deleteSync(recursive: true);
} }
} }
} }
......
...@@ -626,29 +626,90 @@ void main() { ...@@ -626,29 +626,90 @@ void main() {
}); });
group('unzip on macOS', () { group('unzip on macOS', () {
testWithoutContext('falls back to unzip when rsync cannot run', () { testWithoutContext('unzip and copy to empty folder', () {
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
fakeProcessManager.excludedExecutables.add('rsync');
final BufferLogger logger = BufferLogger.test();
final OperatingSystemUtils macOSUtils = OperatingSystemUtils( final OperatingSystemUtils macOSUtils = OperatingSystemUtils(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'macos'),
processManager: fakeProcessManager,
);
final Directory targetDirectory = fileSystem.currentDirectory;
final Directory tempDirectory = fileSystem.systemTempDirectory.childDirectory('flutter_foo.zip.rand0');
fakeProcessManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
'unzip',
'-o',
'-q',
'foo.zip',
'-d',
tempDirectory.path,
],
onRun: () {
expect(tempDirectory, exists);
tempDirectory.childDirectory('dirA').childFile('fileA').createSync(recursive: true);
tempDirectory.childDirectory('dirB').childFile('fileB').createSync(recursive: true);
},
),
]);
macOSUtils.unzip(fileSystem.file('foo.zip'), fileSystem.currentDirectory);
expect(fakeProcessManager, hasNoRemainingExpectations);
expect(tempDirectory, isNot(exists));
expect(targetDirectory.childDirectory('dirA').childFile('fileA').existsSync(), isTrue);
expect(targetDirectory.childDirectory('dirB').childFile('fileB').existsSync(), isTrue);
});
testWithoutContext('unzip and copy to preexisting folder', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils macOSUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'macos'), platform: FakePlatform(operatingSystem: 'macos'),
processManager: fakeProcessManager, processManager: fakeProcessManager,
); );
final Directory targetDirectory = fileSystem.currentDirectory; final Directory targetDirectory = fileSystem.currentDirectory;
fakeProcessManager.addCommand(FakeCommand( final File origFileA = targetDirectory.childDirectory('dirA').childFile('fileA');
command: <String>['unzip', '-o', '-q', 'foo.zip', '-d', targetDirectory.path], origFileA.createSync(recursive: true);
)); origFileA.writeAsStringSync('old');
expect(targetDirectory.childDirectory('dirA').childFile('fileA').existsSync(), isTrue);
expect(targetDirectory.childDirectory('dirA').childFile('fileA').readAsStringSync(), 'old');
final Directory tempDirectory = fileSystem.systemTempDirectory.childDirectory('flutter_foo.zip.rand0');
fakeProcessManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
'unzip',
'-o',
'-q',
'foo.zip',
'-d',
tempDirectory.path,
],
onRun: () {
expect(tempDirectory, exists);
final File newFileA = tempDirectory.childDirectory('dirA').childFile('fileA');
newFileA.createSync(recursive: true);
newFileA.writeAsStringSync('new');
tempDirectory.childDirectory('dirB').childFile('fileB').createSync(recursive: true);
},
),
]);
macOSUtils.unzip(fileSystem.file('foo.zip'), targetDirectory); macOSUtils.unzip(fileSystem.file('foo.zip'), fileSystem.currentDirectory);
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
expect(logger.traceText, contains('Unable to find rsync')); expect(tempDirectory, isNot(exists));
expect(targetDirectory.childDirectory('dirA').childFile('fileA').existsSync(), isTrue);
expect(targetDirectory.childDirectory('dirA').childFile('fileA').readAsStringSync(), 'new');
expect(targetDirectory.childDirectory('dirB').childFile('fileB').existsSync(), isTrue);
}); });
testWithoutContext('unzip and rsyncs', () { testWithoutContext('unzip and copy to preexisting folder with type mismatch', () {
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils macOSUtils = OperatingSystemUtils( final OperatingSystemUtils macOSUtils = OperatingSystemUtils(
...@@ -659,6 +720,10 @@ void main() { ...@@ -659,6 +720,10 @@ void main() {
); );
final Directory targetDirectory = fileSystem.currentDirectory; final Directory targetDirectory = fileSystem.currentDirectory;
final Directory origFileA = targetDirectory.childDirectory('dirA').childDirectory('fileA');
origFileA.createSync(recursive: true);
expect(targetDirectory.childDirectory('dirA').childDirectory('fileA').existsSync(), isTrue);
final Directory tempDirectory = fileSystem.systemTempDirectory.childDirectory('flutter_foo.zip.rand0'); final Directory tempDirectory = fileSystem.systemTempDirectory.childDirectory('flutter_foo.zip.rand0');
fakeProcessManager.addCommands(<FakeCommand>[ fakeProcessManager.addCommands(<FakeCommand>[
FakeCommand( FakeCommand(
...@@ -676,27 +741,13 @@ void main() { ...@@ -676,27 +741,13 @@ void main() {
tempDirectory.childDirectory('dirB').childFile('fileB').createSync(recursive: true); tempDirectory.childDirectory('dirB').childFile('fileB').createSync(recursive: true);
}, },
), ),
FakeCommand(command: <String>[
'rsync',
'-8',
'-av',
'--delete',
tempDirectory.childDirectory('dirA').path,
targetDirectory.path,
]),
FakeCommand(command: <String>[
'rsync',
'-8',
'-av',
'--delete',
tempDirectory.childDirectory('dirB').path,
targetDirectory.path,
]),
]); ]);
macOSUtils.unzip(fileSystem.file('foo.zip'), fileSystem.currentDirectory); macOSUtils.unzip(fileSystem.file('foo.zip'), fileSystem.currentDirectory);
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
expect(tempDirectory, isNot(exists)); expect(tempDirectory, isNot(exists));
expect(targetDirectory.childDirectory('dirA').childFile('fileA').existsSync(), isTrue);
expect(targetDirectory.childDirectory('dirB').childFile('fileB').existsSync(), isTrue);
}); });
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment