bot_detector.dart 4.8 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:async';

7 8
import 'package:meta/meta.dart';

9
import '../persistent_tool_state.dart';
10 11
import 'io.dart';
import 'net.dart';
12
import 'platform.dart';
13 14 15

class BotDetector {
  BotDetector({
16 17 18
    required HttpClientFactory httpClientFactory,
    required Platform platform,
    required PersistentToolState persistentToolState,
19 20 21 22
  }) :
    _platform = platform,
    _azureDetector = AzureDetector(
      httpClientFactory: httpClientFactory,
23 24
    ),
    _persistentToolState = persistentToolState;
25 26

  final Platform _platform;
27
  final AzureDetector _azureDetector;
28
  final PersistentToolState _persistentToolState;
29 30

  Future<bool> get isRunningOnBot async {
31
    if (
32 33
      // Explicitly stated to not be a bot.
      _platform.environment['BOT'] == 'false'
34

35 36 37 38
      // 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')
39
    ) {
40 41
      _persistentToolState.setIsRunningOnBot(false);
      return false;
42 43
    }

44 45 46 47
    if (_persistentToolState.isRunningOnBot != null) {
      return _persistentToolState.isRunningOnBot!;
    }

48
    final bool result = _platform.environment['BOT'] == 'true'
49 50 51 52 53

      // 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
54

55 56
      // https://www.appveyor.com/docs/environment-variables/
      || _platform.environment.containsKey('APPVEYOR')
57

58 59
      // https://cirrus-ci.org/guide/writing-tasks/#environment-variables
      || _platform.environment.containsKey('CIRRUS_CI')
60

61 62 63
      // https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html
      || (_platform.environment.containsKey('AWS_REGION') &&
          _platform.environment.containsKey('CODEBUILD_INITIATOR'))
64

65 66
      // https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
      || _platform.environment.containsKey('JENKINS_URL')
67

68 69
      // https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
      || _platform.environment.containsKey('GITHUB_ACTIONS')
70

71 72 73 74
      // Properties on Flutter's Chrome Infra bots.
      || _platform.environment['CHROME_HEADLESS'] == '1'
      || _platform.environment.containsKey('BUILDBOT_BUILDERNAME')
      || _platform.environment.containsKey('SWARMING_TASK_ID')
75 76 77 78 79

      // Property when running on borg.
      || _platform.environment.containsKey('BORG_ALLOC_DIR')

      // Property when running on Azure.
80
      || await _azureDetector.isRunningOnAzure;
81 82 83

    _persistentToolState.setIsRunningOnBot(result);
    return result;
84 85 86
  }
}

87 88 89 90 91
// Are we running on Azure?
// https://docs.microsoft.com/en-us/azure/virtual-machines/linux/instance-metadata-service
@visibleForTesting
class AzureDetector {
  AzureDetector({
92
    required HttpClientFactory httpClientFactory,
93 94 95 96 97 98
  }) : _httpClientFactory = httpClientFactory;

  static const String _serviceUrl = 'http://169.254.169.254/metadata/instance';

  final HttpClientFactory _httpClientFactory;

99
  bool? _isRunningOnAzure;
100 101 102

  Future<bool> get isRunningOnAzure async {
    if (_isRunningOnAzure != null) {
103
      return _isRunningOnAzure!;
104
    }
105 106
    const Duration connectionTimeout = Duration(milliseconds: 250);
    const Duration requestTimeout = Duration(seconds: 1);
107
    final HttpClient client = _httpClientFactory()
108
      ..connectionTimeout = connectionTimeout;
109 110 111
    try {
      final HttpClientRequest request = await client.getUrl(
        Uri.parse(_serviceUrl),
112
      ).timeout(requestTimeout);
113 114 115
      request.headers.add('Metadata', true);
      await request.close();
    } on SocketException {
116
      // If there is an error on the socket, it probably means that we are not
117 118 119 120 121 122
      // 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;
123 124 125 126
    } 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;
127 128 129
    } on OSError {
      // The HttpClient might be running in a WSL1 environment.
      return _isRunningOnAzure = false;
130 131 132 133
    }
    // We got a response. We're running on Azure.
    return _isRunningOnAzure = true;
  }
134
}