| // 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 'package:collection/collection.dart'; | |
| import 'package:http/http.dart'; | |
| import 'package:pub_semver/pub_semver.dart'; | |
| import 'http.dart'; | |
| import 'log.dart'; | |
| import 'platform_info.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 const FormatException('Bad response - should be a Map'); | |
| } | |
| final releases = decoded['releases']; | |
| if (releases is! List) { | |
| throw const FormatException('Bad response - releases should be a list.'); | |
| } | |
| final result = <FlutterRelease>[]; | |
| for (final release in releases) { | |
| if (release is! Map) { | |
| throw const FormatException( | |
| 'Bad response - releases should be a list of maps.', | |
| ); | |
| } | |
| final channel = | |
| { | |
| 'beta': Channel.beta, | |
| 'stable': Channel.stable, | |
| 'dev': Channel.dev, | |
| }[release['channel']]; | |
| if (channel == null) { | |
| throw const 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 const 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)'; | |
| } |