diff --git a/apps/cli/lib/src/cli/cli.dart b/apps/cli/lib/src/cli/cli.dart index 0b8abd9c8..6d35880e2 100644 --- a/apps/cli/lib/src/cli/cli.dart +++ b/apps/cli/lib/src/cli/cli.dart @@ -89,6 +89,8 @@ final class Cli { ? const SentryPerformance() : const CelestPerformance(); ctx.storage = storage ?? Storage(); + ctx.connectionMonitor.init(); + await ctx.connectionMonitor.stream.first; try { _logger.finest('Initializing secure storage'); @@ -401,6 +403,7 @@ final class Cli { await _logFile?.close(); await ctx.performance.close(); ctx.httpClient.close(); + ctx.connectionMonitor.close(); exit(exitCode); } } diff --git a/apps/cli/lib/src/context.dart b/apps/cli/lib/src/context.dart index e4a368b0b..6844c1a7e 100644 --- a/apps/cli/lib/src/context.dart +++ b/apps/cli/lib/src/context.dart @@ -16,6 +16,7 @@ import 'package:celest_cli/src/project/project_paths.dart'; import 'package:celest_cli/src/serialization/json_generator.dart'; import 'package:celest_cli/src/storage/storage.dart'; import 'package:celest_cli/src/types/type_helper.dart'; +import 'package:celest_cli/src/utils/connection_monitor.dart'; import 'package:celest_cli/src/version.dart'; import 'package:celest_cloud/celest_cloud.dart'; import 'package:file/file.dart'; @@ -223,3 +224,5 @@ NativeSecureStorage secureStorage = storage.secure; /// The isolated [secureStorage] interface. IsolatedNativeStorage get isolatedSecureStorage => secureStorage.isolated; + +ConnectionMonitor connectionMonitor = ConnectionMonitor(); diff --git a/apps/cli/lib/src/pub/pub_action.dart b/apps/cli/lib/src/pub/pub_action.dart index ed3533169..dc12b02e8 100644 --- a/apps/cli/lib/src/pub/pub_action.dart +++ b/apps/cli/lib/src/pub/pub_action.dart @@ -54,7 +54,13 @@ Future runPub({ ? Sdk.current.dart : (await celestProject.determineProjectType()).executable; - final command = [exe, 'pub', action.name, if (verbose) '--verbose']; + final command = [ + exe, + 'pub', + action.name, + if (verbose) '--verbose', + if (!connectionMonitor.isConnected) '--offline', + ]; final logger = Logger(command.join(' ')); logger.fine('Running `${command.join(' ')}` in "$workingDirectory"...'); final process = await processManager.start( diff --git a/apps/cli/lib/src/utils/connection_monitor.dart b/apps/cli/lib/src/utils/connection_monitor.dart new file mode 100644 index 000000000..5c2486f50 --- /dev/null +++ b/apps/cli/lib/src/utils/connection_monitor.dart @@ -0,0 +1,50 @@ +import 'dart:async'; + +import 'package:celest_cli/src/context.dart'; + +/// Monitors the local connection to the Internet. +final class ConnectionMonitor { + /// We use pub.dev to check if an Internet connection is available generally + /// since this is mostly used to determine if we need to pass `--offline` + /// to pub commands. + static final Uri _checkUri = Uri.parse('https://pub.dev/'); + + final _connectionStream = StreamController.broadcast(); + + bool _isConnected = true; + bool get isConnected => _isConnected; + Stream get stream => _connectionStream.stream; + + void init() { + _checkConnection(); + Timer.periodic(const Duration(seconds: 5), (timer) { + if (_connectionStream.isClosed) { + timer.cancel(); + return; + } + _checkConnection(); + }); + } + + Future _checkConnection() async { + final status = await checkConnection(); + _isConnected = status; + if (!_connectionStream.isClosed) { + _connectionStream.add(status); + } + } + + Future checkConnection() async { + try { + final result = + await httpClient.head(_checkUri).timeout(const Duration(seconds: 1)); + return result.statusCode == 200; + } on Object { + return false; + } + } + + void close() { + _connectionStream.close(); + } +}