Ian Hickson 9c7a9e779f
Give channel descriptions in flutter channel, use branch instead of upstream for channel name (#126936)
## How we determine the channel name

Historically, we used the current branch's upstream to figure out the current channel name. I have no idea why. I traced it back to https://github.com/flutter/flutter/pull/446/files where @abarth implement this and I reviewed that PR and left no comment on it at the time.

I think this is confusing. You can be on a branch and it tells you that your channel is different. That seems weird.

This PR changes the logic to uses the current branch as the channel name.

## How we display channels

The main reason this PR exists is to add channel descriptions to the `flutter channel` list:

```
ianh@burmese:~/dev/flutter/packages/flutter_tools$ flutter channel
Flutter channels:
  master (tip of tree, for contributors)
  main (tip of tree, follows master channel)
  beta (updated monthly, recommended for experienced users)
  stable (updated quarterly, for new users and for production app releases)
* foo_bar

Currently not on an official channel.
ianh@burmese:~/dev/flutter/packages/flutter_tools$
```

## Other changes

I made a few other changes while I was at it:

* If you're not on an official channel, we used to imply `--show-all`, but now we don't, we just show the official channels plus yours. This avoids flooding the screen in the case the user is on a weird channel and just wants to know what channel they're on.
* I made the tool more consistent about how it handles unofficial branches. Now it's always `[user branch]`.
* I slightly adjusted how unknown versions are rendered so it's clearer the version is unknown rather than just having the word "Unknown" floating in the output without context.
* Simplified some of the code.
* Made some of the tests more strict (checking all output rather than just some aspects of it).
* Changed the MockFlutterVersion to implement the FlutterVersion API more strictly.
* I made sure we escape the output to `.metadata` to avoid potential injection bugs (previously we just inlined the version and channel name verbatim with no escaping, which is super sketchy).
* Tweaked the help text for the `downgrade` command to be clearer.
* Removed some misleading text in some error messages.
* Made the `.metadata` generator consistent with the template file.
* Removed some obsolete code to do with the `dev` branch.

## Reviewer notes

I'm worried that there are implications to some of these changes that I am not aware of, so please don't assume I know what I'm doing when reviewing this code. :-)
2023-05-23 19:59:20 +00:00

701 lines
29 KiB
Dart

// Copyright 2014 The Flutter Authors. 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:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/time.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:test/fake.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/fake_process_manager.dart';
import '../src/fakes.dart' show FakeFlutterVersion;
final SystemClock _testClock = SystemClock.fixed(DateTime(2015));
final DateTime _stampUpToDate = _testClock.ago(VersionFreshnessValidator.checkAgeConsideredUpToDate ~/ 2);
final DateTime _stampOutOfDate = _testClock.ago(VersionFreshnessValidator.checkAgeConsideredUpToDate * 2);
void main() {
late FakeCache cache;
late FakeProcessManager processManager;
setUp(() {
processManager = FakeProcessManager.empty();
cache = FakeCache();
});
testUsingContext('Channel enum and string transform to each other', () {
for (final Channel channel in Channel.values) {
expect(getNameForChannel(channel), kOfficialChannels.toList()[channel.index]);
}
expect(kOfficialChannels.toList().map((String str) => getChannelForName(str)).toList(),
Channel.values);
});
for (final String channel in kOfficialChannels) {
DateTime getChannelUpToDateVersion() {
return _testClock.ago(VersionFreshnessValidator.versionAgeConsideredUpToDate(channel) ~/ 2);
}
DateTime getChannelOutOfDateVersion() {
return _testClock.ago(VersionFreshnessValidator.versionAgeConsideredUpToDate(channel) * 2);
}
group('$FlutterVersion for $channel', () {
setUpAll(() {
Cache.disableLocking();
VersionFreshnessValidator.timeToPauseToLetUserReadTheMessage = Duration.zero;
});
testUsingContext('prints nothing when Flutter installation looks fresh', () async {
const String flutterUpstreamUrl = 'https://github.com/flutter/flutter.git';
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',
),
FakeCommand(
command: const <String>['git', 'symbolic-ref', '--short', 'HEAD'],
stdout: channel,
),
FakeCommand(
command: const <String>['git', 'rev-parse', '--abbrev-ref', '--symbolic', '@{upstream}'],
stdout: 'origin/$channel',
),
const FakeCommand(
command: <String>['git', 'ls-remote', '--get-url', 'origin'],
stdout: flutterUpstreamUrl,
),
FakeCommand(
command: const <String>['git', '-c', 'log.showSignature=false', 'log', 'HEAD', '-n', '1', '--pretty=format:%ad', '--date=iso'],
stdout: getChannelUpToDateVersion().toString(),
),
const FakeCommand(
command: <String>['git', 'fetch', '--tags'],
),
FakeCommand(
command: const <String>['git', '-c', 'log.showSignature=false', 'log', '@{upstream}', '-n', '1', '--pretty=format:%ad', '--date=iso'],
stdout: getChannelOutOfDateVersion().toString(),
),
const FakeCommand(
command: <String>['git', '-c', 'log.showSignature=false', 'log', '-n', '1', '--pretty=format:%ar'],
stdout: '1 second ago',
),
FakeCommand(
command: const <String>['git', '-c', 'log.showSignature=false', 'log', 'HEAD', '-n', '1', '--pretty=format:%ad', '--date=iso'],
stdout: getChannelUpToDateVersion().toString(),
),
]);
final FlutterVersion flutterVersion = globals.flutterVersion;
await flutterVersion.checkFlutterVersionFreshness();
expect(flutterVersion.channel, channel);
expect(flutterVersion.repositoryUrl, flutterUpstreamUrl);
expect(flutterVersion.frameworkRevision, '1234abcd');
expect(flutterVersion.frameworkRevisionShort, '1234abcd');
expect(flutterVersion.frameworkVersion, '0.0.0-unknown');
expect(
flutterVersion.toString(),
'Flutter • channel $channel$flutterUpstreamUrl\n'
'Framework • revision 1234abcd (1 second ago) • ${getChannelUpToDateVersion()}\n'
'Engine • revision abcdefg\n'
'Tools • Dart 2.12.0 • DevTools 2.8.0',
);
expect(flutterVersion.frameworkAge, '1 second ago');
expect(flutterVersion.getVersionString(), '$channel/1234abcd');
expect(flutterVersion.getBranchName(), channel);
expect(flutterVersion.getVersionString(redactUnknownBranches: true), '$channel/1234abcd');
expect(flutterVersion.getBranchName(redactUnknownBranches: true), channel);
expect(testLogger.statusText, isEmpty);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(clock: _testClock),
ProcessManager: () => processManager,
Cache: () => cache,
});
testWithoutContext('prints nothing when Flutter installation looks out-of-date but is actually up-to-date', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
final VersionCheckStamp stamp = VersionCheckStamp(
lastTimeVersionWasChecked: _stampOutOfDate,
lastKnownRemoteVersion: getChannelOutOfDateVersion(),
);
cache.versionStamp = json.encode(stamp);
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelOutOfDateVersion(),
latestFlutterCommitDate: getChannelOutOfDateVersion(),
).run();
expect(logger.statusText, isEmpty);
});
testWithoutContext('does not ping server when version stamp is up-to-date', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
final VersionCheckStamp stamp = VersionCheckStamp(
lastTimeVersionWasChecked: _stampUpToDate,
lastKnownRemoteVersion: getChannelUpToDateVersion(),
);
cache.versionStamp = json.encode(stamp);
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelOutOfDateVersion(),
latestFlutterCommitDate: getChannelUpToDateVersion(),
).run();
expect(logger.statusText, contains('A new version of Flutter is available!'));
expect(cache.setVersionStamp, true);
});
testWithoutContext('does not print warning if printed recently', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
final VersionCheckStamp stamp = VersionCheckStamp(
lastTimeVersionWasChecked: _stampUpToDate,
lastKnownRemoteVersion: getChannelUpToDateVersion(),
lastTimeWarningWasPrinted: _testClock.now(),
);
cache.versionStamp = json.encode(stamp);
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelOutOfDateVersion(),
latestFlutterCommitDate: getChannelUpToDateVersion(),
).run();
expect(logger.statusText, isEmpty);
});
testWithoutContext('pings server when version stamp is missing', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
cache.versionStamp = '{}';
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelOutOfDateVersion(),
latestFlutterCommitDate: getChannelUpToDateVersion(),
).run();
expect(logger.statusText, contains('A new version of Flutter is available!'));
expect(cache.setVersionStamp, true);
});
testWithoutContext('pings server when version stamp is out-of-date', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
final VersionCheckStamp stamp = VersionCheckStamp(
lastTimeVersionWasChecked: _stampOutOfDate,
lastKnownRemoteVersion: _testClock.ago(const Duration(days: 2)),
);
cache.versionStamp = json.encode(stamp);
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelOutOfDateVersion(),
latestFlutterCommitDate: getChannelUpToDateVersion(),
).run();
expect(logger.statusText, contains('A new version of Flutter is available!'));
});
testWithoutContext('does not print warning when unable to connect to server if not out of date', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
cache.versionStamp = '{}';
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelUpToDateVersion(),
// latestFlutterCommitDate defaults to null because we failed to get remote version
).run();
expect(logger.statusText, isEmpty);
});
testWithoutContext('prints warning when unable to connect to server if really out of date', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(branch: channel);
final BufferLogger logger = BufferLogger.test();
final VersionCheckStamp stamp = VersionCheckStamp(
lastTimeVersionWasChecked: _stampOutOfDate,
lastKnownRemoteVersion: _testClock.ago(const Duration(days: 2)),
);
cache.versionStamp = json.encode(stamp);
await VersionFreshnessValidator(
version: flutterVersion,
cache: cache,
clock: _testClock,
logger: logger,
localFrameworkCommitDate: getChannelOutOfDateVersion(),
// latestFlutterCommitDate defaults to null because we failed to get remote version
).run();
final Duration frameworkAge = _testClock.now().difference(getChannelOutOfDateVersion());
expect(logger.statusText, contains('WARNING: your installation of Flutter is ${frameworkAge.inDays} days old.'));
});
group('$VersionCheckStamp for $channel', () {
void expectDefault(VersionCheckStamp stamp) {
expect(stamp.lastKnownRemoteVersion, isNull);
expect(stamp.lastTimeVersionWasChecked, isNull);
expect(stamp.lastTimeWarningWasPrinted, isNull);
}
testWithoutContext('loads blank when stamp file missing', () async {
cache.versionStamp = null;
expectDefault(await VersionCheckStamp.load(cache, BufferLogger.test()));
});
testWithoutContext('loads blank when stamp file is malformed JSON', () async {
cache.versionStamp = '<';
expectDefault(await VersionCheckStamp.load(cache, BufferLogger.test()));
});
testWithoutContext('loads blank when stamp file is well-formed but invalid JSON', () async {
cache.versionStamp = '[]';
expectDefault(await VersionCheckStamp.load(cache, BufferLogger.test()));
});
testWithoutContext('loads valid JSON', () async {
final String value = '''
{
"lastKnownRemoteVersion": "${_testClock.ago(const Duration(days: 1))}",
"lastTimeVersionWasChecked": "${_testClock.ago(const Duration(days: 2))}",
"lastTimeWarningWasPrinted": "${_testClock.now()}"
}
''';
cache.versionStamp = value;
final VersionCheckStamp stamp = await VersionCheckStamp.load(cache, BufferLogger.test());
expect(stamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1)));
expect(stamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2)));
expect(stamp.lastTimeWarningWasPrinted, _testClock.now());
});
});
});
}
group('VersionUpstreamValidator', () {
const String flutterStandardUrlDotGit = 'https://github.com/flutter/flutter.git';
const String flutterNonStandardUrlDotGit = 'https://githubmirror.com/flutter/flutter.git';
const String flutterStandardSshUrlDotGit = 'git@github.com:flutter/flutter.git';
VersionCheckError? runUpstreamValidator({
String? versionUpstreamUrl,
String? flutterGitUrl,
}){
final Platform testPlatform = FakePlatform(environment: <String, String> {
if (flutterGitUrl != null) 'FLUTTER_GIT_URL': flutterGitUrl,
});
return VersionUpstreamValidator(
version: FakeFlutterVersion(repositoryUrl: versionUpstreamUrl),
platform: testPlatform,
).run();
}
testWithoutContext('returns error if repository url is null', () {
final VersionCheckError error = runUpstreamValidator(
// repositoryUrl is null by default
)!;
expect(error, isNotNull);
expect(
error.message,
contains('The tool could not determine the remote upstream which is being tracked by the SDK.'),
);
});
testWithoutContext('does not return error at standard remote url with FLUTTER_GIT_URL unset', () {
expect(runUpstreamValidator(versionUpstreamUrl: flutterStandardUrlDotGit), isNull);
});
testWithoutContext('returns error at non-standard remote url with FLUTTER_GIT_URL unset', () {
final VersionCheckError error = runUpstreamValidator(versionUpstreamUrl: flutterNonStandardUrlDotGit)!;
expect(error, isNotNull);
expect(
error.message,
contains(
'The Flutter SDK is tracking a non-standard remote "$flutterNonStandardUrlDotGit".\n'
'Set the environment variable "FLUTTER_GIT_URL" to "$flutterNonStandardUrlDotGit". '
'If this is intentional, it is recommended to use "git" directly to manage the SDK.'
),
);
});
testWithoutContext('does not return error at non-standard remote url with FLUTTER_GIT_URL set', () {
expect(runUpstreamValidator(
versionUpstreamUrl: flutterNonStandardUrlDotGit,
flutterGitUrl: flutterNonStandardUrlDotGit,
), isNull);
});
testWithoutContext('respects FLUTTER_GIT_URL even if upstream remote url is standard', () {
final VersionCheckError error = runUpstreamValidator(
versionUpstreamUrl: flutterStandardUrlDotGit,
flutterGitUrl: flutterNonStandardUrlDotGit,
)!;
expect(error, isNotNull);
expect(
error.message,
contains(
'The Flutter SDK is tracking "$flutterStandardUrlDotGit" but "FLUTTER_GIT_URL" is set to "$flutterNonStandardUrlDotGit".\n'
'Either remove "FLUTTER_GIT_URL" from the environment or set it to "$flutterStandardUrlDotGit". '
'If this is intentional, it is recommended to use "git" directly to manage the SDK.'
),
);
});
testWithoutContext('does not return error at standard ssh url with FLUTTER_GIT_URL unset', () {
expect(runUpstreamValidator(versionUpstreamUrl: flutterStandardSshUrlDotGit), isNull);
});
testWithoutContext('stripDotGit removes ".git" suffix if any', () {
expect(VersionUpstreamValidator.stripDotGit('https://github.com/flutter/flutter.git'), 'https://github.com/flutter/flutter');
expect(VersionUpstreamValidator.stripDotGit('https://github.com/flutter/flutter'), 'https://github.com/flutter/flutter');
expect(VersionUpstreamValidator.stripDotGit('git@github.com:flutter/flutter.git'), 'git@github.com:flutter/flutter');
expect(VersionUpstreamValidator.stripDotGit('git@github.com:flutter/flutter'), 'git@github.com:flutter/flutter');
expect(VersionUpstreamValidator.stripDotGit('https://githubmirror.com/flutter/flutter.git.git'), 'https://githubmirror.com/flutter/flutter.git');
expect(VersionUpstreamValidator.stripDotGit('https://githubmirror.com/flutter/flutter.gitgit'), 'https://githubmirror.com/flutter/flutter.gitgit');
});
});
testUsingContext('version handles unknown branch', () 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',
),
]);
final FlutterVersion flutterVersion = globals.flutterVersion;
expect(flutterVersion.channel, '[user-branch]');
expect(flutterVersion.getVersionString(), 'feature-branch/1234abcd');
expect(flutterVersion.getBranchName(), 'feature-branch');
expect(flutterVersion.getVersionString(redactUnknownBranches: true), '[user-branch]/1234abcd');
expect(flutterVersion.getBranchName(redactUnknownBranches: true), '[user-branch]');
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
FlutterVersion: () => FlutterVersion(clock: _testClock),
ProcessManager: () => processManager,
Cache: () => cache,
});
testUsingContext('GitTagVersion', () {
const String hash = 'abcdef';
GitTagVersion gitTagVersion;
// Master channel
gitTagVersion = GitTagVersion.parse('1.2.0-4.5.pre-13-g$hash');
expect(gitTagVersion.frameworkVersionFor(hash), '1.2.0-5.0.pre.13');
expect(gitTagVersion.gitTag, '1.2.0-4.5.pre');
expect(gitTagVersion.devVersion, 4);
expect(gitTagVersion.devPatch, 5);
// Stable channel
gitTagVersion = GitTagVersion.parse('1.2.3');
expect(gitTagVersion.frameworkVersionFor(hash), '1.2.3');
expect(gitTagVersion.x, 1);
expect(gitTagVersion.y, 2);
expect(gitTagVersion.z, 3);
expect(gitTagVersion.devVersion, null);
expect(gitTagVersion.devPatch, null);
// Beta channel
gitTagVersion = GitTagVersion.parse('1.2.3-4.5.pre');
expect(gitTagVersion.frameworkVersionFor(hash), '1.2.3-4.5.pre');
expect(gitTagVersion.gitTag, '1.2.3-4.5.pre');
expect(gitTagVersion.devVersion, 4);
expect(gitTagVersion.devPatch, 5);
gitTagVersion = GitTagVersion.parse('1.2.3-13-g$hash');
expect(gitTagVersion.frameworkVersionFor(hash), '1.2.4-0.0.pre.13');
expect(gitTagVersion.gitTag, '1.2.3');
expect(gitTagVersion.devVersion, null);
expect(gitTagVersion.devPatch, null);
// new tag release format, beta channel
gitTagVersion = GitTagVersion.parse('1.2.3-4.5.pre-0-g$hash');
expect(gitTagVersion.frameworkVersionFor(hash), '1.2.3-4.5.pre');
expect(gitTagVersion.gitTag, '1.2.3-4.5.pre');
expect(gitTagVersion.devVersion, 4);
expect(gitTagVersion.devPatch, 5);
// new tag release format, stable channel
gitTagVersion = GitTagVersion.parse('1.2.3-13-g$hash');
expect(gitTagVersion.frameworkVersionFor(hash), '1.2.4-0.0.pre.13');
expect(gitTagVersion.gitTag, '1.2.3');
expect(gitTagVersion.devVersion, null);
expect(gitTagVersion.devPatch, null);
expect(GitTagVersion.parse('98.76.54-32-g$hash').frameworkVersionFor(hash), '98.76.55-0.0.pre.32');
expect(GitTagVersion.parse('10.20.30-0-g$hash').frameworkVersionFor(hash), '10.20.30');
expect(testLogger.traceText, '');
expect(GitTagVersion.parse('v1.2.3+hotfix.1-4-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
expect(GitTagVersion.parse('x1.2.3-4-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
expect(GitTagVersion.parse('1.0.0-unknown-0-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
expect(GitTagVersion.parse('beta-1-g$hash').frameworkVersionFor(hash), '0.0.0-unknown');
expect(GitTagVersion.parse('1.2.3-4-gx$hash').frameworkVersionFor(hash), '0.0.0-unknown');
expect(testLogger.statusText, '');
expect(testLogger.errorText, '');
expect(
testLogger.traceText,
'Could not interpret results of "git describe": v1.2.3+hotfix.1-4-gabcdef\n'
'Could not interpret results of "git describe": x1.2.3-4-gabcdef\n'
'Could not interpret results of "git describe": 1.0.0-unknown-0-gabcdef\n'
'Could not interpret results of "git describe": beta-1-gabcdef\n'
'Could not interpret results of "git describe": 1.2.3-4-gxabcdef\n',
);
});
testUsingContext('determine reports correct stable version if HEAD is at a tag', () {
const String stableTag = '1.2.3';
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
stdout: stableTag,
),
],
);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform();
final GitTagVersion gitTagVersion = GitTagVersion.determine(processUtils, platform, workingDirectory: '.');
expect(gitTagVersion.frameworkVersionFor('abcd1234'), stableTag);
});
testUsingContext('determine favors stable tag over beta tag if both identify HEAD', () {
const String stableTag = '1.2.3';
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
// This tests the unlikely edge case where a beta release made it to stable without any cherry picks
stdout: '1.2.3-6.0.pre\n$stableTag',
),
],
);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform();
final GitTagVersion gitTagVersion = GitTagVersion.determine(processUtils, platform, workingDirectory: '.');
expect(gitTagVersion.frameworkVersionFor('abcd1234'), stableTag);
});
testUsingContext('determine reports correct git describe version if HEAD is not at a tag', () {
const String devTag = '1.2.0-2.0.pre';
const String headRevision = 'abcd1234';
const String commitsAhead = '12';
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
// no output, since there's no tag
),
const FakeCommand(
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
stdout: '$devTag-$commitsAhead-g$headRevision',
),
],
);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform();
final GitTagVersion gitTagVersion = GitTagVersion.determine(processUtils, platform, workingDirectory: '.');
// reported version should increment the m
expect(gitTagVersion.frameworkVersionFor(headRevision), '1.2.0-3.0.pre.12');
});
testUsingContext('determine does not call fetch --tags', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
),
const FakeCommand(
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
stdout: 'v0.1.2-3-1234abcd',
),
]);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform();
GitTagVersion.determine(processUtils, platform, workingDirectory: '.');
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testUsingContext('determine does not fetch tags on beta', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
stdout: 'beta',
),
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
),
const FakeCommand(
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
stdout: 'v0.1.2-3-1234abcd',
),
]);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform();
GitTagVersion.determine(processUtils, platform, workingDirectory: '.', fetchTags: true);
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testUsingContext('determine calls fetch --tags on master', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
stdout: 'master',
),
const FakeCommand(
command: <String>['git', 'fetch', 'https://github.com/flutter/flutter.git', '--tags', '-f'],
),
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
),
const FakeCommand(
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
stdout: 'v0.1.2-3-1234abcd',
),
]);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform();
GitTagVersion.determine(processUtils, platform, workingDirectory: '.', fetchTags: true);
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testUsingContext('determine uses overridden git url', () {
final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['git', 'symbolic-ref', '--short', 'HEAD'],
stdout: 'master',
),
const FakeCommand(
command: <String>['git', 'fetch', 'https://githubmirror.com/flutter.git', '--tags', '-f'],
),
const FakeCommand(
command: <String>['git', 'tag', '--points-at', 'HEAD'],
),
const FakeCommand(
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
stdout: 'v0.1.2-3-1234abcd',
),
]);
final ProcessUtils processUtils = ProcessUtils(
processManager: fakeProcessManager,
logger: BufferLogger.test(),
);
final FakePlatform platform = FakePlatform(
environment: <String, String> {'FLUTTER_GIT_URL': 'https://githubmirror.com/flutter.git'},
);
GitTagVersion.determine(processUtils, platform, workingDirectory: '.', fetchTags: true);
expect(fakeProcessManager, hasNoRemainingExpectations);
});
}
class FakeCache extends Fake implements Cache {
String? versionStamp;
bool setVersionStamp = false;
@override
String get engineRevision => 'abcdefg';
@override
String get devToolsVersion => '2.8.0';
@override
String get dartSdkVersion => '2.12.0';
@override
void checkLockAcquired() { }
@override
String? getStampFor(String artifactName) {
if (artifactName == VersionCheckStamp.flutterVersionCheckStampFile) {
return versionStamp;
}
return null;
}
@override
void setStampFor(String artifactName, String version) {
if (artifactName == VersionCheckStamp.flutterVersionCheckStampFile) {
setVersionStamp = true;
}
}
}