| // 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:async'; |
| |
| import 'package:meta/meta.dart'; |
| |
| import '../persistent_tool_state.dart'; |
| import 'io.dart'; |
| import 'net.dart'; |
| import 'platform.dart'; |
| |
| class BotDetector { |
| BotDetector({ |
| required HttpClientFactory httpClientFactory, |
| required Platform platform, |
| required PersistentToolState persistentToolState, |
| }) : |
| _platform = platform, |
| _azureDetector = AzureDetector( |
| httpClientFactory: httpClientFactory, |
| ), |
| _persistentToolState = persistentToolState; |
| |
| final Platform _platform; |
| final AzureDetector _azureDetector; |
| final PersistentToolState _persistentToolState; |
| |
| Future<bool> get isRunningOnBot async { |
| if ( |
| // Explicitly stated to not be a bot. |
| _platform.environment['BOT'] == 'false' |
| |
| // Set by the IDEs to the IDE name, so a strong signal that this is not a bot. |
| || _platform.environment.containsKey('FLUTTER_HOST') |
| // When set, GA logs to a local file (normally for tests) so we don't need to filter. |
| || _platform.environment.containsKey('FLUTTER_ANALYTICS_LOG_FILE') |
| ) { |
| _persistentToolState.setIsRunningOnBot(false); |
| return false; |
| } |
| |
| if (_persistentToolState.isRunningOnBot != null) { |
| return _persistentToolState.isRunningOnBot!; |
| } |
| |
| final bool result = _platform.environment['BOT'] == 'true' |
| |
| // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables |
| || _platform.environment['TRAVIS'] == 'true' |
| || _platform.environment['CONTINUOUS_INTEGRATION'] == 'true' |
| || _platform.environment.containsKey('CI') // Travis and AppVeyor |
| |
| // https://www.appveyor.com/docs/environment-variables/ |
| || _platform.environment.containsKey('APPVEYOR') |
| |
| // https://cirrus-ci.org/guide/writing-tasks/#environment-variables |
| || _platform.environment.containsKey('CIRRUS_CI') |
| |
| // https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html |
| || (_platform.environment.containsKey('AWS_REGION') && |
| _platform.environment.containsKey('CODEBUILD_INITIATOR')) |
| |
| // https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables |
| || _platform.environment.containsKey('JENKINS_URL') |
| |
| // https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables |
| || _platform.environment.containsKey('GITHUB_ACTIONS') |
| |
| // Properties on Flutter's Chrome Infra bots. |
| || _platform.environment['CHROME_HEADLESS'] == '1' |
| || _platform.environment.containsKey('BUILDBOT_BUILDERNAME') |
| || _platform.environment.containsKey('SWARMING_TASK_ID') |
| |
| // Property when running on borg. |
| || _platform.environment.containsKey('BORG_ALLOC_DIR') |
| |
| // Property when running on Azure. |
| || await _azureDetector.isRunningOnAzure; |
| |
| _persistentToolState.setIsRunningOnBot(result); |
| return result; |
| } |
| } |
| |
| // Are we running on Azure? |
| // https://docs.microsoft.com/en-us/azure/virtual-machines/linux/instance-metadata-service |
| @visibleForTesting |
| class AzureDetector { |
| AzureDetector({ |
| required HttpClientFactory httpClientFactory, |
| }) : _httpClientFactory = httpClientFactory; |
| |
| static const String _serviceUrl = 'http://169.254.169.254/metadata/instance'; |
| |
| final HttpClientFactory _httpClientFactory; |
| |
| bool? _isRunningOnAzure; |
| |
| Future<bool> get isRunningOnAzure async { |
| if (_isRunningOnAzure != null) { |
| return _isRunningOnAzure!; |
| } |
| const Duration connectionTimeout = Duration(milliseconds: 250); |
| const Duration requestTimeout = Duration(seconds: 1); |
| final HttpClient client = _httpClientFactory() |
| ..connectionTimeout = connectionTimeout; |
| try { |
| final HttpClientRequest request = await client.getUrl( |
| Uri.parse(_serviceUrl), |
| ).timeout(requestTimeout); |
| request.headers.add('Metadata', true); |
| await request.close(); |
| } on SocketException { |
| // If there is an error on the socket, it probably means that we are not |
| // running on Azure. |
| return _isRunningOnAzure = false; |
| } on HttpException { |
| // If the connection gets set up, but encounters an error condition, it |
| // still means we're on Azure. |
| return _isRunningOnAzure = true; |
| } on TimeoutException { |
| // The HttpClient connected to a host, but it did not respond in a timely |
| // fashion. Assume we are not on a bot. |
| return _isRunningOnAzure = false; |
| } on OSError { |
| // The HttpClient might be running in a WSL1 environment. |
| return _isRunningOnAzure = false; |
| } |
| // We got a response. We're running on Azure. |
| return _isRunningOnAzure = true; |
| } |
| } |