mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tools] cache flutter sdk version to disk (#124558)
Fixes https://github.com/flutter/flutter/issues/112833 Most of the actual changes here are in [packages/flutter_tools/lib/src/version.dart](https://github.com/flutter/flutter/pull/124558/files#diff-092e00109d9e1589fbc7c6de750e29a6ae512b2dd44e85d60028953561201605), while the rest is largely just addressing changes to the constructor of `FlutterVersion` which now has different dependencies. This change makes `FlutterVersion` an interface with two concrete implementations: 1. `_FlutterVersionGit` which is mostly the previous implementation, and 2. `_FlutterVersionFromFile` which will read a new `.version.json` file from the root of the repo The [`FlutterVersion` constructor](https://github.com/flutter/flutter/pull/124558/files#diff-092e00109d9e1589fbc7c6de750e29a6ae512b2dd44e85d60028953561201605R70) is now a factory that first checks if `.version.json` exists, and if so returns an instance of `_FlutterVersionFromGit` else it returns the fallback `_FlutterVersionGit` which will end up writing `.version.json` so that we don't need to re-calculate the version on the next invocation. `.version.json` will be deleted in the bash/batch entrypoints any time we need to rebuild he tool (this will usually be because the user did `flutter upgrade` or `flutter channel`, or manually changed the commit with git).
This commit is contained in:
parent
6d2b5ea30e
commit
3246808cd2
@ -128,6 +128,7 @@ GOTO :after_subroutine
|
|||||||
|
|
||||||
:do_snapshot
|
:do_snapshot
|
||||||
IF EXIST "%FLUTTER_ROOT%\version" DEL "%FLUTTER_ROOT%\version"
|
IF EXIST "%FLUTTER_ROOT%\version" DEL "%FLUTTER_ROOT%\version"
|
||||||
|
IF EXIST "%FLUTTER_ROOT%\bin\cache\flutter.version.json" DEL "%FLUTTER_ROOT%\bin\cache\flutter.version.json"
|
||||||
ECHO: > "%cache_dir%\.dartignore"
|
ECHO: > "%cache_dir%\.dartignore"
|
||||||
ECHO Building flutter tool... 1>&2
|
ECHO Building flutter tool... 1>&2
|
||||||
PUSHD "%flutter_tools_dir%"
|
PUSHD "%flutter_tools_dir%"
|
||||||
|
@ -123,7 +123,10 @@ function upgrade_flutter () (
|
|||||||
# * STAMP_PATH is an empty file, or
|
# * STAMP_PATH is an empty file, or
|
||||||
# * Contents of STAMP_PATH is not what we are going to compile, or
|
# * Contents of STAMP_PATH is not what we are going to compile, or
|
||||||
# * pubspec.yaml last modified after pubspec.lock
|
# * pubspec.yaml last modified after pubspec.lock
|
||||||
if [[ ! -f "$SNAPSHOT_PATH" || ! -s "$STAMP_PATH" || "$(cat "$STAMP_PATH")" != "$compilekey" || "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]]; then
|
if [[ ! -f "$SNAPSHOT_PATH" || \
|
||||||
|
! -s "$STAMP_PATH" || \
|
||||||
|
"$(cat "$STAMP_PATH")" != "$compilekey" || \
|
||||||
|
"$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]]; then
|
||||||
# Waits for the update lock to be acquired. Placing this check inside the
|
# Waits for the update lock to be acquired. Placing this check inside the
|
||||||
# conditional allows the majority of flutter/dart installations to bypass
|
# conditional allows the majority of flutter/dart installations to bypass
|
||||||
# the lock entirely, but as a result this required a second verification that
|
# the lock entirely, but as a result this required a second verification that
|
||||||
@ -137,6 +140,7 @@ function upgrade_flutter () (
|
|||||||
|
|
||||||
# Fetch Dart...
|
# Fetch Dart...
|
||||||
rm -f "$FLUTTER_ROOT/version"
|
rm -f "$FLUTTER_ROOT/version"
|
||||||
|
rm -f "$FLUTTER_ROOT/bin/cache/flutter.version.json"
|
||||||
touch "$FLUTTER_ROOT/bin/cache/.dartignore"
|
touch "$FLUTTER_ROOT/bin/cache/.dartignore"
|
||||||
"$FLUTTER_ROOT/bin/internal/update_dart_sdk.sh"
|
"$FLUTTER_ROOT/bin/internal/update_dart_sdk.sh"
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ class DoctorCommand extends FlutterCommand {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<FlutterCommandResult> runCommand() async {
|
Future<FlutterCommandResult> runCommand() async {
|
||||||
globals.flutterVersion.fetchTagsAndUpdate();
|
|
||||||
if (argResults?.wasParsed('check-for-remote-artifacts') ?? false) {
|
if (argResults?.wasParsed('check-for-remote-artifacts') ?? false) {
|
||||||
final String engineRevision = stringArg('check-for-remote-artifacts')!;
|
final String engineRevision = stringArg('check-for-remote-artifacts')!;
|
||||||
if (engineRevision.startsWith(RegExp(r'[a-f0-9]{1,40}'))) {
|
if (engineRevision.startsWith(RegExp(r'[a-f0-9]{1,40}'))) {
|
||||||
|
@ -92,7 +92,10 @@ class DowngradeCommand extends FlutterCommand {
|
|||||||
String workingDirectory = Cache.flutterRoot!;
|
String workingDirectory = Cache.flutterRoot!;
|
||||||
if (argResults!.wasParsed('working-directory')) {
|
if (argResults!.wasParsed('working-directory')) {
|
||||||
workingDirectory = stringArg('working-directory')!;
|
workingDirectory = stringArg('working-directory')!;
|
||||||
_flutterVersion = FlutterVersion(workingDirectory: workingDirectory);
|
_flutterVersion = FlutterVersion(
|
||||||
|
fs: _fileSystem!,
|
||||||
|
flutterRoot: workingDirectory,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String currentChannel = _flutterVersion!.channel;
|
final String currentChannel = _flutterVersion!.channel;
|
||||||
|
@ -80,7 +80,7 @@ class UpgradeCommand extends FlutterCommand {
|
|||||||
gitTagVersion: GitTagVersion.determine(globals.processUtils, globals.platform),
|
gitTagVersion: GitTagVersion.determine(globals.processUtils, globals.platform),
|
||||||
flutterVersion: stringArg('working-directory') == null
|
flutterVersion: stringArg('working-directory') == null
|
||||||
? globals.flutterVersion
|
? globals.flutterVersion
|
||||||
: FlutterVersion(workingDirectory: _commandRunner.workingDirectory),
|
: FlutterVersion(flutterRoot: _commandRunner.workingDirectory!, fs: globals.fs),
|
||||||
verifyOnly: boolArg('verify-only'),
|
verifyOnly: boolArg('verify-only'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -297,7 +297,11 @@ class UpgradeCommandRunner {
|
|||||||
'for instructions.'
|
'for instructions.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return FlutterVersion(workingDirectory: workingDirectory, frameworkRevision: revision);
|
return FlutterVersion.fromRevision(
|
||||||
|
flutterRoot: workingDirectory!,
|
||||||
|
frameworkRevision: revision,
|
||||||
|
fs: globals.fs,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts a hard reset to the given revision.
|
/// Attempts a hard reset to the given revision.
|
||||||
|
@ -226,7 +226,10 @@ Future<T> runInContext<T>(
|
|||||||
config: globals.config,
|
config: globals.config,
|
||||||
platform: globals.platform,
|
platform: globals.platform,
|
||||||
),
|
),
|
||||||
FlutterVersion: () => FlutterVersion(),
|
FlutterVersion: () => FlutterVersion(
|
||||||
|
fs: globals.fs,
|
||||||
|
flutterRoot: Cache.flutterRoot!,
|
||||||
|
),
|
||||||
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
|
FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
|
||||||
FuchsiaDeviceTools: () => FuchsiaDeviceTools(),
|
FuchsiaDeviceTools: () => FuchsiaDeviceTools(),
|
||||||
FuchsiaSdk: () => FuchsiaSdk(),
|
FuchsiaSdk: () => FuchsiaSdk(),
|
||||||
|
@ -123,7 +123,7 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
|
|||||||
FlutterValidator(
|
FlutterValidator(
|
||||||
fileSystem: globals.fs,
|
fileSystem: globals.fs,
|
||||||
platform: globals.platform,
|
platform: globals.platform,
|
||||||
flutterVersion: () => globals.flutterVersion,
|
flutterVersion: () => globals.flutterVersion.fetchTagsAndGetVersion(clock: globals.systemClock),
|
||||||
devToolsVersion: () => globals.cache.devToolsVersion,
|
devToolsVersion: () => globals.cache.devToolsVersion,
|
||||||
processManager: globals.processManager,
|
processManager: globals.processManager,
|
||||||
userMessages: userMessages,
|
userMessages: userMessages,
|
||||||
|
@ -136,7 +136,10 @@ class VariableDumpMachineProjectValidator extends MachineProjectValidator {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// FlutterVersion
|
// FlutterVersion
|
||||||
final FlutterVersion version = FlutterVersion(workingDirectory: project.directory.absolute.path);
|
final FlutterVersion version = FlutterVersion(
|
||||||
|
flutterRoot: Cache.flutterRoot!,
|
||||||
|
fs: fileSystem,
|
||||||
|
);
|
||||||
result.add(ProjectValidatorResult(
|
result.add(ProjectValidatorResult(
|
||||||
name: 'FlutterVersion.frameworkRevision',
|
name: 'FlutterVersion.frameworkRevision',
|
||||||
value: _toJsonValue(version.frameworkRevision),
|
value: _toJsonValue(version.frameworkRevision),
|
||||||
|
@ -18,6 +18,7 @@ import '../cache.dart';
|
|||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
import '../globals.dart' as globals;
|
import '../globals.dart' as globals;
|
||||||
import '../tester/flutter_tester.dart';
|
import '../tester/flutter_tester.dart';
|
||||||
|
import '../version.dart';
|
||||||
import '../web/web_device.dart';
|
import '../web/web_device.dart';
|
||||||
|
|
||||||
/// Common flutter command line options.
|
/// Common flutter command line options.
|
||||||
@ -318,14 +319,16 @@ class FlutterCommandRunner extends CommandRunner<void> {
|
|||||||
|
|
||||||
if ((topLevelResults[FlutterGlobalOptions.kVersionFlag] as bool?) ?? false) {
|
if ((topLevelResults[FlutterGlobalOptions.kVersionFlag] as bool?) ?? false) {
|
||||||
globals.flutterUsage.sendCommand(FlutterGlobalOptions.kVersionFlag);
|
globals.flutterUsage.sendCommand(FlutterGlobalOptions.kVersionFlag);
|
||||||
globals.flutterVersion.fetchTagsAndUpdate();
|
final FlutterVersion version = globals.flutterVersion.fetchTagsAndGetVersion(
|
||||||
String status;
|
clock: globals.systemClock,
|
||||||
|
);
|
||||||
|
final String status;
|
||||||
if (machineFlag) {
|
if (machineFlag) {
|
||||||
final Map<String, Object> jsonOut = globals.flutterVersion.toJson();
|
final Map<String, Object> jsonOut = version.toJson();
|
||||||
jsonOut['flutterRoot'] = Cache.flutterRoot!;
|
jsonOut['flutterRoot'] = Cache.flutterRoot!;
|
||||||
status = const JsonEncoder.withIndent(' ').convert(jsonOut);
|
status = const JsonEncoder.withIndent(' ').convert(jsonOut);
|
||||||
} else {
|
} else {
|
||||||
status = globals.flutterVersion.toString();
|
status = version.toString();
|
||||||
}
|
}
|
||||||
globals.printStatus(status);
|
globals.printStatus(status);
|
||||||
return;
|
return;
|
||||||
|
@ -79,103 +79,152 @@ Channel? getChannelForName(String name) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlutterVersion {
|
abstract class FlutterVersion {
|
||||||
/// Parses the Flutter version from currently available tags in the local
|
/// Parses the Flutter version from currently available tags in the local
|
||||||
/// repo.
|
/// repo.
|
||||||
///
|
factory FlutterVersion({
|
||||||
/// Call [fetchTagsAndUpdate] to update the version based on the latest tags
|
|
||||||
/// available upstream.
|
|
||||||
FlutterVersion({
|
|
||||||
SystemClock clock = const SystemClock(),
|
SystemClock clock = const SystemClock(),
|
||||||
String? workingDirectory,
|
required FileSystem fs,
|
||||||
String? frameworkRevision,
|
required String flutterRoot,
|
||||||
}) : _clock = clock,
|
@protected
|
||||||
_workingDirectory = workingDirectory {
|
bool fetchTags = false,
|
||||||
_frameworkRevision = frameworkRevision ?? _runGit(
|
}) {
|
||||||
|
final File versionFile = getVersionFile(fs, flutterRoot);
|
||||||
|
|
||||||
|
if (!fetchTags && versionFile.existsSync()) {
|
||||||
|
final _FlutterVersionFromFile? version = _FlutterVersionFromFile.tryParseFromFile(
|
||||||
|
versionFile,
|
||||||
|
flutterRoot: flutterRoot,
|
||||||
|
);
|
||||||
|
if (version != null) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are fetching tags, ignore cached versionFile
|
||||||
|
if (fetchTags && versionFile.existsSync()) {
|
||||||
|
versionFile.deleteSync();
|
||||||
|
final File legacyVersionFile = fs.file(fs.path.join(flutterRoot, 'version'));
|
||||||
|
if (legacyVersionFile.existsSync()) {
|
||||||
|
legacyVersionFile.deleteSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final String frameworkRevision = _runGit(
|
||||||
gitLog(<String>['-n', '1', '--pretty=format:%H']).join(' '),
|
gitLog(<String>['-n', '1', '--pretty=format:%H']).join(' '),
|
||||||
globals.processUtils,
|
globals.processUtils,
|
||||||
_workingDirectory,
|
flutterRoot,
|
||||||
|
);
|
||||||
|
|
||||||
|
return FlutterVersion.fromRevision(
|
||||||
|
clock: clock,
|
||||||
|
frameworkRevision: frameworkRevision,
|
||||||
|
fs: fs,
|
||||||
|
flutterRoot: flutterRoot,
|
||||||
|
fetchTags: fetchTags,
|
||||||
);
|
);
|
||||||
_gitTagVersion = GitTagVersion.determine(globals.processUtils, globals.platform, workingDirectory: _workingDirectory, gitRef: _frameworkRevision);
|
|
||||||
_frameworkVersion = gitTagVersion.frameworkVersionFor(_frameworkRevision);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlutterVersion._({
|
||||||
|
required SystemClock clock,
|
||||||
|
required this.flutterRoot,
|
||||||
|
required this.fs,
|
||||||
|
}) : _clock = clock;
|
||||||
|
|
||||||
|
factory FlutterVersion.fromRevision({
|
||||||
|
SystemClock clock = const SystemClock(),
|
||||||
|
required String flutterRoot,
|
||||||
|
required String frameworkRevision,
|
||||||
|
required FileSystem fs,
|
||||||
|
bool fetchTags = false,
|
||||||
|
}) {
|
||||||
|
final GitTagVersion gitTagVersion = GitTagVersion.determine(
|
||||||
|
globals.processUtils,
|
||||||
|
globals.platform,
|
||||||
|
gitRef: frameworkRevision,
|
||||||
|
workingDirectory: flutterRoot,
|
||||||
|
fetchTags: fetchTags,
|
||||||
|
);
|
||||||
|
final String frameworkVersion = gitTagVersion.frameworkVersionFor(frameworkRevision);
|
||||||
|
return _FlutterVersionGit._(
|
||||||
|
clock: clock,
|
||||||
|
flutterRoot: flutterRoot,
|
||||||
|
frameworkRevision: frameworkRevision,
|
||||||
|
frameworkVersion: frameworkVersion,
|
||||||
|
gitTagVersion: gitTagVersion,
|
||||||
|
fs: fs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensure the latest git tags are fetched and recalculate [FlutterVersion].
|
||||||
|
///
|
||||||
|
/// This is only required when not on beta or stable and we need to calculate
|
||||||
|
/// the current version relative to upstream tags.
|
||||||
|
///
|
||||||
|
/// This is a method and not a factory constructor so that test classes can
|
||||||
|
/// override it.
|
||||||
|
FlutterVersion fetchTagsAndGetVersion({
|
||||||
|
SystemClock clock = const SystemClock(),
|
||||||
|
}) {
|
||||||
|
// We don't need to fetch tags on beta and stable to calculate the version,
|
||||||
|
// we should already exactly be on a tag that was pushed when this release
|
||||||
|
// was published.
|
||||||
|
if (channel != 'master' && channel != 'main') {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return FlutterVersion(
|
||||||
|
clock: clock,
|
||||||
|
flutterRoot: flutterRoot,
|
||||||
|
fs: fs,
|
||||||
|
fetchTags: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileSystem fs;
|
||||||
|
|
||||||
final SystemClock _clock;
|
final SystemClock _clock;
|
||||||
final String? _workingDirectory;
|
|
||||||
|
|
||||||
/// Fetches tags from the upstream Flutter repository and re-calculates the
|
String? get repositoryUrl;
|
||||||
/// version.
|
|
||||||
|
GitTagVersion get gitTagVersion;
|
||||||
|
|
||||||
|
/// The channel is the upstream branch.
|
||||||
///
|
///
|
||||||
/// This carries a performance penalty, and should only be called when the
|
/// `master`, `dev`, `beta`, `stable`; or old ones, like `alpha`, `hackathon`, ...
|
||||||
/// user explicitly wants to get the version, e.g. for `flutter --version` or
|
String get channel;
|
||||||
/// `flutter doctor`.
|
|
||||||
void fetchTagsAndUpdate() {
|
|
||||||
_gitTagVersion = GitTagVersion.determine(globals.processUtils, globals.platform, workingDirectory: _workingDirectory, fetchTags: true);
|
|
||||||
_frameworkVersion = gitTagVersion.frameworkVersionFor(_frameworkRevision);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _repositoryUrl;
|
String get frameworkRevision;
|
||||||
String? get repositoryUrl {
|
|
||||||
if (_repositoryUrl == null) {
|
|
||||||
final String gitChannel = _runGit(
|
|
||||||
'git rev-parse --abbrev-ref --symbolic $kGitTrackingUpstream',
|
|
||||||
globals.processUtils,
|
|
||||||
_workingDirectory,
|
|
||||||
);
|
|
||||||
final int slash = gitChannel.indexOf('/');
|
|
||||||
if (slash != -1) {
|
|
||||||
final String remote = gitChannel.substring(0, slash);
|
|
||||||
_repositoryUrl = _runGit(
|
|
||||||
'git ls-remote --get-url $remote',
|
|
||||||
globals.processUtils,
|
|
||||||
_workingDirectory,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _repositoryUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The channel is the current branch if we recognize it, or "[user-branch]" (kUserBranch).
|
|
||||||
/// `master`, `beta`, `stable`; or old ones, like `alpha`, `hackathon`, `dev`, ...
|
|
||||||
String get channel {
|
|
||||||
final String channel = getBranchName(redactUnknownBranches: true);
|
|
||||||
assert(kOfficialChannels.contains(channel) || kObsoleteBranches.containsKey(channel) || channel == kUserBranch, 'Potential PII leak in channel name: "$channel"');
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
late GitTagVersion _gitTagVersion;
|
|
||||||
GitTagVersion get gitTagVersion => _gitTagVersion;
|
|
||||||
|
|
||||||
/// The name of the local branch.
|
|
||||||
/// Use getBranchName() to read this.
|
|
||||||
String? _branch;
|
|
||||||
|
|
||||||
late String _frameworkRevision;
|
|
||||||
String get frameworkRevision => _frameworkRevision;
|
|
||||||
String get frameworkRevisionShort => _shortGitRevision(frameworkRevision);
|
String get frameworkRevisionShort => _shortGitRevision(frameworkRevision);
|
||||||
|
|
||||||
|
String get frameworkVersion;
|
||||||
|
|
||||||
|
String get devToolsVersion;
|
||||||
|
|
||||||
|
String get dartSdkVersion;
|
||||||
|
|
||||||
|
String get engineRevision;
|
||||||
|
String get engineRevisionShort => _shortGitRevision(engineRevision);
|
||||||
|
|
||||||
|
// This is static as it is called from a constructor.
|
||||||
|
static File getVersionFile(FileSystem fs, String flutterRoot) {
|
||||||
|
return fs.file(fs.path.join(flutterRoot, 'bin', 'cache', 'flutter.version.json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
final String flutterRoot;
|
||||||
|
|
||||||
String? _frameworkAge;
|
String? _frameworkAge;
|
||||||
|
|
||||||
|
// TODO(fujino): calculate this relative to frameworkCommitDate for
|
||||||
|
// _FlutterVersionFromFile so we don't need a git call.
|
||||||
String get frameworkAge {
|
String get frameworkAge {
|
||||||
return _frameworkAge ??= _runGit(
|
return _frameworkAge ??= _runGit(
|
||||||
gitLog(<String>['-n', '1', '--pretty=format:%ar']).join(' '),
|
FlutterVersion.gitLog(<String>['-n', '1', '--pretty=format:%ar']).join(' '),
|
||||||
globals.processUtils,
|
globals.processUtils,
|
||||||
_workingDirectory,
|
flutterRoot,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late String _frameworkVersion;
|
void ensureVersionFile();
|
||||||
String get frameworkVersion => _frameworkVersion;
|
|
||||||
|
|
||||||
String get devToolsVersion => globals.cache.devToolsVersion;
|
|
||||||
|
|
||||||
String get dartSdkVersion => globals.cache.dartSdkVersion;
|
|
||||||
|
|
||||||
String get engineRevision => globals.cache.engineRevision;
|
|
||||||
String get engineRevisionShort => _shortGitRevision(engineRevision);
|
|
||||||
|
|
||||||
void ensureVersionFile() {
|
|
||||||
globals.fs.file(globals.fs.path.join(Cache.flutterRoot!, 'version')).writeAsStringSync(_frameworkVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -202,47 +251,13 @@ class FlutterVersion {
|
|||||||
'engineRevision': engineRevision,
|
'engineRevision': engineRevision,
|
||||||
'dartSdkVersion': dartSdkVersion,
|
'dartSdkVersion': dartSdkVersion,
|
||||||
'devToolsVersion': devToolsVersion,
|
'devToolsVersion': devToolsVersion,
|
||||||
|
'flutterVersion': frameworkVersion,
|
||||||
};
|
};
|
||||||
|
|
||||||
String get frameworkDate => frameworkCommitDate;
|
|
||||||
|
|
||||||
/// A date String describing the last framework commit.
|
/// A date String describing the last framework commit.
|
||||||
///
|
///
|
||||||
/// If a git command fails, this will return a placeholder date.
|
/// If a git command fails, this will return a placeholder date.
|
||||||
String get frameworkCommitDate => _gitCommitDate(lenient: true);
|
String get frameworkCommitDate;
|
||||||
|
|
||||||
// The date of the given commit hash as [gitRef]. If no hash is specified,
|
|
||||||
// then it is the HEAD of the current local branch.
|
|
||||||
//
|
|
||||||
// If lenient is true, and the git command fails, a placeholder date is
|
|
||||||
// returned. Otherwise, the VersionCheckError exception is propagated.
|
|
||||||
static String _gitCommitDate({
|
|
||||||
String gitRef = 'HEAD',
|
|
||||||
bool lenient = false,
|
|
||||||
}) {
|
|
||||||
final List<String> args = gitLog(<String>[
|
|
||||||
gitRef,
|
|
||||||
'-n',
|
|
||||||
'1',
|
|
||||||
'--pretty=format:%ad',
|
|
||||||
'--date=iso',
|
|
||||||
]);
|
|
||||||
try {
|
|
||||||
// Don't plumb 'lenient' through directly so that we can print an error
|
|
||||||
// if something goes wrong.
|
|
||||||
return _runSync(args, lenient: false);
|
|
||||||
} on VersionCheckError catch (e) {
|
|
||||||
if (lenient) {
|
|
||||||
final DateTime dummyDate = DateTime.fromMillisecondsSinceEpoch(0);
|
|
||||||
globals.printError('Failed to find the latest git commit date: $e\n'
|
|
||||||
'Returning $dummyDate instead.');
|
|
||||||
// Return something that DateTime.parse() can parse.
|
|
||||||
return dummyDate.toString();
|
|
||||||
} else {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the currently installed version of Flutter is up-to-date, and
|
/// Checks if the currently installed version of Flutter is up-to-date, and
|
||||||
/// warns the user if it isn't.
|
/// warns the user if it isn't.
|
||||||
@ -261,7 +276,7 @@ class FlutterVersion {
|
|||||||
DateTime localFrameworkCommitDate;
|
DateTime localFrameworkCommitDate;
|
||||||
try {
|
try {
|
||||||
// Don't perform the update check if fetching the latest local commit failed.
|
// Don't perform the update check if fetching the latest local commit failed.
|
||||||
localFrameworkCommitDate = DateTime.parse(_gitCommitDate());
|
localFrameworkCommitDate = DateTime.parse(_gitCommitDate(workingDirectory: flutterRoot));
|
||||||
} on VersionCheckError {
|
} on VersionCheckError {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -278,6 +293,53 @@ class FlutterVersion {
|
|||||||
).run();
|
).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the release date of the latest available Flutter version.
|
||||||
|
///
|
||||||
|
/// This method sends a server request if it's been more than
|
||||||
|
/// [checkAgeConsideredUpToDate] since the last version check.
|
||||||
|
///
|
||||||
|
/// Returns null if the cached version is out-of-date or missing, and we are
|
||||||
|
/// unable to reach the server to get the latest version.
|
||||||
|
Future<DateTime?> _getLatestAvailableFlutterDate() async {
|
||||||
|
globals.cache.checkLockAcquired();
|
||||||
|
final VersionCheckStamp versionCheckStamp = await VersionCheckStamp.load(globals.cache, globals.logger);
|
||||||
|
|
||||||
|
final DateTime now = _clock.now();
|
||||||
|
if (versionCheckStamp.lastTimeVersionWasChecked != null) {
|
||||||
|
final Duration timeSinceLastCheck = now.difference(
|
||||||
|
versionCheckStamp.lastTimeVersionWasChecked!,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Don't ping the server too often. Return cached value if it's fresh.
|
||||||
|
if (timeSinceLastCheck < VersionFreshnessValidator.checkAgeConsideredUpToDate) {
|
||||||
|
return versionCheckStamp.lastKnownRemoteVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache is empty or it's been a while since the last server ping. Ping the server.
|
||||||
|
try {
|
||||||
|
final DateTime remoteFrameworkCommitDate = DateTime.parse(
|
||||||
|
await fetchRemoteFrameworkCommitDate(),
|
||||||
|
);
|
||||||
|
await versionCheckStamp.store(
|
||||||
|
newTimeVersionWasChecked: now,
|
||||||
|
newKnownRemoteVersion: remoteFrameworkCommitDate,
|
||||||
|
);
|
||||||
|
return remoteFrameworkCommitDate;
|
||||||
|
} on VersionCheckError catch (error) {
|
||||||
|
// This happens when any of the git commands fails, which can happen when
|
||||||
|
// there's no Internet connectivity. Remote version check is best effort
|
||||||
|
// only. We do not prevent the command from running when it fails.
|
||||||
|
globals.printTrace('Failed to check Flutter version in the remote repository: $error');
|
||||||
|
// Still update the timestamp to avoid us hitting the server on every single
|
||||||
|
// command if for some reason we cannot connect (eg. we may be offline).
|
||||||
|
await versionCheckStamp.store(
|
||||||
|
newTimeVersionWasChecked: now,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The date of the latest framework commit in the remote repository.
|
/// The date of the latest framework commit in the remote repository.
|
||||||
///
|
///
|
||||||
/// Throws [VersionCheckError] if a git command fails, for example, when the
|
/// Throws [VersionCheckError] if a git command fails, for example, when the
|
||||||
@ -286,7 +348,7 @@ class FlutterVersion {
|
|||||||
try {
|
try {
|
||||||
// Fetch upstream branch's commit and tags
|
// Fetch upstream branch's commit and tags
|
||||||
await _run(<String>['git', 'fetch', '--tags']);
|
await _run(<String>['git', 'fetch', '--tags']);
|
||||||
return _gitCommitDate(gitRef: kGitTrackingUpstream);
|
return _gitCommitDate(gitRef: kGitTrackingUpstream, workingDirectory: Cache.flutterRoot);
|
||||||
} on VersionCheckError catch (error) {
|
} on VersionCheckError catch (error) {
|
||||||
globals.printError(error.message);
|
globals.printError(error.message);
|
||||||
rethrow;
|
rethrow;
|
||||||
@ -301,13 +363,18 @@ class FlutterVersion {
|
|||||||
return '${getBranchName(redactUnknownBranches: redactUnknownBranches)}/$frameworkRevisionShort';
|
return '${getBranchName(redactUnknownBranches: redactUnknownBranches)}/$frameworkRevisionShort';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The name of the local branch.
|
||||||
|
///
|
||||||
|
/// Use getBranchName() to read this.
|
||||||
|
String? _branch;
|
||||||
|
|
||||||
/// Return the branch name.
|
/// Return the branch name.
|
||||||
///
|
///
|
||||||
/// If [redactUnknownBranches] is true and the branch is unknown,
|
/// If [redactUnknownBranches] is true and the branch is unknown,
|
||||||
/// the branch name will be returned as `'[user-branch]'` ([kUserBranch]).
|
/// the branch name will be returned as `'[user-branch]'` ([kUserBranch]).
|
||||||
String getBranchName({ bool redactUnknownBranches = false }) {
|
String getBranchName({ bool redactUnknownBranches = false }) {
|
||||||
_branch ??= () {
|
_branch ??= () {
|
||||||
final String branch = _runGit('git symbolic-ref --short HEAD', globals.processUtils, _workingDirectory);
|
final String branch = _runGit('git symbolic-ref --short HEAD', globals.processUtils, flutterRoot);
|
||||||
return branch == 'HEAD' ? '' : branch;
|
return branch == 'HEAD' ? '' : branch;
|
||||||
}();
|
}();
|
||||||
if (redactUnknownBranches || _branch!.isEmpty) {
|
if (redactUnknownBranches || _branch!.isEmpty) {
|
||||||
@ -342,53 +409,207 @@ class FlutterVersion {
|
|||||||
static List<String> gitLog(List<String> args) {
|
static List<String> gitLog(List<String> args) {
|
||||||
return <String>['git', '-c', 'log.showSignature=false', 'log'] + args;
|
return <String>['git', '-c', 'log.showSignature=false', 'log'] + args;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the release date of the latest available Flutter version.
|
// The date of the given commit hash as [gitRef]. If no hash is specified,
|
||||||
///
|
// then it is the HEAD of the current local branch.
|
||||||
/// This method sends a server request if it's been more than
|
//
|
||||||
/// [checkAgeConsideredUpToDate] since the last version check.
|
// If lenient is true, and the git command fails, a placeholder date is
|
||||||
///
|
// returned. Otherwise, the VersionCheckError exception is propagated.
|
||||||
/// Returns null if the cached version is out-of-date or missing, and we are
|
String _gitCommitDate({
|
||||||
/// unable to reach the server to get the latest version.
|
String gitRef = 'HEAD',
|
||||||
Future<DateTime?> _getLatestAvailableFlutterDate() async {
|
bool lenient = false,
|
||||||
globals.cache.checkLockAcquired();
|
required String? workingDirectory,
|
||||||
final VersionCheckStamp versionCheckStamp = await VersionCheckStamp.load(globals.cache, globals.logger);
|
}) {
|
||||||
|
final List<String> args = FlutterVersion.gitLog(<String>[
|
||||||
final DateTime now = _clock.now();
|
gitRef,
|
||||||
if (versionCheckStamp.lastTimeVersionWasChecked != null) {
|
'-n',
|
||||||
final Duration timeSinceLastCheck = now.difference(
|
'1',
|
||||||
versionCheckStamp.lastTimeVersionWasChecked!,
|
'--pretty=format:%ad',
|
||||||
);
|
'--date=iso',
|
||||||
|
]);
|
||||||
// Don't ping the server too often. Return cached value if it's fresh.
|
|
||||||
if (timeSinceLastCheck < VersionFreshnessValidator.checkAgeConsideredUpToDate) {
|
|
||||||
return versionCheckStamp.lastKnownRemoteVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache is empty or it's been a while since the last server ping. Ping the server.
|
|
||||||
try {
|
try {
|
||||||
final DateTime remoteFrameworkCommitDate = DateTime.parse(
|
// Don't plumb 'lenient' through directly so that we can print an error
|
||||||
await FlutterVersion.fetchRemoteFrameworkCommitDate(),
|
// if something goes wrong.
|
||||||
|
return _runSync(
|
||||||
|
args,
|
||||||
|
lenient: false,
|
||||||
|
workingDirectory: workingDirectory,
|
||||||
);
|
);
|
||||||
await versionCheckStamp.store(
|
} on VersionCheckError catch (e) {
|
||||||
newTimeVersionWasChecked: now,
|
if (lenient) {
|
||||||
newKnownRemoteVersion: remoteFrameworkCommitDate,
|
final DateTime dummyDate = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
);
|
globals.printError('Failed to find the latest git commit date: $e\n'
|
||||||
return remoteFrameworkCommitDate;
|
'Returning $dummyDate instead.');
|
||||||
} on VersionCheckError catch (error) {
|
// Return something that DateTime.parse() can parse.
|
||||||
// This happens when any of the git commands fails, which can happen when
|
return dummyDate.toString();
|
||||||
// there's no Internet connectivity. Remote version check is best effort
|
} else {
|
||||||
// only. We do not prevent the command from running when it fails.
|
rethrow;
|
||||||
globals.printTrace('Failed to check Flutter version in the remote repository: $error');
|
}
|
||||||
// Still update the timestamp to avoid us hitting the server on every single
|
}
|
||||||
// command if for some reason we cannot connect (eg. we may be offline).
|
}
|
||||||
await versionCheckStamp.store(
|
|
||||||
newTimeVersionWasChecked: now,
|
class _FlutterVersionFromFile extends FlutterVersion {
|
||||||
|
_FlutterVersionFromFile._({
|
||||||
|
required super.clock,
|
||||||
|
required this.frameworkVersion,
|
||||||
|
required this.channel,
|
||||||
|
required this.repositoryUrl,
|
||||||
|
required this.frameworkRevision,
|
||||||
|
required this.frameworkCommitDate,
|
||||||
|
required this.engineRevision,
|
||||||
|
required this.dartSdkVersion,
|
||||||
|
required this.devToolsVersion,
|
||||||
|
required this.gitTagVersion,
|
||||||
|
required super.flutterRoot,
|
||||||
|
required super.fs,
|
||||||
|
}) : super._();
|
||||||
|
|
||||||
|
static _FlutterVersionFromFile? tryParseFromFile(
|
||||||
|
File jsonFile, {
|
||||||
|
required String flutterRoot,
|
||||||
|
SystemClock clock = const SystemClock(),
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
final String jsonContents = jsonFile.readAsStringSync();
|
||||||
|
final Map<String, Object?> manifest = jsonDecode(jsonContents) as Map<String, Object?>;
|
||||||
|
|
||||||
|
return _FlutterVersionFromFile._(
|
||||||
|
clock: clock,
|
||||||
|
frameworkVersion: manifest['frameworkVersion']! as String,
|
||||||
|
channel: manifest['channel']! as String,
|
||||||
|
repositoryUrl: manifest['repositoryUrl']! as String,
|
||||||
|
frameworkRevision: manifest['frameworkRevision']! as String,
|
||||||
|
frameworkCommitDate: manifest['frameworkCommitDate']! as String,
|
||||||
|
engineRevision: manifest['engineRevision']! as String,
|
||||||
|
dartSdkVersion: manifest['dartSdkVersion']! as String,
|
||||||
|
devToolsVersion: manifest['devToolsVersion']! as String,
|
||||||
|
gitTagVersion: GitTagVersion.parse(manifest['flutterVersion']! as String),
|
||||||
|
flutterRoot: flutterRoot,
|
||||||
|
fs: jsonFile.fileSystem,
|
||||||
);
|
);
|
||||||
|
// ignore: avoid_catches_without_on_clauses
|
||||||
|
} catch (err) {
|
||||||
|
globals.printTrace('Failed to parse ${jsonFile.path} with $err');
|
||||||
|
try {
|
||||||
|
jsonFile.deleteSync();
|
||||||
|
} on FileSystemException {
|
||||||
|
globals.printTrace('Failed to delete ${jsonFile.path}');
|
||||||
|
}
|
||||||
|
// Returning null means fallback to git implementation.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final GitTagVersion gitTagVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String frameworkVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String channel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getBranchName({bool redactUnknownBranches = false}) => channel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String repositoryUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String frameworkRevision;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String frameworkCommitDate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String engineRevision;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String dartSdkVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String devToolsVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void ensureVersionFile() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FlutterVersionGit extends FlutterVersion {
|
||||||
|
_FlutterVersionGit._({
|
||||||
|
required super.clock,
|
||||||
|
required super.flutterRoot,
|
||||||
|
required this.frameworkRevision,
|
||||||
|
required this.frameworkVersion,
|
||||||
|
required this.gitTagVersion,
|
||||||
|
required super.fs,
|
||||||
|
}) : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final GitTagVersion gitTagVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String frameworkRevision;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get frameworkCommitDate => _gitCommitDate(lenient: true, workingDirectory: flutterRoot);
|
||||||
|
|
||||||
|
String? _repositoryUrl;
|
||||||
|
@override
|
||||||
|
String? get repositoryUrl {
|
||||||
|
if (_repositoryUrl == null) {
|
||||||
|
final String gitChannel = _runGit(
|
||||||
|
'git rev-parse --abbrev-ref --symbolic $kGitTrackingUpstream',
|
||||||
|
globals.processUtils,
|
||||||
|
flutterRoot,
|
||||||
|
);
|
||||||
|
final int slash = gitChannel.indexOf('/');
|
||||||
|
if (slash != -1) {
|
||||||
|
final String remote = gitChannel.substring(0, slash);
|
||||||
|
_repositoryUrl = _runGit(
|
||||||
|
'git ls-remote --get-url $remote',
|
||||||
|
globals.processUtils,
|
||||||
|
flutterRoot,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _repositoryUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get devToolsVersion => globals.cache.devToolsVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get dartSdkVersion => globals.cache.dartSdkVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get engineRevision => globals.cache.engineRevision;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String frameworkVersion;
|
||||||
|
|
||||||
|
/// The channel is the current branch if we recognize it, or "[user-branch]" (kUserBranch).
|
||||||
|
/// `master`, `beta`, `stable`; or old ones, like `alpha`, `hackathon`, `dev`, ...
|
||||||
|
@override
|
||||||
|
String get channel {
|
||||||
|
final String channel = getBranchName(redactUnknownBranches: true);
|
||||||
|
assert(kOfficialChannels.contains(channel) || kObsoleteBranches.containsKey(channel) || channel == kUserBranch, 'Potential PII leak in channel name: "$channel"');
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void ensureVersionFile() {
|
||||||
|
final File legacyVersionFile = fs.file(fs.path.join(flutterRoot, 'version'));
|
||||||
|
if (!legacyVersionFile.existsSync()) {
|
||||||
|
legacyVersionFile.writeAsStringSync(frameworkVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
const JsonEncoder encoder = JsonEncoder.withIndent(' ');
|
||||||
|
final File newVersionFile = FlutterVersion.getVersionFile(fs, flutterRoot);
|
||||||
|
if (!newVersionFile.existsSync()) {
|
||||||
|
newVersionFile.writeAsStringSync(encoder.convert(toJson()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the provided [version] is tracking a standard remote.
|
/// Checks if the provided [version] is tracking a standard remote.
|
||||||
@ -606,10 +827,14 @@ class VersionCheckError implements Exception {
|
|||||||
///
|
///
|
||||||
/// If [lenient] is true and the command fails, returns an empty string.
|
/// If [lenient] is true and the command fails, returns an empty string.
|
||||||
/// Otherwise, throws a [ToolExit] exception.
|
/// Otherwise, throws a [ToolExit] exception.
|
||||||
String _runSync(List<String> command, { bool lenient = true }) {
|
String _runSync(
|
||||||
|
List<String> command, {
|
||||||
|
bool lenient = true,
|
||||||
|
required String? workingDirectory,
|
||||||
|
}) {
|
||||||
final ProcessResult results = globals.processManager.runSync(
|
final ProcessResult results = globals.processManager.runSync(
|
||||||
command,
|
command,
|
||||||
workingDirectory: Cache.flutterRoot,
|
workingDirectory: workingDirectory,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (results.exitCode == 0) {
|
if (results.exitCode == 0) {
|
||||||
@ -630,7 +855,7 @@ String _runSync(List<String> command, { bool lenient = true }) {
|
|||||||
String _runGit(String command, ProcessUtils processUtils, String? workingDirectory) {
|
String _runGit(String command, ProcessUtils processUtils, String? workingDirectory) {
|
||||||
return processUtils.runSync(
|
return processUtils.runSync(
|
||||||
command.split(' '),
|
command.split(' '),
|
||||||
workingDirectory: workingDirectory ?? Cache.flutterRoot,
|
workingDirectory: workingDirectory,
|
||||||
).stdout.trim();
|
).stdout.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,10 @@ import 'base/context.dart';
|
|||||||
import 'base/io.dart' as io;
|
import 'base/io.dart' as io;
|
||||||
import 'base/logger.dart';
|
import 'base/logger.dart';
|
||||||
import 'base/utils.dart';
|
import 'base/utils.dart';
|
||||||
|
import 'cache.dart';
|
||||||
import 'convert.dart';
|
import 'convert.dart';
|
||||||
import 'device.dart';
|
import 'device.dart';
|
||||||
|
import 'globals.dart' as globals;
|
||||||
import 'ios/xcodeproj.dart';
|
import 'ios/xcodeproj.dart';
|
||||||
import 'project.dart';
|
import 'project.dart';
|
||||||
import 'version.dart';
|
import 'version.dart';
|
||||||
@ -244,7 +246,10 @@ Future<vm_service.VmService> setUpVmService({
|
|||||||
}
|
}
|
||||||
|
|
||||||
vmService.registerServiceCallback(kFlutterVersionServiceName, (Map<String, Object?> params) async {
|
vmService.registerServiceCallback(kFlutterVersionServiceName, (Map<String, Object?> params) async {
|
||||||
final FlutterVersion version = context.get<FlutterVersion>() ?? FlutterVersion();
|
final FlutterVersion version = context.get<FlutterVersion>() ?? FlutterVersion(
|
||||||
|
fs: globals.fs,
|
||||||
|
flutterRoot: Cache.flutterRoot!,
|
||||||
|
);
|
||||||
final Map<String, Object> versionJson = version.toJson();
|
final Map<String, Object> versionJson = version.toJson();
|
||||||
versionJson['frameworkRevisionShort'] = version.frameworkRevisionShort;
|
versionJson['frameworkRevisionShort'] = version.frameworkRevisionShort;
|
||||||
versionJson['engineRevisionShort'] = version.engineRevisionShort;
|
versionJson['engineRevisionShort'] = version.engineRevisionShort;
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
|
||||||
import 'package:fake_async/fake_async.dart';
|
import 'package:fake_async/fake_async.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/android/android_studio_validator.dart';
|
import 'package:flutter_tools/src/android/android_studio_validator.dart';
|
||||||
@ -16,7 +15,6 @@ import 'package:flutter_tools/src/base/terminal.dart';
|
|||||||
import 'package:flutter_tools/src/base/user_messages.dart';
|
import 'package:flutter_tools/src/base/user_messages.dart';
|
||||||
import 'package:flutter_tools/src/build_info.dart';
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/commands/doctor.dart';
|
|
||||||
import 'package:flutter_tools/src/custom_devices/custom_device_workflow.dart';
|
import 'package:flutter_tools/src/custom_devices/custom_device_workflow.dart';
|
||||||
import 'package:flutter_tools/src/device.dart';
|
import 'package:flutter_tools/src/device.dart';
|
||||||
import 'package:flutter_tools/src/doctor.dart';
|
import 'package:flutter_tools/src/doctor.dart';
|
||||||
@ -32,17 +30,16 @@ import 'package:test/fake.dart';
|
|||||||
import '../../src/common.dart';
|
import '../../src/common.dart';
|
||||||
import '../../src/context.dart';
|
import '../../src/context.dart';
|
||||||
import '../../src/fakes.dart';
|
import '../../src/fakes.dart';
|
||||||
import '../../src/test_flutter_command_runner.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late FakeFlutterVersion flutterVersion;
|
|
||||||
late BufferLogger logger;
|
late BufferLogger logger;
|
||||||
late FakeProcessManager fakeProcessManager;
|
late FakeProcessManager fakeProcessManager;
|
||||||
|
late MemoryFileSystem fs;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
flutterVersion = FakeFlutterVersion();
|
|
||||||
logger = BufferLogger.test();
|
logger = BufferLogger.test();
|
||||||
fakeProcessManager = FakeProcessManager.empty();
|
fakeProcessManager = FakeProcessManager.empty();
|
||||||
|
fs = MemoryFileSystem.test();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('ValidationMessage equality and hashCode includes contextUrl', () {
|
testWithoutContext('ValidationMessage equality and hashCode includes contextUrl', () {
|
||||||
@ -761,27 +758,55 @@ void main() {
|
|||||||
contains(isA<CustomDeviceWorkflow>()),
|
contains(isA<CustomDeviceWorkflow>()),
|
||||||
);
|
);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FileSystem: () => MemoryFileSystem.test(),
|
FileSystem: () => fs,
|
||||||
ProcessManager: () => fakeProcessManager,
|
ProcessManager: () => fakeProcessManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('Fetches tags to get the right version', () async {
|
group('FlutterValidator', () {
|
||||||
Cache.disableLocking();
|
late FakeFlutterVersion initialVersion;
|
||||||
|
late FakeFlutterVersion secondVersion;
|
||||||
|
late TestFeatureFlags featureFlags;
|
||||||
|
|
||||||
final DoctorCommand doctorCommand = DoctorCommand();
|
setUp(() {
|
||||||
final CommandRunner<void> commandRunner = createTestCommandRunner(doctorCommand);
|
secondVersion = FakeFlutterVersion(frameworkRevisionShort: '222');
|
||||||
|
initialVersion = FakeFlutterVersion(
|
||||||
|
frameworkRevisionShort: '111',
|
||||||
|
nextFlutterVersion: secondVersion,
|
||||||
|
);
|
||||||
|
featureFlags = TestFeatureFlags();
|
||||||
|
});
|
||||||
|
|
||||||
await commandRunner.run(<String>['doctor']);
|
testUsingContext('FlutterValidator fetches tags and gets fresh version', () async {
|
||||||
|
final Directory devtoolsDir = fs.directory('/path/to/flutter/bin/cache/dart-sdk/bin/resources/devtools')
|
||||||
expect(flutterVersion.didFetchTagsAndUpdate, true);
|
..createSync(recursive: true);
|
||||||
Cache.enableLocking();
|
fs.directory('/path/to/flutter/bin/cache/artifacts').createSync(recursive: true);
|
||||||
|
devtoolsDir.childFile('version.json').writeAsStringSync('{"version": "123"}');
|
||||||
|
fakeProcessManager.addCommands(const <FakeCommand>[
|
||||||
|
FakeCommand(command: <String>['which', 'java']),
|
||||||
|
]);
|
||||||
|
final List<DoctorValidator> validators = DoctorValidatorsProvider.test(
|
||||||
|
featureFlags: featureFlags,
|
||||||
|
platform: FakePlatform(),
|
||||||
|
).validators;
|
||||||
|
final FlutterValidator flutterValidator = validators.whereType<FlutterValidator>().first;
|
||||||
|
final ValidationResult result = await flutterValidator.validate();
|
||||||
|
expect(
|
||||||
|
result.messages.map((ValidationMessage msg) => msg.message),
|
||||||
|
contains(contains('Framework revision 222')),
|
||||||
|
);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
Cache: () => Cache.test(
|
||||||
FileSystem: () => MemoryFileSystem.test(),
|
rootOverride: fs.directory('/path/to/flutter'),
|
||||||
FlutterVersion: () => flutterVersion,
|
fileSystem: fs,
|
||||||
Doctor: () => NoOpDoctor(),
|
processManager: fakeProcessManager,
|
||||||
}, initializeFlutterRoot: false);
|
),
|
||||||
|
FileSystem: () => fs,
|
||||||
|
FlutterVersion: () => initialVersion,
|
||||||
|
Platform: () => FakePlatform(),
|
||||||
|
ProcessManager: () => fakeProcessManager,
|
||||||
|
TestFeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
});
|
||||||
testUsingContext('If android workflow is disabled, AndroidStudio validator is not included', () {
|
testUsingContext('If android workflow is disabled, AndroidStudio validator is not included', () {
|
||||||
final DoctorValidatorsProvider provider = DoctorValidatorsProvider.test(
|
final DoctorValidatorsProvider provider = DoctorValidatorsProvider.test(
|
||||||
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
|
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
|
||||||
@ -826,6 +851,7 @@ class NoOpDoctor implements Doctor {
|
|||||||
bool showPii = true,
|
bool showPii = true,
|
||||||
List<ValidatorTask>? startedValidatorTasks,
|
List<ValidatorTask>? startedValidatorTasks,
|
||||||
bool sendEvent = true,
|
bool sendEvent = true,
|
||||||
|
FlutterVersion? version,
|
||||||
}) async => true;
|
}) async => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -37,7 +37,8 @@ void main() {
|
|||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
fakeCommandRunner = FakeUpgradeCommandRunner();
|
fakeCommandRunner = FakeUpgradeCommandRunner();
|
||||||
realCommandRunner = UpgradeCommandRunner();
|
realCommandRunner = UpgradeCommandRunner()
|
||||||
|
..workingDirectory = Cache.flutterRoot;
|
||||||
processManager = FakeProcessManager.empty();
|
processManager = FakeProcessManager.empty();
|
||||||
fakeCommandRunner.willHaveUncommittedChanges = false;
|
fakeCommandRunner.willHaveUncommittedChanges = false;
|
||||||
fakePlatform = FakePlatform()..environment = Map<String, String>.unmodifiable(<String, String>{
|
fakePlatform = FakePlatform()..environment = Map<String, String>.unmodifiable(<String, String>{
|
||||||
|
@ -41,11 +41,14 @@ void main() {
|
|||||||
group('analytics', () {
|
group('analytics', () {
|
||||||
late Directory tempDir;
|
late Directory tempDir;
|
||||||
late Config testConfig;
|
late Config testConfig;
|
||||||
|
late FileSystem fs;
|
||||||
|
const String flutterRoot = '/path/to/flutter';
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
Cache.flutterRoot = '../..';
|
Cache.flutterRoot = flutterRoot;
|
||||||
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_analytics_test.');
|
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_analytics_test.');
|
||||||
testConfig = Config.test();
|
testConfig = Config.test();
|
||||||
|
fs = MemoryFileSystem.test();
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() {
|
tearDown(() {
|
||||||
@ -77,7 +80,7 @@ void main() {
|
|||||||
|
|
||||||
expect(count, 0);
|
expect(count, 0);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FlutterVersion: () => FlutterVersion(),
|
FlutterVersion: () => FakeFlutterVersion(),
|
||||||
Usage: () => Usage(
|
Usage: () => Usage(
|
||||||
configDirOverride: tempDir.path,
|
configDirOverride: tempDir.path,
|
||||||
logFile: tempDir.childFile('analytics.log').path,
|
logFile: tempDir.childFile('analytics.log').path,
|
||||||
@ -101,7 +104,7 @@ void main() {
|
|||||||
|
|
||||||
expect(count, 0);
|
expect(count, 0);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FlutterVersion: () => FlutterVersion(),
|
FlutterVersion: () => FakeFlutterVersion(),
|
||||||
Usage: () => Usage(
|
Usage: () => Usage(
|
||||||
configDirOverride: tempDir.path,
|
configDirOverride: tempDir.path,
|
||||||
logFile: tempDir.childFile('analytics.log').path,
|
logFile: tempDir.childFile('analytics.log').path,
|
||||||
@ -118,12 +121,12 @@ void main() {
|
|||||||
|
|
||||||
expect(globals.fs.file('test').readAsStringSync(), contains('$featuresKey: enable-web'));
|
expect(globals.fs.file('test').readAsStringSync(), contains('$featuresKey: enable-web'));
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FlutterVersion: () => FlutterVersion(),
|
FlutterVersion: () => FakeFlutterVersion(),
|
||||||
Config: () => testConfig,
|
Config: () => testConfig,
|
||||||
Platform: () => FakePlatform(environment: <String, String>{
|
Platform: () => FakePlatform(environment: <String, String>{
|
||||||
'FLUTTER_ANALYTICS_LOG_FILE': 'test',
|
'FLUTTER_ANALYTICS_LOG_FILE': 'test',
|
||||||
}),
|
}),
|
||||||
FileSystem: () => MemoryFileSystem.test(),
|
FileSystem: () => fs,
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -141,12 +144,12 @@ void main() {
|
|||||||
contains('$featuresKey: enable-web,enable-linux-desktop,enable-macos-desktop'),
|
contains('$featuresKey: enable-web,enable-linux-desktop,enable-macos-desktop'),
|
||||||
);
|
);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FlutterVersion: () => FlutterVersion(),
|
FlutterVersion: () => FakeFlutterVersion(),
|
||||||
Config: () => testConfig,
|
Config: () => testConfig,
|
||||||
Platform: () => FakePlatform(environment: <String, String>{
|
Platform: () => FakePlatform(environment: <String, String>{
|
||||||
'FLUTTER_ANALYTICS_LOG_FILE': 'test',
|
'FLUTTER_ANALYTICS_LOG_FILE': 'test',
|
||||||
}),
|
}),
|
||||||
FileSystem: () => MemoryFileSystem.test(),
|
FileSystem: () => fs,
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -384,6 +387,7 @@ class FakeDoctor extends Fake implements Doctor {
|
|||||||
bool showPii = true,
|
bool showPii = true,
|
||||||
List<ValidatorTask>? startedValidatorTasks,
|
List<ValidatorTask>? startedValidatorTasks,
|
||||||
bool sendEvent = true,
|
bool sendEvent = true,
|
||||||
|
FlutterVersion? version,
|
||||||
}) async {
|
}) async {
|
||||||
return diagnoseSucceeds;
|
return diagnoseSucceeds;
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:file/file.dart';
|
||||||
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/base/process.dart';
|
import 'package:flutter_tools/src/base/process.dart';
|
||||||
import 'package:flutter_tools/src/base/time.dart';
|
import 'package:flutter_tools/src/base/time.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
||||||
import 'package:flutter_tools/src/version.dart';
|
import 'package:flutter_tools/src/version.dart';
|
||||||
import 'package:test/fake.dart';
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ import '../src/context.dart';
|
|||||||
import '../src/fake_process_manager.dart';
|
import '../src/fake_process_manager.dart';
|
||||||
import '../src/fakes.dart' show FakeFlutterVersion;
|
import '../src/fakes.dart' show FakeFlutterVersion;
|
||||||
|
|
||||||
final SystemClock _testClock = SystemClock.fixed(DateTime(2015));
|
final SystemClock _testClock = SystemClock.fixed(DateTime.utc(2015));
|
||||||
final DateTime _stampUpToDate = _testClock.ago(VersionFreshnessValidator.checkAgeConsideredUpToDate ~/ 2);
|
final DateTime _stampUpToDate = _testClock.ago(VersionFreshnessValidator.checkAgeConsideredUpToDate ~/ 2);
|
||||||
final DateTime _stampOutOfDate = _testClock.ago(VersionFreshnessValidator.checkAgeConsideredUpToDate * 2);
|
final DateTime _stampOutOfDate = _testClock.ago(VersionFreshnessValidator.checkAgeConsideredUpToDate * 2);
|
||||||
|
|
||||||
@ -49,7 +50,11 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group('$FlutterVersion for $channel', () {
|
group('$FlutterVersion for $channel', () {
|
||||||
|
late FileSystem fs;
|
||||||
|
const String flutterRoot = '/path/to/flutter';
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
|
fs = MemoryFileSystem.test();
|
||||||
Cache.disableLocking();
|
Cache.disableLocking();
|
||||||
VersionFreshnessValidator.timeToPauseToLetUserReadTheMessage = Duration.zero;
|
VersionFreshnessValidator.timeToPauseToLetUserReadTheMessage = Duration.zero;
|
||||||
});
|
});
|
||||||
@ -101,7 +106,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
final FlutterVersion flutterVersion = globals.flutterVersion;
|
final FlutterVersion flutterVersion = FlutterVersion(clock: _testClock, fs: fs, flutterRoot: flutterRoot);
|
||||||
await flutterVersion.checkFlutterVersionFreshness();
|
await flutterVersion.checkFlutterVersionFreshness();
|
||||||
expect(flutterVersion.channel, channel);
|
expect(flutterVersion.channel, channel);
|
||||||
expect(flutterVersion.repositoryUrl, flutterUpstreamUrl);
|
expect(flutterVersion.repositoryUrl, flutterUpstreamUrl);
|
||||||
@ -124,7 +129,6 @@ void main() {
|
|||||||
expect(testLogger.statusText, isEmpty);
|
expect(testLogger.statusText, isEmpty);
|
||||||
expect(processManager, hasNoRemainingExpectations);
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FlutterVersion: () => FlutterVersion(clock: _testClock),
|
|
||||||
ProcessManager: () => processManager,
|
ProcessManager: () => processManager,
|
||||||
Cache: () => cache,
|
Cache: () => cache,
|
||||||
});
|
});
|
||||||
@ -419,15 +423,197 @@ void main() {
|
|||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
final FlutterVersion flutterVersion = globals.flutterVersion;
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final FlutterVersion flutterVersion = FlutterVersion(
|
||||||
|
clock: _testClock,
|
||||||
|
fs: fs,
|
||||||
|
flutterRoot: '/path/to/flutter',
|
||||||
|
);
|
||||||
expect(flutterVersion.channel, '[user-branch]');
|
expect(flutterVersion.channel, '[user-branch]');
|
||||||
expect(flutterVersion.getVersionString(), 'feature-branch/1234abcd');
|
expect(flutterVersion.getVersionString(), 'feature-branch/1234abcd');
|
||||||
expect(flutterVersion.getBranchName(), 'feature-branch');
|
expect(flutterVersion.getBranchName(), 'feature-branch');
|
||||||
expect(flutterVersion.getVersionString(redactUnknownBranches: true), '[user-branch]/1234abcd');
|
expect(flutterVersion.getVersionString(redactUnknownBranches: true), '[user-branch]/1234abcd');
|
||||||
expect(flutterVersion.getBranchName(redactUnknownBranches: true), '[user-branch]');
|
expect(flutterVersion.getBranchName(redactUnknownBranches: true), '[user-branch]');
|
||||||
|
|
||||||
expect(processManager, hasNoRemainingExpectations);
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
FlutterVersion: () => FlutterVersion(clock: _testClock),
|
ProcessManager: () => processManager,
|
||||||
|
Cache: () => cache,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('ensureVersionFile() writes version information to disk', () async {
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', '-c', 'log.showSignature=false', 'log', '-n', '1', '--pretty=format:%H'],
|
||||||
|
stdout: '1234abcd',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'tag', '--points-at', '1234abcd'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'],
|
||||||
|
stdout: '0.1.2-3-1234abcd',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
|
||||||
|
stdout: 'feature-branch',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', '--abbrev-ref', '--symbolic', '@{upstream}'],
|
||||||
|
),
|
||||||
|
FakeCommand(
|
||||||
|
command: const <String>[
|
||||||
|
'git',
|
||||||
|
'-c',
|
||||||
|
'log.showSignature=false',
|
||||||
|
'log',
|
||||||
|
'HEAD',
|
||||||
|
'-n',
|
||||||
|
'1',
|
||||||
|
'--pretty=format:%ad',
|
||||||
|
'--date=iso',
|
||||||
|
],
|
||||||
|
stdout: _testClock.ago(VersionFreshnessValidator.versionAgeConsideredUpToDate('stable') ~/ 2).toString(),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final Directory flutterRoot = fs.directory('/path/to/flutter');
|
||||||
|
flutterRoot.childDirectory('bin').childDirectory('cache').createSync(recursive: true);
|
||||||
|
final FlutterVersion flutterVersion = FlutterVersion(
|
||||||
|
clock: _testClock,
|
||||||
|
fs: fs,
|
||||||
|
flutterRoot: flutterRoot.path,
|
||||||
|
);
|
||||||
|
|
||||||
|
final File versionFile = fs.file('/path/to/flutter/bin/cache/flutter.version.json');
|
||||||
|
expect(versionFile.existsSync(), isFalse);
|
||||||
|
|
||||||
|
flutterVersion.ensureVersionFile();
|
||||||
|
expect(versionFile.existsSync(), isTrue);
|
||||||
|
expect(versionFile.readAsStringSync(), '''
|
||||||
|
{
|
||||||
|
"frameworkVersion": "0.0.0-unknown",
|
||||||
|
"channel": "[user-branch]",
|
||||||
|
"repositoryUrl": "unknown source",
|
||||||
|
"frameworkRevision": "1234abcd",
|
||||||
|
"frameworkCommitDate": "2014-10-02 00:00:00.000Z",
|
||||||
|
"engineRevision": "abcdefg",
|
||||||
|
"dartSdkVersion": "2.12.0",
|
||||||
|
"devToolsVersion": "2.8.0",
|
||||||
|
"flutterVersion": "0.0.0-unknown"
|
||||||
|
}''');
|
||||||
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
Cache: () => cache,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('version does not call git if a .version.json file exists', () async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final Directory flutterRoot = fs.directory('/path/to/flutter');
|
||||||
|
final Directory cacheDir = flutterRoot
|
||||||
|
.childDirectory('bin')
|
||||||
|
.childDirectory('cache')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
const String devToolsVersion = '0000000';
|
||||||
|
const Map<String, Object> versionJson = <String, Object>{
|
||||||
|
'channel': 'stable',
|
||||||
|
'frameworkVersion': '1.2.3',
|
||||||
|
'repositoryUrl': 'https://github.com/flutter/flutter.git',
|
||||||
|
'frameworkRevision': '1234abcd',
|
||||||
|
'frameworkCommitDate': '2023-04-28 12:34:56 -0400',
|
||||||
|
'engineRevision': 'deadbeef',
|
||||||
|
'dartSdkVersion': 'deadbeef2',
|
||||||
|
'devToolsVersion': devToolsVersion,
|
||||||
|
'flutterVersion': 'foo',
|
||||||
|
};
|
||||||
|
cacheDir.childFile('flutter.version.json').writeAsStringSync(
|
||||||
|
jsonEncode(versionJson),
|
||||||
|
);
|
||||||
|
final FlutterVersion flutterVersion = FlutterVersion(
|
||||||
|
clock: _testClock,
|
||||||
|
fs: fs,
|
||||||
|
flutterRoot: flutterRoot.path,
|
||||||
|
);
|
||||||
|
expect(flutterVersion.channel, 'stable');
|
||||||
|
expect(flutterVersion.getVersionString(), 'stable/1.2.3');
|
||||||
|
expect(flutterVersion.getBranchName(), 'stable');
|
||||||
|
expect(flutterVersion.dartSdkVersion, 'deadbeef2');
|
||||||
|
expect(flutterVersion.devToolsVersion, devToolsVersion);
|
||||||
|
expect(flutterVersion.engineRevision, 'deadbeef');
|
||||||
|
|
||||||
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
Cache: () => cache,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('FlutterVersion() falls back to git if .version.json is malformed', () async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final Directory flutterRoot = fs.directory(fs.path.join('path', 'to', 'flutter'));
|
||||||
|
final Directory cacheDir = flutterRoot
|
||||||
|
.childDirectory('bin')
|
||||||
|
.childDirectory('cache')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
final File legacyVersionFile = flutterRoot.childFile('version');
|
||||||
|
final File versionFile = cacheDir.childFile('flutter.version.json')..writeAsStringSync(
|
||||||
|
'{',
|
||||||
|
);
|
||||||
|
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', '-c', 'log.showSignature=false', 'log', '-n', '1', '--pretty=format:%H'],
|
||||||
|
stdout: '1234abcd',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'tag', '--points-at', '1234abcd'],
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', '1234abcd'],
|
||||||
|
stdout: '0.1.2-3-1234abcd',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
|
||||||
|
stdout: 'feature-branch',
|
||||||
|
),
|
||||||
|
const FakeCommand(
|
||||||
|
command: <String>['git', 'rev-parse', '--abbrev-ref', '--symbolic', '@{upstream}'],
|
||||||
|
stdout: 'feature-branch',
|
||||||
|
),
|
||||||
|
FakeCommand(
|
||||||
|
command: const <String>[
|
||||||
|
'git',
|
||||||
|
'-c',
|
||||||
|
'log.showSignature=false',
|
||||||
|
'log',
|
||||||
|
'HEAD',
|
||||||
|
'-n',
|
||||||
|
'1',
|
||||||
|
'--pretty=format:%ad',
|
||||||
|
'--date=iso',
|
||||||
|
],
|
||||||
|
stdout: _testClock.ago(VersionFreshnessValidator.versionAgeConsideredUpToDate('stable') ~/ 2).toString(),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// version file exists in a malformed state
|
||||||
|
expect(versionFile.existsSync(), isTrue);
|
||||||
|
final FlutterVersion flutterVersion = FlutterVersion(
|
||||||
|
clock: _testClock,
|
||||||
|
fs: fs,
|
||||||
|
flutterRoot: flutterRoot.path,
|
||||||
|
);
|
||||||
|
|
||||||
|
// version file was deleted because it couldn't be parsed
|
||||||
|
expect(versionFile.existsSync(), isFalse);
|
||||||
|
expect(legacyVersionFile.existsSync(), isFalse);
|
||||||
|
// version file was written to disk
|
||||||
|
flutterVersion.ensureVersionFile();
|
||||||
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
|
expect(versionFile.existsSync(), isTrue);
|
||||||
|
expect(legacyVersionFile.existsSync(), isTrue);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
ProcessManager: () => processManager,
|
ProcessManager: () => processManager,
|
||||||
Cache: () => cache,
|
Cache: () => cache,
|
||||||
});
|
});
|
||||||
|
@ -158,7 +158,6 @@ void main() {
|
|||||||
expect(decoded['FlutterProject.isModule'], false);
|
expect(decoded['FlutterProject.isModule'], false);
|
||||||
expect(decoded['FlutterProject.isPlugin'], false);
|
expect(decoded['FlutterProject.isPlugin'], false);
|
||||||
expect(decoded['FlutterProject.manifest.appname'], 'test_project');
|
expect(decoded['FlutterProject.manifest.appname'], 'test_project');
|
||||||
expect(decoded['FlutterVersion.frameworkRevision'], '');
|
|
||||||
|
|
||||||
expect(decoded['Platform.isAndroid'], false);
|
expect(decoded['Platform.isAndroid'], false);
|
||||||
expect(decoded['Platform.isIOS'], false);
|
expect(decoded['Platform.isIOS'], false);
|
||||||
|
@ -43,8 +43,6 @@ void main() {
|
|||||||
final Directory testDirectory = parentDirectory.childDirectory('flutter');
|
final Directory testDirectory = parentDirectory.childDirectory('flutter');
|
||||||
testDirectory.createSync(recursive: true);
|
testDirectory.createSync(recursive: true);
|
||||||
|
|
||||||
int exitCode = 0;
|
|
||||||
|
|
||||||
// Enable longpaths for windows integration test.
|
// Enable longpaths for windows integration test.
|
||||||
await processManager.run(<String>[
|
await processManager.run(<String>[
|
||||||
'git', 'config', '--system', 'core.longpaths', 'true',
|
'git', 'config', '--system', 'core.longpaths', 'true',
|
||||||
|
@ -24,14 +24,18 @@ void main() {
|
|||||||
);
|
);
|
||||||
exampleAppDir = tempDir.childDirectory('bbb').childDirectory('example');
|
exampleAppDir = tempDir.childDirectory('bbb').childDirectory('example');
|
||||||
|
|
||||||
processManager.runSync(<String>[
|
processManager.runSync(
|
||||||
|
<String>[
|
||||||
flutterBin,
|
flutterBin,
|
||||||
...getLocalEngineArguments(),
|
...getLocalEngineArguments(),
|
||||||
'create',
|
'create',
|
||||||
'--template=plugin',
|
'--template=plugin',
|
||||||
'--platforms=android',
|
'--platforms=android',
|
||||||
'bbb',
|
'bbb',
|
||||||
], workingDirectory: tempDir.path);
|
'-v',
|
||||||
|
],
|
||||||
|
workingDirectory: tempDir.path,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
@ -48,14 +52,17 @@ void main() {
|
|||||||
// Ensure file is gone prior to configOnly running.
|
// Ensure file is gone prior to configOnly running.
|
||||||
await gradleFile.delete();
|
await gradleFile.delete();
|
||||||
|
|
||||||
final ProcessResult result = processManager.runSync(<String>[
|
final ProcessResult result = processManager.runSync(
|
||||||
|
<String>[
|
||||||
flutterBin,
|
flutterBin,
|
||||||
...getLocalEngineArguments(),
|
...getLocalEngineArguments(),
|
||||||
'build',
|
'build',
|
||||||
'apk',
|
'apk',
|
||||||
'--target-platform=android-arm',
|
'--target-platform=android-arm',
|
||||||
'--config-only',
|
'--config-only',
|
||||||
], workingDirectory: exampleAppDir.path);
|
],
|
||||||
|
workingDirectory: exampleAppDir.path,
|
||||||
|
);
|
||||||
|
|
||||||
expect(gradleFile, exists);
|
expect(gradleFile, exists);
|
||||||
expect(result.stdout, contains(RegExp(r'Config complete')));
|
expect(result.stdout, contains(RegExp(r'Config complete')));
|
||||||
|
@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
|
|||||||
import 'package:flutter_tools/src/base/io.dart';
|
import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/os.dart';
|
import 'package:flutter_tools/src/base/os.dart';
|
||||||
|
import 'package:flutter_tools/src/base/time.dart';
|
||||||
import 'package:flutter_tools/src/base/version.dart';
|
import 'package:flutter_tools/src/base/version.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/convert.dart';
|
import 'package:flutter_tools/src/convert.dart';
|
||||||
@ -337,6 +338,8 @@ class FakeFlutterVersion implements FlutterVersion {
|
|||||||
this.frameworkAge = '0 hours ago',
|
this.frameworkAge = '0 hours ago',
|
||||||
this.frameworkCommitDate = '12/01/01',
|
this.frameworkCommitDate = '12/01/01',
|
||||||
this.gitTagVersion = const GitTagVersion.unknown(),
|
this.gitTagVersion = const GitTagVersion.unknown(),
|
||||||
|
this.flutterRoot = '/path/to/flutter',
|
||||||
|
this.nextFlutterVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String branch;
|
final String branch;
|
||||||
@ -344,6 +347,17 @@ class FakeFlutterVersion implements FlutterVersion {
|
|||||||
bool get didFetchTagsAndUpdate => _didFetchTagsAndUpdate;
|
bool get didFetchTagsAndUpdate => _didFetchTagsAndUpdate;
|
||||||
bool _didFetchTagsAndUpdate = false;
|
bool _didFetchTagsAndUpdate = false;
|
||||||
|
|
||||||
|
/// Will be returned by [fetchTagsAndGetVersion] if not null.
|
||||||
|
final FlutterVersion? nextFlutterVersion;
|
||||||
|
|
||||||
|
@override
|
||||||
|
FlutterVersion fetchTagsAndGetVersion({
|
||||||
|
SystemClock clock = const SystemClock(),
|
||||||
|
}) {
|
||||||
|
_didFetchTagsAndUpdate = true;
|
||||||
|
return nextFlutterVersion ?? this;
|
||||||
|
}
|
||||||
|
|
||||||
bool get didCheckFlutterVersionFreshness => _didCheckFlutterVersionFreshness;
|
bool get didCheckFlutterVersionFreshness => _didCheckFlutterVersionFreshness;
|
||||||
bool _didCheckFlutterVersionFreshness = false;
|
bool _didCheckFlutterVersionFreshness = false;
|
||||||
|
|
||||||
@ -355,6 +369,9 @@ class FakeFlutterVersion implements FlutterVersion {
|
|||||||
return kUserBranch;
|
return kUserBranch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String flutterRoot;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String devToolsVersion;
|
final String devToolsVersion;
|
||||||
|
|
||||||
@ -385,16 +402,11 @@ class FakeFlutterVersion implements FlutterVersion {
|
|||||||
@override
|
@override
|
||||||
final String frameworkCommitDate;
|
final String frameworkCommitDate;
|
||||||
|
|
||||||
@override
|
|
||||||
String get frameworkDate => frameworkCommitDate;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final GitTagVersion gitTagVersion;
|
final GitTagVersion gitTagVersion;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void fetchTagsAndUpdate() {
|
FileSystem get fs => throw UnimplementedError('FakeFlutterVersion.fs is not implemented');
|
||||||
_didFetchTagsAndUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> checkFlutterVersionFreshness() async {
|
Future<void> checkFlutterVersionFreshness() async {
|
||||||
|
Loading…
Reference in New Issue
Block a user