Unverified Commit bbe18f75 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

use Expand-Archive and Compress-Archive in windows os utils (#58390)

Work towards removal of package:archive and ideally more stable unzipping of artifacts. These commands are available in Powershell 5+, which we already require for windows.
parent eae77804
...@@ -291,7 +291,15 @@ class _WindowsUtils extends OperatingSystemUtils { ...@@ -291,7 +291,15 @@ class _WindowsUtils extends OperatingSystemUtils {
logger: logger, logger: logger,
platform: platform, platform: platform,
processManager: processManager, processManager: processManager,
); ) {
if (processManager.canRun('pwsh.exe')) {
_activePowershell = 'pwsh.exe';
} else {
_activePowershell = 'PowerShell.exe';
}
}
String _activePowershell;
@override @override
void makeExecutable(File file) {} void makeExecutable(File file) {}
...@@ -315,36 +323,30 @@ class _WindowsUtils extends OperatingSystemUtils { ...@@ -315,36 +323,30 @@ class _WindowsUtils extends OperatingSystemUtils {
@override @override
void zip(Directory data, File zipFile) { void zip(Directory data, File zipFile) {
final Archive archive = Archive(); final RunResult result = _processUtils.runSync(<String>[
for (final FileSystemEntity entity in data.listSync(recursive: true)) { _activePowershell,
if (entity is! File) { '-command',
continue; '"Compress-Archive ${data.path} -DestinationPath ${zipFile.path}"',
} ]);
final File file = entity as File; if (result.stderr.isNotEmpty) {
final String path = file.fileSystem.path.relative(file.path, from: data.path); throw ProcessException(_activePowershell, <String>['Compress-Archive'], result.stderr);
final List<int> bytes = file.readAsBytesSync();
archive.addFile(ArchiveFile(path, bytes.length, bytes));
} }
zipFile.writeAsBytesSync(ZipEncoder().encode(archive), flush: true);
} }
@override @override
void unzip(File file, Directory targetDirectory) { void unzip(File file, Directory targetDirectory) {
final Archive archive = ZipDecoder().decodeBytes(file.readAsBytesSync()); final RunResult result = _processUtils.runSync(<String>[
_unpackArchive(archive, targetDirectory); _activePowershell,
'-command',
'"Expand-Archive ${file.path} -DestinationPath ${targetDirectory.path}"',
], throwOnError: true);
if (result.stderr.isNotEmpty) {
throw ProcessException(_activePowershell, <String>['Expand-Archive'], result.stderr);
}
} }
@override @override
bool verifyZip(File zipFile) { bool verifyZip(File zipFile) => true;
try {
ZipDecoder().decodeBytes(zipFile.readAsBytesSync(), verify: true);
} on FileSystemException catch (_) {
return false;
} on ArchiveException catch (_) {
return false;
}
return true;
}
@override @override
void unpack(File gzippedTarFile, Directory targetDirectory) { void unpack(File gzippedTarFile, Directory targetDirectory) {
......
...@@ -21,6 +21,17 @@ const String kExecutable = 'foo'; ...@@ -21,6 +21,17 @@ const String kExecutable = 'foo';
const String kPath1 = '/bar/bin/$kExecutable'; const String kPath1 = '/bar/bin/$kExecutable';
const String kPath2 = '/another/bin/$kExecutable'; const String kPath2 = '/another/bin/$kExecutable';
const String kPowershellException = r'''
New-Object : Exception calling ".ctor" with "3" argument(s): "End of Central Directory record could not be found."
At
C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Archive\Microsoft.PowerShell.Archive.psm1:934
char:23
+ ... ipArchive = New-Object -TypeName System.IO.Compression.ZipArchive -Ar ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
''';
void main() { void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
...@@ -67,6 +78,7 @@ void main() { ...@@ -67,6 +78,7 @@ void main() {
testWithoutContext('returns null when executable does not exist', () async { testWithoutContext('returns null when executable does not exist', () async {
when(mockProcessManager.runSync(<String>['where', kExecutable])) when(mockProcessManager.runSync(<String>['where', kExecutable]))
.thenReturn(ProcessResult(0, 1, null, null)); .thenReturn(ProcessResult(0, 1, null, null));
when(mockProcessManager.canRun('pwsh.exe')).thenReturn(true);
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows')); final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
expect(utils.which(kExecutable), isNull); expect(utils.which(kExecutable), isNull);
}); });
...@@ -74,6 +86,7 @@ void main() { ...@@ -74,6 +86,7 @@ void main() {
testWithoutContext('returns exactly one result', () async { testWithoutContext('returns exactly one result', () async {
when(mockProcessManager.runSync(<String>['where', 'foo'])) when(mockProcessManager.runSync(<String>['where', 'foo']))
.thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null)); .thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null));
when(mockProcessManager.canRun('pwsh.exe')).thenReturn(true);
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows')); final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
expect(utils.which(kExecutable).path, kPath1); expect(utils.which(kExecutable).path, kPath1);
}); });
...@@ -81,6 +94,7 @@ void main() { ...@@ -81,6 +94,7 @@ void main() {
testWithoutContext('returns all results for whichAll', () async { testWithoutContext('returns all results for whichAll', () async {
when(mockProcessManager.runSync(<String>['where', kExecutable])) when(mockProcessManager.runSync(<String>['where', kExecutable]))
.thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null)); .thenReturn(ProcessResult(0, 0, '$kPath1\n$kPath2', null));
when(mockProcessManager.canRun('pwsh.exe')).thenReturn(true);
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows')); final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
final List<File> result = utils.whichAll(kExecutable); final List<File> result = utils.whichAll(kExecutable);
expect(result, hasLength(2)); expect(result, hasLength(2));
...@@ -97,6 +111,7 @@ void main() { ...@@ -97,6 +111,7 @@ void main() {
when(mockFile.readAsBytesSync()).thenThrow( when(mockFile.readAsBytesSync()).thenThrow(
const FileSystemException('error'), const FileSystemException('error'),
); );
when(mockProcessManager.canRun('pwsh.exe')).thenReturn(true);
final OperatingSystemUtils osUtils = OperatingSystemUtils( final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
...@@ -116,6 +131,7 @@ void main() { ...@@ -116,6 +131,7 @@ void main() {
0x01, 0x01,
0x02, 0x02,
])); ]));
when(mockProcessManager.canRun('pwsh.exe')).thenReturn(true);
final OperatingSystemUtils osUtils = OperatingSystemUtils( final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
...@@ -131,6 +147,7 @@ void main() { ...@@ -131,6 +147,7 @@ void main() {
final MockFile mockFile = MockFile(); final MockFile mockFile = MockFile();
when(fileSystem.file(any)).thenReturn(mockFile); when(fileSystem.file(any)).thenReturn(mockFile);
when(mockFile.readAsBytesSync()).thenReturn(Uint8List(0)); when(mockFile.readAsBytesSync()).thenReturn(Uint8List(0));
when(mockProcessManager.canRun('pwsh.exe')).thenReturn(true);
final OperatingSystemUtils osUtils = OperatingSystemUtils( final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
...@@ -142,6 +159,116 @@ void main() { ...@@ -142,6 +159,116 @@ void main() {
}); });
}); });
testWithoutContext('Windows PowerShell Expand-Archive', () async {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'pwsh.exe',
'-command',
'"Expand-Archive a -DestinationPath b"',
],
),
]);
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'windows'),
processManager: processManager,
);
osUtils.unzip(fileSystem.file('a'), fileSystem.directory('b'));
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('Windows PowerShell Expand-Archive with stderr', () async {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'pwsh.exe',
'-command',
'"Expand-Archive a -DestinationPath b"',
],
stderr: kPowershellException,
),
]);
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'windows'),
processManager: processManager,
);
expect(() => osUtils.unzip(fileSystem.file('a'), fileSystem.directory('b')),
throwsA(isA<ProcessException>()));
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('Windows PowerShell Compress-Archive', () {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'pwsh.exe',
'-command',
'"Compress-Archive b -DestinationPath a"',
],
),
]);
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'windows'),
processManager: processManager,
);
osUtils.zip(fileSystem.directory('b'), fileSystem.file('a'));
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('Windows PowerShell Compress-Archive with stderr', () {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'pwsh.exe',
'-command',
'"Compress-Archive b -DestinationPath a"',
],
stderr: kPowershellException,
),
]);
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'windows'),
processManager: processManager,
);
expect(() => osUtils.zip(fileSystem.directory('b'), fileSystem.file('a')),
throwsA(isA<ProcessException>()));
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('Windows PowerShell verifyZip is a no-op', () {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
final OperatingSystemUtils osUtils = OperatingSystemUtils(
fileSystem: fileSystem,
logger: BufferLogger.test(),
platform: FakePlatform(operatingSystem: 'windows'),
processManager: processManager,
);
expect(osUtils.verifyZip(fileSystem.file('a')), true);
expect(processManager.hasRemainingExpectations, false);
});
testWithoutContext('stream compression level', () { testWithoutContext('stream compression level', () {
expect(OperatingSystemUtils.gzipLevel1.level, equals(1)); expect(OperatingSystemUtils.gzipLevel1.level, equals(1));
}); });
......
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