Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move plugin from sentry_dart_plugin (experimental) #1099

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions dart/bin/plugin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'dart:io';
import 'dart:async';

import 'package:sentry/plugin/plugin.dart';

/// Main class that executes the SentryDartPlugin
Future<void> main(List<String> arguments) async {
exitCode = await SentryDartPlugin().run(arguments);
}
220 changes: 220 additions & 0 deletions dart/lib/plugin/plugin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import 'dart:io';

import 'package:process/process.dart';

import 'src/configuration.dart';
import 'src/utils/injector.dart';
import 'src/utils/log.dart';

/// Class responsible to load the configurations and upload the
/// debug symbols and source maps
class SentryDartPlugin {
late Configuration _configuration;

/// SentryDartPlugin ctor. that inits the injectors
SentryDartPlugin() {
initInjector();
}

/// Method responsible to load the configurations and upload the
/// debug symbols and source maps
Future<int> run(List<String> cliArguments) async {
_configuration = injector.get<Configuration>();

try {
await _configuration.getConfigValues(cliArguments);
if (!_configuration.validateConfigValues()) {
return 1;
}

if (_configuration.uploadNativeSymbols) {
_executeCliForDebugSymbols();
} else {
Log.info('uploadNativeSymbols is disabled.');
}

_executeNewRelease();

if (_configuration.uploadSourceMaps) {
_executeCliForSourceMaps();
} else {
Log.info('uploadSourceMaps is disabled.');
}

if (_configuration.commits.toLowerCase() != 'false') {
_executeSetCommits();
} else {
Log.info('Commit integration is disabled.');
}

_executeFinalizeRelease();
} on ExitError catch (e) {
return e.code;
}
return 0;
}

void _executeCliForDebugSymbols() {
const taskName = 'uploading debug symbols';
Log.startingTask(taskName);

List<String> params = [];

_setUrlAndTokenAndLog(params);

params.add('upload-dif');

_addOrgAndProject(params);

if (_configuration.includeNativeSources) {
params.add('--include-sources');
} else {
Log.info('includeNativeSources is disabled, not uploading sources.');
}

params.add(_configuration.buildFilesFolder);

_addWait(params);

_executeAndLog('Failed to upload symbols', params);

Log.taskCompleted(taskName);
}

List<String> _releasesCliParams() {
final params = <String>[];
_setUrlAndTokenAndLog(params);
params.add('releases');
_addOrgAndProject(params);
return params;
}

void _executeNewRelease() {
_executeAndLog('Failed to create a new release',
[..._releasesCliParams(), 'new', _release]);
}

void _executeFinalizeRelease() {
_executeAndLog('Failed to finalize the new release',
[..._releasesCliParams(), 'finalize', _release]);
}

void _executeSetCommits() {
final params = [
..._releasesCliParams(),
'set-commits',
_release,
];

if (['auto', 'true', ''].contains(_configuration.commits.toLowerCase())) {
params.add('--auto');
} else {
params.add('--commit');
params.add(_configuration.commits);
}

_executeAndLog('Failed to set commits', params);
}

void _executeCliForSourceMaps() {
const taskName = 'uploading source maps';
Log.startingTask(taskName);

List<String> params = _releasesCliParams();

// upload source maps (js and map)
List<String> releaseJsFilesParams = [];
releaseJsFilesParams.addAll(params);

_addExtensionToParams(['map', 'js'], releaseJsFilesParams, _release,
_configuration.webBuildFilesFolder);

_addWait(releaseJsFilesParams);

_executeAndLog('Failed to upload source maps', releaseJsFilesParams);

// upload source maps (dart)
List<String> releaseDartFilesParams = [];
releaseDartFilesParams.addAll(params);

_addExtensionToParams(['dart'], releaseDartFilesParams, _release,
_configuration.buildFilesFolder);

_addWait(releaseDartFilesParams);

_executeAndLog('Failed to upload source maps', releaseDartFilesParams);

Log.taskCompleted(taskName);
}

void _setUrlAndTokenAndLog(List<String> params) {
if (_configuration.url != null) {
params.add('--url');
params.add(_configuration.url!);
}

if (_configuration.authToken != null) {
params.add('--auth-token');
params.add(_configuration.authToken!);
}

if (_configuration.logLevel != null) {
params.add('--log-level');
params.add(_configuration.logLevel!);
}
}

void _executeAndLog(String errorMessage, List<String> params) {
ProcessResult? processResult;
try {
processResult = injector
.get<ProcessManager>()
.runSync([_configuration.cliPath!, ...params]);
} on Exception catch (exception) {
Log.error('$errorMessage: \n$exception');
}
if (processResult != null) {
Log.processResult(processResult);
}
}

void _addExtensionToParams(
List<String> exts, List<String> params, String version, String folder) {
params.add('files');
params.add(version);
params.add('upload-sourcemaps');
params.add(folder);

for (final ext in exts) {
params.add('--ext');
params.add(ext);
}

// TODO: add support to custom dist
if (version.contains('+')) {
params.add('--dist');
final values = version.split('+');
params.add(values.last);
}
}

String get _release => '${_configuration.name}@${_configuration.version}';

void _addWait(List<String> params) {
if (_configuration.waitForProcessing) {
params.add('--wait');
}
}

void _addOrgAndProject(List<String> params) {
if (_configuration.org != null) {
params.add('--org');
params.add(_configuration.org!);
}

if (_configuration.project != null) {
params.add('--project');
params.add(_configuration.project!);
}
}
}
25 changes: 25 additions & 0 deletions dart/lib/plugin/src/cli/_sources.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// FILE GENERATED BY scripts/update-cli.sh - DO NOT MODIFY BY HAND

import 'sources.dart';
import 'host_platform.dart';

const _version = '2.8.0';
const _urlPrefix = 'https://downloads.sentry-cdn.com/sentry-cli/$_version';

final currentCLISources = {
HostPlatform.darwinUniversal: CLISource(
'$_urlPrefix/sentry-cli-Darwin-universal',
'a2aadaf804fad99ac70f52a32bf1f0ff53327ed52c2a723fea04bbe9cbde3485'),
HostPlatform.linuxAarch64: CLISource('$_urlPrefix/sentry-cli-Linux-aarch64',
'652b0a6fb992fac95b80bcc6d2f59868750ac22eb2ff4156d3c8bd646c934c3b'),
HostPlatform.linuxArmv7: CLISource('$_urlPrefix/sentry-cli-Linux-armv7',
'0c41f307f3f2a69270150b1b8fd41acbbcfbe8730610d3a7dd8bdd7aecac6613'),
HostPlatform.linux64bit: CLISource('$_urlPrefix/sentry-cli-Linux-x86_64',
'bbbd739afc0d8a6736ae45e5f6fcd6db87f7abf45de9fa76e5621834f2dfd15d'),
HostPlatform.windows32bit: CLISource(
'$_urlPrefix/sentry-cli-Windows-i686.exe',
'760d313eddd0f1dd5b23301635dc7c4512b021d9dc01067eb7fa6b54e4789078'),
HostPlatform.windows64bit: CLISource(
'$_urlPrefix/sentry-cli-Windows-x86_64.exe',
'a89a4ca913be1e5ff9ccb456b5bde221bb59128eb5ca2de0f6bcd09a119471ee'),
};
15 changes: 15 additions & 0 deletions dart/lib/plugin/src/cli/host_platform.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
enum HostPlatform {
darwinUniversal,
linux64bit,
linuxArmv7,
linuxAarch64,
windows32bit,
windows64bit,
}

extension HostPlatformUtils on HostPlatform {
get executableExtension =>
(this == HostPlatform.windows32bit || this == HostPlatform.windows64bit)
? '.exe'
: '';
}
82 changes: 82 additions & 0 deletions dart/lib/plugin/src/cli/setup.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:file/file.dart';
import 'package:http/http.dart' as http;

import '../utils/injector.dart';
import '../utils/log.dart';
import 'host_platform.dart';
import 'sources.dart';

class CLISetup {
final /*CLISources*/ Map<HostPlatform, CLISource> _sources;
final _directory = '.dart_tool/pub/bin/sentry_dart_plugin';

CLISetup(this._sources);

Future<String> download(HostPlatform platform) async {
final dir = injector.get<FileSystem>().directory(_directory);
await dir.create(recursive: true);
final file = dir.childFile('sentry-cli${platform.executableExtension}');

final source = _sources[platform]!;

if (!await _check(source, file)) {
await _download(source, file);
}

return file.path;
}

Future<void> _download(CLISource source, File file) async {
Log.info(
"Downloading sentry-cli from ${source.downloadUrl} to ${file.path}");

final client = http.Client();
try {
final response =
await client.send(http.Request('GET', source.downloadUrl));
final sink = file.openWrite();
await sink.addStream(response.stream);
await sink.close();
} finally {
client.close();
}

if (await _check(source, file)) {
Log.info("Sentry CLI downloaded successfully.");
} else {
Log.error(
"Failed to download Sentry CLI: downloaded file doesn't match the expected checksum.");
}
}

Future<bool> _check(CLISource source, File file) async {
if (!await file.exists()) {
return false;
}

final calculated = await _hash(file);
final expected = source.hash;
if (calculated != expected) {
Log.warn(
"Sentry CLI checksum mismatch on ${file.path} - expected $expected but got $calculated");
return false;
}

Log.info(
"Downloaded Sentry CLI binary checksum verification passed successfully (hash: $calculated).");
return true;
}

Future<String> _hash(File file) async {
var output = AccumulatorSink<Digest>();
var input = sha256.startChunkedConversion(output);
final stream = file.openRead();
await for (final chunk in stream) {
input.add(chunk);
}
input.close();
return output.events.single.toString();
}
}
10 changes: 10 additions & 0 deletions dart/lib/plugin/src/cli/sources.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CLISource {
final Uri downloadUrl;
final String hash;

CLISource(String downloadUrl, this.hash)
: downloadUrl = Uri.parse(downloadUrl);
}

// TODO after increasing min dart SDK version to 2.13.0
// typedef CLISources = Map<HostPlatform, CLISource>;
Loading