Skip to content

Commit de0a2ce

Browse files
uemanmarandaneto
andauthored
Fix: Read all environment variables in sentry (#375)
* Correctly read environment variables * Changelog * Remove env vars from sentry_flutter * Docs & Changelog * Calm Linter * Hopefully fix failing tests * Implement PR feedback * Another try at fixing the failing tests * Update CHANGELOG.md Co-authored-by: Manoel Aranda Neto <[email protected]> * Fix: Set `SentryOptions.debug` in sentry (#376) * Fix: Set `SentryOptions.debug` in sentry_dart * Update CHANGELOG.md Co-authored-by: Manoel Aranda Neto <[email protected]> Co-authored-by: Manoel Aranda Neto <[email protected]> * Only override release and dist if not existing * Apply PR feedback * add teardown * Fix path * PR feedback Co-authored-by: Manoel Aranda Neto <[email protected]> Co-authored-by: Manoel Aranda Neto <[email protected]>
1 parent e37743e commit de0a2ce

11 files changed

+227
-48
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* Bump: sentry-cocoa to v6.2.1 (#360)
88
* Feat: Migration from `package_info` to `package_info_plus` plugin (#370)
99
* Fix: missing event.origin and event.environment tags for non-native events on iOS (#369)
10+
* Fix: Set `SentryOptions.debug` in `sentry` (#376)
11+
* Fix: Read all environment variables in `sentry` (#375)
1012

1113
## Breaking Changes:
1214

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'platform_checker.dart';
2+
3+
/// Reads environment variables from the system.
4+
/// In an Flutter environment these can be set via
5+
/// `flutter build --dart-define=VARIABLE_NAME=VARIABLE_VALUE`.
6+
class EnvironmentVariables {
7+
static const _sentryEnvironment = 'SENTRY_ENVIRONMENT';
8+
static const _sentryDsn = 'SENTRY_DSN';
9+
static const _sentryRelease = 'SENTRY_RELEASE';
10+
static const _sentryDist = 'SENTRY_DSN';
11+
12+
/// `SENTRY_ENVIRONMENT`
13+
/// See [SentryOptions.environment]
14+
String? get environment => const bool.hasEnvironment(_sentryEnvironment)
15+
? const String.fromEnvironment(_sentryEnvironment)
16+
: null;
17+
18+
/// `SENTRY_DSN`
19+
/// See [SentryOptions.dsn]
20+
String? get dsn => const bool.hasEnvironment(_sentryDsn)
21+
? const String.fromEnvironment(_sentryDsn)
22+
: null;
23+
24+
// `SENTRY_RELEASE`
25+
/// See [SentryOptions.release]
26+
String? get release => const bool.hasEnvironment(_sentryRelease)
27+
? const String.fromEnvironment(_sentryRelease)
28+
: null;
29+
30+
/// `SENTRY_DIST`
31+
/// See [SentryOptions.dist]
32+
String? get dist => const bool.hasEnvironment(_sentryDist)
33+
? const String.fromEnvironment(_sentryDist)
34+
: null;
35+
36+
/// Returns an environment based on the compilation mode of Dart or Flutter.
37+
/// This can be set as [SentryOptions.environment]
38+
String environmentForMode(PlatformChecker checker) {
39+
// We infer the enviroment based on the release/non-release and profile
40+
// constants.
41+
42+
if (checker.isReleaseMode()) {
43+
return 'production';
44+
}
45+
if (checker.isProfileMode()) {
46+
return 'profile';
47+
}
48+
return 'debug';
49+
}
50+
}

dart/lib/src/sentry.dart

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:async';
22

33
import 'default_integrations.dart';
4+
import 'environment_variables.dart';
45
import 'hub.dart';
56
import 'hub_adapter.dart';
67
import 'noop_isolate_error_integration.dart'
@@ -52,24 +53,12 @@ class Sentry {
5253
}
5354

5455
static Future<void> _initDefaultValues(
55-
SentryOptions options, AppRunner? appRunner) async {
56-
// We infer the enviroment based on the release/non-release and profile
57-
// constants.
58-
var environment = options.platformChecker.isReleaseMode()
59-
? defaultEnvironment
60-
: options.platformChecker.isProfileMode()
61-
? 'profile'
62-
: 'debug';
63-
64-
// if the SENTRY_ENVIRONMENT is set, we read from it.
65-
options.environment = const bool.hasEnvironment('SENTRY_ENVIRONMENT')
66-
? const String.fromEnvironment('SENTRY_ENVIRONMENT')
67-
: environment;
68-
69-
// if the SENTRY_DSN is set, we read from it.
70-
options.dsn = const bool.hasEnvironment('SENTRY_DSN')
71-
? const String.fromEnvironment('SENTRY_DSN')
72-
: options.dsn;
56+
SentryOptions options,
57+
AppRunner? appRunner,
58+
) async {
59+
options.debug = options.platformChecker.isDebugMode();
60+
61+
_setEnvironmentVariables(options);
7362

7463
// Throws when running on the browser
7564
if (!isWeb) {
@@ -79,6 +68,25 @@ class Sentry {
7968
}
8069
}
8170

71+
/// This method reads available environment variables and uses them
72+
/// accordingly.
73+
/// To see which environment variables are available, see [EnvironmentVariables]
74+
///
75+
/// The precendence of these options are also described on
76+
/// https://docs.sentry.io/platforms/dart/configuration/options/
77+
static void _setEnvironmentVariables(SentryOptions options) {
78+
final vars = options.environmentVariables;
79+
options.dsn = options.dsn ?? vars.dsn;
80+
81+
if (options.environment == null) {
82+
var environment = vars.environmentForMode(options.platformChecker);
83+
options.environment = vars.environment ?? environment;
84+
}
85+
86+
options.release = options.release ?? vars.release;
87+
options.dist = options.dist ?? vars.dist;
88+
}
89+
8290
/// Initializes the SDK
8391
static Future<void> _init(SentryOptions options, AppRunner? appRunner) async {
8492
if (isEnabled) {

dart/lib/src/sentry_options.dart

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:async';
33
import 'package:http/http.dart';
44

55
import 'diagnostic_logger.dart';
6+
import 'environment_variables.dart';
67
import 'integration.dart';
78
import 'noop_client.dart';
89
import 'protocol.dart';
@@ -12,8 +13,9 @@ import 'utils.dart';
1213
import 'version.dart';
1314
import 'platform_checker.dart';
1415

15-
/// Default Environment is none is set
16-
const defaultEnvironment = 'production';
16+
// TODO: Scope observers, enableScopeSync
17+
// TODO: shutdownTimeout, flushTimeoutMillis
18+
// https://api.dart.dev/stable/2.10.2/dart-io/HttpClient/close.html doesn't have a timeout param, we'd need to implement manually
1719

1820
/// Sentry SDK options
1921
class SentryOptions {
@@ -68,9 +70,6 @@ class SentryOptions {
6870

6971
final List<Integration> _integrations = [];
7072

71-
// TODO: shutdownTimeout, flushTimeoutMillis
72-
// https://api.dart.dev/stable/2.10.2/dart-io/HttpClient/close.html doesn't have a timeout param, we'd need to implement manually
73-
7473
/// Code that provides middlewares, bindings or hooks into certain frameworks or environments,
7574
/// along with code that inserts those bindings and activates them.
7675
List<Integration> get integrations => List.unmodifiable(_integrations);
@@ -95,11 +94,13 @@ class SentryOptions {
9594
BeforeBreadcrumbCallback? beforeBreadcrumb;
9695

9796
/// Sets the release. SDK will try to automatically configure a release out of the box
97+
/// See [docs for further information](https://docs.sentry.io/platforms/flutter/configuration/releases/)
9898
String? release;
9999

100100
/// Sets the environment. This string is freeform and not set by default. A release can be
101101
/// associated with more than one environment to separate them in the UI Think staging vs prod or
102102
/// similar.
103+
/// See [docs for further information](https://docs.sentry.io/platforms/flutter/configuration/environments/)
103104
String? environment;
104105

105106
/// Configures the sample rate as a percentage of events to be sent in the range of 0.0 to 1.0. if
@@ -139,7 +140,7 @@ class SentryOptions {
139140
/// however, when this option is set, stack traces are also sent with messages.
140141
/// This option, for instance, means that stack traces appear next to all log messages.
141142
///
142-
/// This option is true` by default.
143+
/// This option is `true` by default.
143144
///
144145
/// Grouping in Sentry is different for events with stack traces and without.
145146
/// As a result, you will get new groups as you enable or disable this flag for certain events.
@@ -149,14 +150,16 @@ class SentryOptions {
149150
/// This is useful in tests. Should be an implementation of [PlatformChecker].
150151
PlatformChecker platformChecker = PlatformChecker();
151152

153+
/// If [environmentVariables] is provided, it is used get the envirnoment
154+
/// variables. This is useful in tests.
155+
EnvironmentVariables environmentVariables = EnvironmentVariables();
156+
152157
/// When enabled, all the threads are automatically attached to all logged events (Android).
153158
bool attachThreads = false;
154159

155160
/// Whether to send personal identifiable information along with events
156161
bool sendDefaultPii = false;
157162

158-
// TODO: Scope observers, enableScopeSync
159-
160163
SentryOptions({this.dsn}) {
161164
sdk.addPackage('pub:sentry', sdkVersion);
162165
}

dart/test/environment_test.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:sentry/sentry.dart';
2+
import 'package:test/test.dart';
3+
4+
import 'mocks.dart';
5+
import 'mocks/mock_environment_variables.dart';
6+
7+
void main() {
8+
// See https://docs.sentry.io/platforms/dart/configuration/options/
9+
// and https://github.com/getsentry/sentry-dart/issues/306
10+
group('Environment Variables', () {
11+
tearDown(() {
12+
Sentry.close();
13+
});
14+
15+
test('SentryOptions are not overriden by environment', () async {
16+
final options = SentryOptions(dsn: fakeDsn);
17+
options.release = 'release-1.2.3';
18+
options.dist = 'foo';
19+
options.environment = 'prod';
20+
options.environmentVariables = MockEnvironmentVariables(
21+
dsn: 'foo-bar',
22+
environment: 'staging',
23+
release: 'release-9.8.7',
24+
dist: 'bar',
25+
);
26+
27+
await Sentry.init(
28+
(options) => options,
29+
options: options,
30+
);
31+
32+
expect(options.dsn, fakeDsn);
33+
expect(options.environment, 'prod');
34+
expect(options.release, 'release-1.2.3');
35+
expect(options.dist, 'foo');
36+
});
37+
38+
test('SentryOptions are overriden by environment', () async {
39+
final options = SentryOptions();
40+
options.environmentVariables = MockEnvironmentVariables(
41+
dsn: 'foo-bar',
42+
environment: 'staging',
43+
release: 'release-9.8.7',
44+
dist: 'bar',
45+
);
46+
47+
await Sentry.init(
48+
(options) => options,
49+
options: options,
50+
);
51+
52+
expect(options.dsn, 'foo-bar');
53+
expect(options.environment, 'staging');
54+
expect(options.release, 'release-9.8.7');
55+
expect(options.dist, 'bar');
56+
});
57+
});
58+
}

dart/test/fake_platform_checker.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:sentry/src/platform_checker.dart';
22

33
/// Fake class to be used in unit tests for mocking different environments
4-
class FakePlatformChecker implements PlatformChecker {
4+
class FakePlatformChecker extends PlatformChecker {
55
FakePlatformChecker.releaseMode() {
66
_releaseMode = true;
77
_debugMode = false;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:sentry/src/environment_variables.dart';
2+
3+
class MockEnvironmentVariables extends EnvironmentVariables {
4+
MockEnvironmentVariables({
5+
String? dist,
6+
String? dsn,
7+
String? environment,
8+
String? release,
9+
}) : _dist = dist,
10+
_dsn = dsn,
11+
_environment = environment,
12+
_release = release;
13+
14+
final String? _dist;
15+
final String? _dsn;
16+
final String? _environment;
17+
final String? _release;
18+
19+
@override
20+
String? get dist => _dist;
21+
22+
@override
23+
String? get dsn => _dsn;
24+
25+
@override
26+
String? get environment => _environment;
27+
28+
@override
29+
String? get release => _release;
30+
}

dart/test/sentry_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ void main() {
185185
await Sentry.init((options) {
186186
options.dsn = fakeDsn;
187187
expect(options.environment, 'debug');
188+
expect(options.debug, true);
188189
}, options: sentryOptions);
189190
});
190191

@@ -194,6 +195,7 @@ void main() {
194195
await Sentry.init((options) {
195196
options.dsn = fakeDsn;
196197
expect(options.environment, 'profile');
198+
expect(options.debug, false);
197199
}, options: sentryOptions);
198200
});
199201

@@ -202,7 +204,8 @@ void main() {
202204
..platformChecker = FakePlatformChecker.releaseMode();
203205
await Sentry.init((options) {
204206
options.dsn = fakeDsn;
205-
expect(options.environment, defaultEnvironment);
207+
expect(options.environment, 'production');
208+
expect(options.debug, false);
206209
}, options: sentryOptions);
207210
});
208211
}

flutter/lib/src/default_integrations.dart

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,7 @@ class LoadAndroidImageListIntegration
340340
/// a PackageInfo wrapper to make it testable
341341
typedef PackageLoader = Future<PackageInfo> Function();
342342

343-
/// an Integration that loads the Release version from Native Apps
344-
/// or SENTRY_RELEASE and SENTRY_DIST variables
343+
/// An [Integration] that loads the release version from native apps
345344
class LoadReleaseIntegration extends Integration<SentryFlutterOptions> {
346345
final PackageLoader _packageLoader;
347346

@@ -351,22 +350,15 @@ class LoadReleaseIntegration extends Integration<SentryFlutterOptions> {
351350
FutureOr<void> call(Hub hub, SentryFlutterOptions options) async {
352351
try {
353352
if (!kIsWeb) {
354-
final packageInfo = await _packageLoader();
355-
final release =
356-
'${packageInfo.packageName}@${packageInfo.version}+${packageInfo.buildNumber}';
357-
options.logger(SentryLevel.debug, 'release: $release');
358-
359-
options.release = release;
360-
options.dist = packageInfo.buildNumber;
361-
} else {
362-
// for non-mobile builds, we read the release and dist from the
363-
// system variables (SENTRY_RELEASE and SENTRY_DIST).
364-
options.release = const bool.hasEnvironment('SENTRY_RELEASE')
365-
? const String.fromEnvironment('SENTRY_RELEASE')
366-
: options.release;
367-
options.dist = const bool.hasEnvironment('SENTRY_DIST')
368-
? const String.fromEnvironment('SENTRY_DIST')
369-
: options.dist;
353+
if (options.release == null || options.dist == null) {
354+
final packageInfo = await _packageLoader();
355+
final release =
356+
'${packageInfo.packageName}@${packageInfo.version}+${packageInfo.buildNumber}';
357+
options.logger(SentryLevel.debug, 'release: $release');
358+
359+
options.release = options.release ?? release;
360+
options.dist = options.dist ?? packageInfo.buildNumber;
361+
}
370362
}
371363
} catch (error) {
372364
options.logger(

flutter/lib/src/sentry_flutter.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ mixin SentryFlutter {
6060
SentryFlutterOptions options,
6161
MethodChannel channel,
6262
) async {
63-
options.debug = kDebugMode;
64-
6563
// web still uses a http transport for Web which is set by default
6664
if (!kIsWeb) {
6765
options.transport = FileSystemTransport(channel, options);

0 commit comments

Comments
 (0)