blob: 904f8db2a4b3776fd9ddcdfcb13fedea05f6b53e [file] [log] [blame]
// Copyright (c) 2012, 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 'dart:io';
import 'package:collection/collection.dart';
import 'package:http/http.dart';
import 'package:pub_semver/pub_semver.dart';
import 'http.dart';
import 'log.dart';
String get flutterReleasesUrl =>
Platform.environment['_PUB_TEST_FLUTTER_RELEASES_URL'] ??
'https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json';
// Retrieves all released versions of Flutter.
Future<List<FlutterRelease>> _flutterReleases = () async {
final response = await retryForHttp(
'fetching available Flutter releases',
() => globalHttpClient.fetch(Request('GET', Uri.parse(flutterReleasesUrl))),
);
final decoded = jsonDecode(response.body);
if (decoded is! Map) throw FormatException('Bad response - should be a Map');
final releases = decoded['releases'];
if (releases is! List) {
throw FormatException('Bad response - releases should be a list.');
}
final result = <FlutterRelease>[];
for (final release in releases) {
final channel = {
'beta': Channel.beta,
'stable': Channel.stable,
'dev': Channel.dev,
}[release['channel']];
if (channel == null) throw FormatException('Release with bad channel');
final dartVersion = release['dart_sdk_version'];
// Some releases don't have an associated dart version, ignore.
if (dartVersion is! String) continue;
final flutterVersion = release['version'];
if (flutterVersion is! String) throw FormatException('Not a string');
result.add(
FlutterRelease(
flutterVersion: Version.parse(flutterVersion),
dartVersion: Version.parse(dartVersion.split(' ').first),
channel: channel,
),
);
}
return result
// Sort releases by channel and version.
.sorted((a, b) {
final compareChannels = b.channel.index - a.channel.index;
if (compareChannels != 0) return compareChannels;
return a.flutterVersion.compareTo(b.flutterVersion);
})
// Newest first.
.reversed
.toList();
}();
/// The "best" Flutter release for a given set of constraints is the first one
/// in [_flutterReleases] that matches both the flutter and dart constraint.
///
/// Returns if no such release could be found.
Future<FlutterRelease?> inferBestFlutterRelease(
Map<String, VersionConstraint> sdkConstraints,
) async {
final List<FlutterRelease> flutterReleases;
try {
flutterReleases = await _flutterReleases;
} on Exception catch (e) {
fine('Failed retrieving the list of flutter-releases: $e');
return null;
}
return flutterReleases.firstWhereOrNull(
(release) =>
(sdkConstraints['flutter'] ?? VersionConstraint.any)
.allows(release.flutterVersion) &&
(sdkConstraints['dart'] ?? VersionConstraint.any)
.allows(release.dartVersion),
);
}
enum Channel {
stable,
beta,
dev,
}
/// A version of the Flutter SDK and its related Dart SDK.
class FlutterRelease {
final Version flutterVersion;
final Version dartVersion;
final Channel channel;
FlutterRelease({
required this.flutterVersion,
required this.dartVersion,
required this.channel,
});
@override
String toString() =>
'FlutterRelease(flutter=$flutterVersion, dart=$dartVersion, channel=$channel)';
}