Unverified Commit 74f08c73 authored by Anurag Roy's avatar Anurag Roy Committed by GitHub

[flutter_tools] Skip version freshness check for non-standard remotes (#97202)

parent a2424602
......@@ -229,74 +229,6 @@ class UpgradeCommandRunner {
}
}
/// Checks if the Flutter git repository is tracking a standard remote.
///
/// A "standard remote" is one having the same url as [globals.flutterGit].
/// The upgrade process only supports standard remotes.
///
/// Exits tool if the tracking remote is not standard.
void verifyStandardRemote(FlutterVersion localVersion) {
// If repositoryUrl of the local version is null, exit
final String? repositoryUrl = localVersion.repositoryUrl;
if (repositoryUrl == null) {
throwToolExit(
'Unable to upgrade Flutter: The tool could not determine the remote '
'upstream which is being tracked by the SDK.\n'
'Re-install Flutter by going to $_flutterInstallDocs.'
);
}
// Strip `.git` suffix before comparing the remotes
final String trackingUrl = stripDotGit(repositoryUrl);
final String flutterGitUrl = stripDotGit(globals.flutterGit);
// Exempt the official flutter git SSH remote from this check
if (trackingUrl == 'git@github.com:flutter/flutter') {
return;
}
if (trackingUrl != flutterGitUrl) {
if (globals.platform.environment.containsKey('FLUTTER_GIT_URL')) {
// If `FLUTTER_GIT_URL` is set, inform the user to either remove the
// `FLUTTER_GIT_URL` environment variable or set it to the current
// tracking remote to continue.
throwToolExit(
'Unable to upgrade Flutter: The Flutter SDK is tracking '
'"${localVersion.repositoryUrl}" but "FLUTTER_GIT_URL" is set to '
'"${globals.flutterGit}".\n'
'Either remove "FLUTTER_GIT_URL" from the environment or set it to '
'"${localVersion.repositoryUrl}", and retry. '
'Alternatively, re-install Flutter by going to $_flutterInstallDocs.\n'
'If this is intentional, it is recommended to use "git" directly to '
'keep Flutter SDK up-to date.'
);
}
// If `FLUTTER_GIT_URL` is unset, inform that the user has to set the
// environment variable to continue.
throwToolExit(
'Unable to upgrade Flutter: The Flutter SDK is tracking a non-standard '
'remote "${localVersion.repositoryUrl}".\n'
'Set the environment variable "FLUTTER_GIT_URL" to '
'"${localVersion.repositoryUrl}", and retry. '
'Alternatively, re-install Flutter by going to $_flutterInstallDocs.\n'
'If this is intentional, it is recommended to use "git" directly to '
'keep Flutter SDK up-to date.'
);
}
}
// Strips ".git" suffix from a given string, preferably an url.
// For example, changes 'https://github.com/flutter/flutter.git' to 'https://github.com/flutter/flutter'.
// URLs without ".git" suffix will remain unaffected.
String stripDotGit(String url) {
final RegExp pattern = RegExp(r'(.*)(\.git)$');
final RegExpMatch? match = pattern.firstMatch(url);
if (match == null) {
return url;
}
return match.group(1)!;
}
/// Returns the remote HEAD flutter version.
///
/// Exits tool if HEAD isn't pointing to a branch, or there is no upstream.
......@@ -337,7 +269,17 @@ class UpgradeCommandRunner {
throwToolExit(errorString);
}
}
verifyStandardRemote(localVersion);
// At this point the current checkout should be on HEAD of a branch having
// an upstream. Check whether this upstream is "standard".
final VersionCheckError? error = VersionUpstreamValidator(version: localVersion, platform: globals.platform).run();
if (error != null) {
throwToolExit(
'Unable to upgrade Flutter: '
'${error.message}\n'
'Reinstalling Flutter may fix this issue. Visit $_flutterInstallDocs '
'for instructions.'
);
}
return FlutterVersion(workingDirectory: workingDirectory, frameworkRevision: revision);
}
......
......@@ -8,6 +8,7 @@ import 'base/common.dart';
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/platform.dart';
import 'base/process.dart';
import 'base/time.dart';
import 'cache.dart';
......@@ -239,11 +240,15 @@ class FlutterVersion {
if (!kOfficialChannels.contains(channel)) {
return;
}
// Don't perform the update check if the tracking remote is not standard.
if (VersionUpstreamValidator(version: this, platform: globals.platform).run() != null) {
return;
}
DateTime localFrameworkCommitDate;
try {
// Don't perform the update check if fetching the latest local commit failed.
localFrameworkCommitDate = DateTime.parse(_latestGitCommitDate());
} on VersionCheckError {
// Don't perform the update check if the version check failed.
return;
}
final DateTime? latestFlutterCommitDate = await _getLatestAvailableFlutterDate();
......@@ -405,6 +410,91 @@ class FlutterVersion {
}
}
/// Checks if the provided [version] is tracking a standard remote.
///
/// A "standard remote" is one having the same url as(in order of precedence):
/// * The value of `FLUTTER_GIT_URL` environment variable.
/// * The HTTPS or SSH url of the Flutter repository as provided by GitHub.
///
/// To initiate the validation check, call [run].
///
/// This prevents the tool to check for version freshness from the standard
/// remote but fetch updates from the upstream of current branch/channel, both
/// of which can be different.
///
/// This also prevents unnecessary freshness check from a forked repo unless the
/// user explicitly configures the environment to do so.
class VersionUpstreamValidator {
VersionUpstreamValidator({
required this.version,
required this.platform,
});
final Platform platform;
final FlutterVersion version;
/// Performs the validation against the tracking remote of the [version].
///
/// Returns [VersionCheckError] if the tracking remote is not standard.
VersionCheckError? run(){
final String? flutterGit = platform.environment['FLUTTER_GIT_URL'];
final String? repositoryUrl = version.repositoryUrl;
if (repositoryUrl == null) {
return VersionCheckError(
'The tool could not determine the remote upstream which is being '
'tracked by the SDK.'
);
}
// Strip `.git` suffix before comparing the remotes
final List<String> sanitizedStandardRemotes = <String>[
if (flutterGit != null) flutterGit,
'https://github.com/flutter/flutter.git',
'git@github.com:flutter/flutter.git',
].map((String remote) => stripDotGit(remote)).toList();
final String sanitizedRepositoryUrl = stripDotGit(repositoryUrl);
if (!sanitizedStandardRemotes.contains(sanitizedRepositoryUrl)) {
if (platform.environment.containsKey('FLUTTER_GIT_URL')) {
// If `FLUTTER_GIT_URL` is set, inform to either remove the
// `FLUTTER_GIT_URL` environment variable or set it to the current
// tracking remote.
return VersionCheckError(
'The Flutter SDK is tracking "$repositoryUrl" but "FLUTTER_GIT_URL" '
'is set to "$flutterGit".\n'
'Either remove "FLUTTER_GIT_URL" from the environment or set it to '
'"$repositoryUrl". '
'If this is intentional, it is recommended to use "git" directly to '
'manage the SDK.'
);
}
// If `FLUTTER_GIT_URL` is unset, inform to set the environment variable.
return VersionCheckError(
'The Flutter SDK is tracking a non-standard remote "$repositoryUrl".\n'
'Set the environment variable "FLUTTER_GIT_URL" to '
'"$repositoryUrl". '
'If this is intentional, it is recommended to use "git" directly to '
'manage the SDK.'
);
}
return null;
}
// Strips ".git" suffix from a given string, preferably an url.
// For example, changes 'https://github.com/flutter/flutter.git' to 'https://github.com/flutter/flutter'.
// URLs without ".git" suffix will remain unaffected.
static final RegExp _patternUrlDotGit = RegExp(r'(.*)(\.git)$');
static String stripDotGit(String url) {
final RegExpMatch? match = _patternUrlDotGit.firstMatch(url);
if (match == null) {
return url;
}
return match.group(1)!;
}
}
/// Contains data and load/save logic pertaining to Flutter version checks.
@visibleForTesting
class VersionCheckStamp {
......
......@@ -239,130 +239,6 @@ void main() {
Platform: () => fakePlatform,
});
group('verifyStandardRemote', () {
const String flutterStandardUrlDotGit = 'https://github.com/flutter/flutter.git';
const String flutterNonStandardUrlDotGit = 'https://githubmirror.com/flutter/flutter.git';
const String flutterStandardSshUrl = 'git@github.com:flutter/flutter';
testUsingContext('throws toolExit if repository url is null', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
channel: 'beta',
repositoryUrl: null,
);
await expectLater(
() async => realCommandRunner.verifyStandardRemote(flutterVersion),
throwsToolExit(message: 'Unable to upgrade Flutter: The tool could not '
'determine the remote upstream which is being tracked by the SDK.\n'
'Re-install Flutter by going to https://flutter.dev/docs/get-started/install.'
),
);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator> {
ProcessManager: () => processManager,
Platform: () => fakePlatform,
});
testUsingContext('does not throw toolExit at standard remote url with FLUTTER_GIT_URL unset', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
channel: 'beta',
);
expect(() => realCommandRunner.verifyStandardRemote(flutterVersion), returnsNormally);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator> {
ProcessManager: () => processManager,
Platform: () => fakePlatform,
});
testUsingContext('throws toolExit at non-standard remote url with FLUTTER_GIT_URL unset', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
channel: 'beta',
repositoryUrl: flutterNonStandardUrlDotGit,
);
await expectLater(
() async => realCommandRunner.verifyStandardRemote(flutterVersion),
throwsToolExit(message: 'Unable to upgrade Flutter: The Flutter SDK '
'is tracking a non-standard remote "$flutterNonStandardUrlDotGit".\n'
'Set the environment variable "FLUTTER_GIT_URL" to '
'"$flutterNonStandardUrlDotGit", and retry. '
'Alternatively, re-install Flutter by going to '
'https://flutter.dev/docs/get-started/install.\n'
'If this is intentional, it is recommended to use "git" directly to '
'keep Flutter SDK up-to date.'
),
);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator> {
ProcessManager: () => processManager,
Platform: () => fakePlatform,
});
testUsingContext('does not throw toolExit at non-standard remote url with FLUTTER_GIT_URL set', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
channel: 'beta',
repositoryUrl: flutterNonStandardUrlDotGit,
);
expect(() => realCommandRunner.verifyStandardRemote(flutterVersion), returnsNormally);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator> {
ProcessManager: () => processManager,
Platform: () => fakePlatform..environment = Map<String, String>.unmodifiable(<String, String> {
'FLUTTER_GIT_URL': flutterNonStandardUrlDotGit,
}),
});
testUsingContext('throws toolExit at remote url and FLUTTER_GIT_URL set to different urls', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
channel: 'beta',
repositoryUrl: flutterNonStandardUrlDotGit,
);
await expectLater(
() async => realCommandRunner.verifyStandardRemote(flutterVersion),
throwsToolExit(message: 'Unable to upgrade Flutter: The Flutter SDK '
'is tracking "$flutterNonStandardUrlDotGit" but "FLUTTER_GIT_URL" '
'is set to "$flutterStandardUrlDotGit".\n'
'Either remove "FLUTTER_GIT_URL" from the environment or set it to '
'"$flutterNonStandardUrlDotGit", and retry. '
'Alternatively, re-install Flutter by going to '
'https://flutter.dev/docs/get-started/install.\n'
'If this is intentional, it is recommended to use "git" directly to '
'keep Flutter SDK up-to date.'
),
);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator> {
ProcessManager: () => processManager,
Platform: () => fakePlatform..environment = Map<String, String>.unmodifiable(<String, String> {
'FLUTTER_GIT_URL': flutterStandardUrlDotGit,
}),
});
testUsingContext('exempts standard ssh url from check with FLUTTER_GIT_URL unset', () async {
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
channel: 'beta',
repositoryUrl: flutterStandardSshUrl,
);
expect(() => realCommandRunner.verifyStandardRemote(flutterVersion), returnsNormally);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator> {
ProcessManager: () => processManager,
Platform: () => fakePlatform,
});
testUsingContext('stripDotGit removes ".git" suffix if any', () async {
expect(realCommandRunner.stripDotGit('https://github.com/flutter/flutter.git'), 'https://github.com/flutter/flutter');
expect(realCommandRunner.stripDotGit('https://github.com/flutter/flutter'), 'https://github.com/flutter/flutter');
expect(realCommandRunner.stripDotGit('git@github.com:flutter/flutter.git'), 'git@github.com:flutter/flutter');
expect(realCommandRunner.stripDotGit('git@github.com:flutter/flutter'), 'git@github.com:flutter/flutter');
expect(realCommandRunner.stripDotGit('https://githubmirror.com/flutter/flutter.git.git'), 'https://githubmirror.com/flutter/flutter.git');
expect(realCommandRunner.stripDotGit('https://githubmirror.com/flutter/flutter.gitgit'), 'https://githubmirror.com/flutter/flutter.gitgit');
});
});
testUsingContext('git exception during attemptReset throwsToolExit', () async {
const String revision = 'abc123';
const String errorMessage = 'fatal: Could not parse object ´$revision´';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment