Makes switching channels remove version freshness stamp. (#21182)

When switching between channels, we were leaving around the version freshness stamp file (bin/cache/flutter_version_check.stamp), which meant that the flutter tool would read from that file to see what the cached date of the most recent commit to the current channel (branch) was. The problem was that since the file was created while on the previous channel, the cached date was for the wrong channel, so if you switch from master to beta, flutter would think that the channel was out of date, and a new version was available, at least for three days after the first time it checked (after three days since the last time the freshness was checked, the cached date would get updated).

This PR modifies the channel command to remove that stamp file whenever the user switches channels, so that the cached date will be from the right channel when it is recreated.

Fixes #21134
This commit is contained in:
Greg Spencer 2018-08-29 21:53:39 -07:00 committed by GitHub
parent 8f8af99983
commit 7cebaac985
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 21 deletions

View file

@ -126,7 +126,12 @@ class ChannelCommand extends FlutterCommand {
);
}
}
if (result != 0)
if (result != 0) {
throwToolExit('Switching channels failed with error code $result.', exitCode: result);
} else {
// Remove the version check stamp, since it could contain out-of-date
// information that pertains to the previous channel.
await FlutterVersion.resetFlutterVersionFreshnessCheck();
}
}
}

View file

@ -228,6 +228,22 @@ class FlutterVersion {
@visibleForTesting
static Duration timeToPauseToLetUserReadTheMessage = const Duration(seconds: 2);
/// Reset the version freshness information by removing the stamp file.
///
/// New version freshness information will be regenerated when
/// [checkFlutterVersionFreshness] is called after this. This is typically
/// used when switching channels so that stale information from another
/// channel doesn't linger.
static Future<Null> resetFlutterVersionFreshnessCheck() async {
try {
await Cache.instance.getStampFileFor(
VersionCheckStamp.kFlutterVersionCheckStampFile,
).delete();
} on FileSystemException {
// Ignore, since we don't mind if the file didn't exist in the first place.
}
}
/// Checks if the currently installed version of Flutter is up-to-date, and
/// warns the user if it isn't.
///

View file

@ -4,17 +4,35 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:io' hide File;
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/channel.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'src/common.dart';
import 'src/context.dart';
Process createMockProcess({int exitCode = 0, String stdout = '', String stderr = ''}) {
final Stream<List<int>> stdoutStream = new Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(stdout),
]);
final Stream<List<int>> stderrStream = new Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(stderr),
]);
final Process process = new MockProcess();
when(process.stdout).thenAnswer((_) => stdoutStream);
when(process.stderr).thenAnswer((_) => stderrStream);
when(process.exitCode).thenAnswer((_) => new Future<int>.value(exitCode));
return process;
}
void main() {
group('channel', () {
final MockProcessManager mockProcessManager = new MockProcessManager();
@ -35,32 +53,26 @@ void main() {
});
testUsingContext('removes duplicates', () async {
final Stream<List<int>> stdout = new Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(
'origin/dev\n'
'origin/beta\n'
'upstream/dev\n'
'upstream/beta\n'
),
]);
final Process process = new MockProcess();
when(process.stdout).thenAnswer((_) => stdout);
when(process.stderr).thenAnswer((_) => const Stream<List<int>>.empty());
when(process.exitCode).thenAnswer((_) => new Future<int>.value(0));
final Process process = createMockProcess(
stdout: 'origin/dev\n'
'origin/beta\n'
'upstream/dev\n'
'upstream/beta\n');
when(mockProcessManager.start(
<String>['git', 'branch', '-r'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
.thenAnswer((_) => new Future<Process>.value(process));
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(process));
final ChannelCommand command = new ChannelCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['channel']);
verify(mockProcessManager.start(<String>['git', 'branch', '-r'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'))).called(1);
verify(mockProcessManager.start(
<String>['git', 'branch', '-r'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
expect(testLogger.errorText, hasLength(0));
@ -73,7 +85,112 @@ void main() {
expect(rows, <String>['dev', 'beta']);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
ProcessManager: () => mockProcessManager,
});
testUsingContext('can switch channels', () async {
when(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
final ChannelCommand command = new ChannelCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['channel', 'beta']);
verify(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
expect(testLogger.statusText, contains("Switching to flutter channel 'beta'..."));
expect(testLogger.errorText, hasLength(0));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
FileSystem: () => new MemoryFileSystem(),
});
// This verifies that bug https://github.com/flutter/flutter/issues/21134
// doesn't return.
testUsingContext('removes version stamp file when switching channels', () async {
when(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
final File versionCheckFile = Cache.instance.getStampFileFor(
VersionCheckStamp.kFlutterVersionCheckStampFile,
);
/// Create a bogus "leftover" version check file to make sure it gets
/// removed when the channel changes. The content doesn't matter.
versionCheckFile.createSync(recursive: true);
versionCheckFile.writeAsStringSync('''
{
"lastTimeVersionWasChecked": "2151-08-29 10:17:30.763802",
"lastKnownRemoteVersion": "2151-09-26 15:56:19.000Z"
}
''');
final ChannelCommand command = new ChannelCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['channel', 'beta']);
verify(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
expect(testLogger.statusText, isNot(contains('A new version of Flutter')));
expect(testLogger.errorText, hasLength(0));
expect(versionCheckFile.existsSync(), isFalse);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
FileSystem: () => new MemoryFileSystem(),
});
});
}