blob: 880c1317e28922a9bcf47f9822100df97fb08f3d [file] [edit]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. 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:convert';
import 'package:collection/collection.dart';
import 'package:file/file.dart';
import 'package:pub_semver/pub_semver.dart';
import 'http.dart';
Map<String, DartSdkVersion> versionsFromFile(
FileSystem fileSystem,
HttpRead read,
) {
var json = jsonDecode(fileSystem.file('versions.json').readAsStringSync());
return versionsFromJson(json, read);
}
Map<String, DartSdkVersion> versionsFromJson(
Map<String, dynamic> json,
HttpRead read,
) {
var stable = DartSdkVersion.fromJson('stable', json['stable']!, read);
var beta = DartSdkVersion.fromJson('beta', json['beta']!, read);
return {'stable': stable, 'beta': beta};
}
void writeVersionsFile(FileSystem fileSystem, List<DartSdkVersion> versions) {
fileSystem
.file('versions.json')
.writeAsStringSync(
JsonEncoder.withIndent(' ').convert({
for (var version in versions)
version.channel: {
'version': version.version.toString(),
'sha256': version.sha256,
},
}),
);
}
class DartSdkVersion {
static final baseUri = Uri.https(
'storage.googleapis.com',
'dart-archive/channels/',
);
final String channel;
Version version;
Map<String, String> sha256;
final HttpRead _read;
DartSdkVersion(this.channel, this.version, this.sha256, this._read);
factory DartSdkVersion.fromJson(
String channel,
Map<String, dynamic> json,
HttpRead read,
) {
var version = Version.parse(json['version']!);
var sha256 = (json['sha256']! as Map).cast<String, String>();
return DartSdkVersion(channel, version, sha256, read);
}
Iterable<String> get tags {
if (channel == 'stable') {
var major = version.major;
var minor = version.minor;
return [
'$version-sdk',
'$major.$minor-sdk',
'$major-sdk',
'stable-sdk',
'sdk',
'$version',
'$major.$minor',
'$major',
'stable',
'latest',
];
}
if (channel == 'beta') {
return ['$version-sdk', 'beta-sdk', '$version', 'beta'];
}
throw StateError("Unsupported channel '$channel'");
}
@override
String toString() {
return '$version $channel $sha256';
}
Future<void> verify() async {
var remoteSha256 = await _readSha256(version);
if (!MapEquality().equals(sha256, remoteSha256)) {
throw StateError("Expected SHA256 '$sha256' but got '$remoteSha256'");
}
}
Future<bool> update() async {
var latest = await _readLatestVersion();
if (version == latest) {
return false;
}
sha256 = await _readSha256(latest);
version = latest;
return true;
}
Future<Map<String, String>> _readSha256(Version version) async {
var sha256 = <String, String>{};
for (var arch in ['x64', 'arm', 'arm64', 'riscv64']) {
var sdk = 'dartsdk-linux-$arch-release.zip';
var sha256Url = baseUri.resolve(
'$channel/release/$version/sdk/$sdk.sha256sum',
);
var sha256sum = await _read(sha256Url);
if (!sha256sum.trim().endsWith(sdk)) {
throw StateError("Expected file name $sdk in sha256sum:\n$sha256sum");
}
sha256[arch] = sha256sum.split(' ').first;
}
return sha256;
}
Future<Version> _readLatestVersion() async {
var versionUrl = baseUri.resolve('$channel/release/latest/VERSION');
var versionJson = jsonDecode(await _read(versionUrl));
return Version.parse(versionJson['version']);
}
bool operator ==(Object other) {
return other is DartSdkVersion &&
other.channel == channel &&
other.version == version &&
MapEquality().equals(other.sha256, sha256);
}
@override
int get hashCode {
return channel.hashCode ^ version.hashCode ^ MapEquality().hash(sha256);
}
}