chore(shorebird_cli): remove aot_tools exe artifact now that it is no longer needed (#2334)

This commit is contained in:
Bryan Oltman
2024-07-10 14:39:35 -04:00
committed by GitHub
parent 4a8f3279fd
commit 567068d344
3 changed files with 28 additions and 189 deletions
+27 -82
View File
@@ -49,8 +49,7 @@ class Cache {
Cache() {
registerArtifact(PatchArtifact(cache: this, platform: platform));
registerArtifact(BundleToolArtifact(cache: this, platform: platform));
registerArtifact(AotToolsDillArtifact(cache: this, platform: platform));
registerArtifact(AotToolsExeArtifact(cache: this, platform: platform));
registerArtifact(AotToolsArtifact(cache: this, platform: platform));
}
void registerArtifact(CachedArtifact artifact) => _artifacts.add(artifact);
@@ -66,7 +65,7 @@ class Cache {
maxAttempts: 3,
onRetry: (e) {
logger
..detail('Failed to update ${artifact.name}, retrying...')
..detail('Failed to update ${artifact.fileName}, retrying...')
..detail(e.toString());
},
);
@@ -131,7 +130,7 @@ abstract class CachedArtifact {
final Platform platform;
/// The on-disk name of the artifact.
String get name;
String get fileName;
/// Should the artifact be marked executable.
bool get isExecutable;
@@ -139,10 +138,6 @@ abstract class CachedArtifact {
/// The URL from which the artifact can be downloaded.
String get storageUrl;
/// Whether the artifact is required for Shorebird to function.
/// If we fail to fetch it we will exit with an error.
bool get required => true;
/// The SHA256 checksum of the artifact binary.
///
/// When null, the checksum is not verified and the downloaded artifact
@@ -150,13 +145,15 @@ abstract class CachedArtifact {
String? get checksum;
Future<void> extractArtifact(http.ByteStream stream, String outputPath) {
final file = File(p.join(outputPath, name))..createSync(recursive: true);
final file = File(p.join(outputPath, fileName))
..createSync(recursive: true);
return stream.pipe(file.openWrite());
}
Directory get location => cache.getArtifactDirectory(name);
File get file =>
File(p.join(cache.getArtifactDirectory(fileName).path, fileName));
Future<bool> isUpToDate() async => location.existsSync();
Future<bool> isUpToDate() async => file.existsSync();
Future<void> update() async {
final request = http.Request('GET', Uri.parse(storageUrl));
@@ -166,41 +163,33 @@ abstract class CachedArtifact {
} catch (error) {
throw CacheUpdateFailure(
'''
Failed to download $name: $error
Failed to download $fileName: $error
If you're behind a firewall/proxy, please, make sure shorebird_cli is
allowed to access $storageUrl.''',
);
}
if (response.statusCode != HttpStatus.ok) {
if (!required && response.statusCode == HttpStatus.notFound) {
logger.detail(
'[cache] optional artifact: "$name" was not found, skipping...',
);
return;
}
throw CacheUpdateFailure(
'''Failed to download $name: ${response.statusCode} ${response.reasonPhrase}''',
'''Failed to download $fileName: ${response.statusCode} ${response.reasonPhrase}''',
);
}
await extractArtifact(response.stream, location.path);
final artifactDirectory = Directory(p.dirname(file.path));
await extractArtifact(response.stream, artifactDirectory.path);
final expectedChecksum = checksum;
if (expectedChecksum != null) {
final artifactFile = File(p.join(location.path, name));
if (!checksumChecker.checkFile(artifactFile, expectedChecksum)) {
if (!checksumChecker.checkFile(file, expectedChecksum)) {
// Delete the location, so if the download is retried, it will be
// re-downloaded.
location.deleteSync(recursive: true);
artifactDirectory.deleteSync(recursive: true);
throw CacheUpdateFailure(
'''Failed to download $name: checksum mismatch''',
'''Failed to download $fileName: checksum mismatch''',
);
} else {
logger.detail(
'No checksum provided for $name, skipping file corruption validation',
'No checksum provided for $fileName, skipping file corruption validation',
);
}
}
@@ -208,78 +197,34 @@ allowed to access $storageUrl.''',
if (!platform.isWindows && isExecutable) {
final result = await process.start(
'chmod',
['+x', p.join(location.path, name)],
['+x', file.path],
);
await result.exitCode;
}
}
}
class AotToolsDillArtifact extends CachedArtifact {
AotToolsDillArtifact({required super.cache, required super.platform});
class AotToolsArtifact extends CachedArtifact {
AotToolsArtifact({required super.cache, required super.platform});
@override
String get name => 'aot-tools.dill';
String get fileName => 'aot-tools.dill';
@override
bool get isExecutable => false;
/// The aot-tools are only available for revisions that support mixed-mode.
@override
bool get required => false;
@override
Directory get location => Directory(
File get file => File(
p.join(
cache.getArtifactDirectory(name).path,
cache.getArtifactDirectory(fileName).path,
shorebirdEnv.shorebirdEngineRevision,
fileName,
),
);
@override
String get storageUrl =>
'${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${shorebirdEnv.shorebirdEngineRevision}/$name';
@override
String? get checksum => null;
}
/// For a few revisions in Dec 2023, we distributed aot-tools as an executable.
/// Should be removed sometime after June 2024.
class AotToolsExeArtifact extends CachedArtifact {
AotToolsExeArtifact({required super.cache, required super.platform});
@override
String get name => 'aot-tools';
@override
bool get isExecutable => true;
/// The aot-tools are only available for revisions that support mixed-mode.
@override
bool get required => false;
@override
Directory get location => Directory(
p.join(
cache.getArtifactDirectory(name).path,
shorebirdEnv.shorebirdEngineRevision,
),
);
@override
String get storageUrl {
var artifactName = 'aot-tools-';
if (platform.isMacOS) {
artifactName += 'darwin-x64';
} else if (platform.isLinux) {
artifactName += 'linux-x64';
} else if (platform.isWindows) {
artifactName += 'windows-x64';
}
return '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${shorebirdEnv.shorebirdEngineRevision}/$artifactName';
}
'${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${shorebirdEnv.shorebirdEngineRevision}/$fileName';
@override
String? get checksum => null;
@@ -289,7 +234,7 @@ class PatchArtifact extends CachedArtifact {
PatchArtifact({required super.cache, required super.platform});
@override
String get name => 'patch';
String get fileName => 'patch';
@override
bool get isExecutable => true;
@@ -300,7 +245,7 @@ class PatchArtifact extends CachedArtifact {
String outputPath,
) async {
final tempDir = Directory.systemTemp.createTempSync();
final artifactPath = p.join(tempDir.path, '$name.zip');
final artifactPath = p.join(tempDir.path, '$fileName.zip');
await stream.pipe(File(artifactPath).openWrite());
await artifactManager.extractZip(
zipFile: File(artifactPath),
@@ -330,7 +275,7 @@ class BundleToolArtifact extends CachedArtifact {
BundleToolArtifact({required super.cache, required super.platform});
@override
String get name => 'bundletool.jar';
String get fileName => 'bundletool.jar';
@override
bool get isExecutable => false;
@@ -237,41 +237,6 @@ void main() {
);
});
test('skips optional artifacts if a 404 is returned', () async {
when(() => httpClient.send(any())).thenAnswer(
(invocation) async {
final request =
invocation.positionalArguments.first as http.BaseRequest;
final fileName = p.basename(request.url.path);
if (fileName.startsWith('aot-tools')) {
return http.StreamedResponse(
const Stream.empty(),
HttpStatus.notFound,
reasonPhrase: 'Not Found',
);
}
return http.StreamedResponse(
Stream.value(ZipEncoder().encode(Archive())!),
HttpStatus.ok,
);
},
);
await expectLater(
runWithOverrides(cache.updateAll),
completes,
);
verify(
() => logger.detail(
'''[cache] optional artifact: "aot-tools.dill" was not found, skipping...''',
),
).called(1);
verify(
() => logger.detail(
'''[cache] optional artifact: "aot-tools" was not found, skipping...''',
),
).called(1);
});
test('downloads correct artifacts', () async {
final patchArtifactDirectory = runWithOverrides(
() => cache.getArtifactDirectory('patch'),
@@ -325,77 +290,6 @@ void main() {
expect(requests, equals(expected));
});
test('aot-tools falls back to executable', () async {
setMockPlatform(Platform.macOS);
when(() => httpClient.send(any())).thenAnswer(
(invocation) async {
final request =
invocation.positionalArguments.first as http.BaseRequest;
final fileName = p.basename(request.url.path);
if (fileName == 'aot-tools.dill') {
return http.StreamedResponse(
const Stream.empty(),
HttpStatus.notFound,
reasonPhrase: 'Not Found',
);
}
return http.StreamedResponse(
Stream.value(ZipEncoder().encode(Archive())!),
HttpStatus.ok,
);
},
);
await expectLater(runWithOverrides(cache.updateAll), completes);
final requests = verify(() => httpClient.send(captureAny()))
.captured
.cast<http.BaseRequest>()
.map((r) => r.url)
.toList();
String perEngine(String name) =>
'${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/$name';
final expected = [
perEngine('patch-darwin-x64.zip'),
'https://github.com/google/bundletool/releases/download/1.15.6/bundletool-all-1.15.6.jar',
// Requests the .dill, fails and falls back to executable:
perEngine('aot-tools.dill'),
perEngine('aot-tools-darwin-x64'),
].map(Uri.parse).toList();
expect(requests, equals(expected));
});
test('aot-tools executable paths by platform', () async {
setMockPlatform(Platform.windows);
expect(
runWithOverrides(
() => AotToolsExeArtifact(cache: cache, platform: platform)
.storageUrl,
),
endsWith('aot-tools-windows-x64'),
);
setMockPlatform(Platform.linux);
expect(
runWithOverrides(
() => AotToolsExeArtifact(cache: cache, platform: platform)
.storageUrl,
),
endsWith('aot-tools-linux-x64'),
);
setMockPlatform(Platform.macOS);
expect(
runWithOverrides(
() => AotToolsExeArtifact(cache: cache, platform: platform)
.storageUrl,
),
endsWith('aot-tools-darwin-x64'),
);
});
test('pull correct artifact for Windows', () async {
setMockPlatform(Platform.windows);
@@ -1686,7 +1686,7 @@ void main() {
id: 0,
number: 1,
channel: 'stable',
artifacts: [],
artifacts: const [],
);
response = GetReleasePatchesResponse(patches: [patch]);
when(() => httpClient.send(any())).thenAnswer(