Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
7bfcaf9
chore: flutter symbol collector CLI tool
vaind Oct 11, 2023
d1313bd
renames
vaind Oct 12, 2023
7a0c73e
symbol collector CLI integration
vaind Oct 12, 2023
ac1c1c0
fixup getVersion()
vaind Oct 12, 2023
7218df6
collector upload
vaind Oct 12, 2023
d394181
collect platform info with symbol archive
vaind Oct 12, 2023
5e963aa
download and extract zip archives
vaind Oct 12, 2023
c4aa8a8
download via cli bin
vaind Oct 12, 2023
a7a80ea
upload via symbol-collector cli
vaind Oct 12, 2023
667c144
add symbol collector CI
vaind Oct 13, 2023
5390bcf
extract inner zip files
vaind Oct 13, 2023
2bc4ed7
test symbol collector in CI
vaind Oct 13, 2023
ed79ef4
fix tests
vaind Oct 13, 2023
ff91b35
chore: update changelog
vaind Oct 13, 2023
795e7a4
fix tests
vaind Oct 13, 2023
816ae3e
fix tests
vaind Oct 13, 2023
5170a46
upload android symbols
vaind Oct 13, 2023
caa9b62
fix duplicate upload
vaind Oct 13, 2023
4724c81
upload symbols for all of 3.13.*
vaind Oct 24, 2023
b55becb
cache previous symbol collector successful uploads
vaind Oct 24, 2023
cb298d6
fix artifacts
vaind Oct 24, 2023
57efb36
we need to use cache
vaind Oct 24, 2023
8d686cb
cron
vaind Oct 24, 2023
bdfcfdc
test
vaind Oct 24, 2023
cee0761
use artifacts
vaind Oct 24, 2023
611f636
run for all v3 flutter versions
vaind Oct 24, 2023
321198c
fixup changelog
vaind Oct 24, 2023
6834bc8
fixup changelog
vaind Oct 25, 2023
dfb8fee
refactor: move status cache to a separate file
vaind Oct 25, 2023
deca8fc
change artifact action to download from previous runs
vaind Oct 25, 2023
12f6033
fix status cache
vaind Oct 25, 2023
0dc96c3
fix status cache
vaind Oct 25, 2023
3875129
register symbol collector CLI updater
vaind Oct 25, 2023
6ba64f6
log status change
vaind Oct 25, 2023
d352c73
fix status caching
vaind Oct 25, 2023
6d163a6
fix
vaind Oct 25, 2023
b7e4719
process all flutter v3 subversions
vaind Oct 25, 2023
2edb9be
try to fix memory usage
vaind Oct 25, 2023
adf8f60
cleanup earlier
vaind Oct 25, 2023
6529c4b
roll back changes after figuring out the issue is with symbol collect…
vaind Oct 25, 2023
6f1fc8d
update symbol-collector to the latest version
vaind Oct 26, 2023
7b9fb06
rename .successful to .cache
vaind Oct 26, 2023
e7a20f3
don't use version in the status cache
vaind Oct 26, 2023
9461372
remove temp code
vaind Oct 26, 2023
986f954
run cron every hour
vaind Oct 26, 2023
9b74649
update symbol collector issue link
vaind Oct 27, 2023
5edbf3d
Merge branch 'main' into feat/flutter-symbol-upload
vaind Oct 30, 2023
0d8652b
minor fixes
vaind Oct 30, 2023
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
Next Next commit
chore: flutter symbol collector CLI tool
  • Loading branch information
vaind committed Oct 25, 2023
commit 7bfcaf989fdee76f146ec907e18c2908fa447bde
3 changes: 3 additions & 0 deletions scripts/flutter_symbol_collector/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
4 changes: 4 additions & 0 deletions scripts/flutter_symbol_collector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Flutter symbol collector

This is an internal tool to collect Flutter debug symbols and upload them to Sentry.
This application is not intended for public usage - we're uploading the symbols in CI automatically so you don't have to.
30 changes: 30 additions & 0 deletions scripts/flutter_symbol_collector/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.

include: package:lints/recommended.yaml

# Uncomment the following section to specify additional rules.

# linter:
# rules:
# - camel_case_types

# analyzer:
# exclude:
# - path/to/excluded/files/**

# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints

# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:flutter_symbol_collector/flutter_symbol_collector.dart';

void main(List<String> arguments) {
final downloader = FlutterSymbolDownloader();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'src/downloader.dart';
export 'src/flutter_version.dart';
60 changes: 60 additions & 0 deletions scripts/flutter_symbol_collector/lib/src/downloader.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:flutter_symbol_collector/src/flutter_version.dart';
import 'package:flutter_symbol_collector/src/symbol_resolver.dart';
import 'package:github/github.dart';
import 'package:gcloud/storage.dart';
import 'package:http/http.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;

class FlutterSymbolDownloader {
late final Logger _log;
final _github = GitHub();
late final _flutterRepo = RepositorySlug('flutter', 'flutter');
late final _symbolsBucket =
Storage(Client(), '').bucket('flutter_infra_release');

FlutterSymbolDownloader({Logger? logger}) {
_log = logger ?? Logger.root;
}

Stream<FlutterVersion> listFlutterVersions() => _github.repositories
.listTags(_flutterRepo, perPage: 30)
.map((t) => FlutterVersion(t.name));

Future<List<String>> listSymbolArchives(FlutterVersion version) async {
// example: https://console.cloud.google.com/storage/browser/flutter_infra_release/flutter/9064459a8b0dcd32877107f6002cc429a71659d1
final prefix = 'flutter/${await version.getEngineVersion()}/';

late final List<SymbolResolver> resolvers;
if (version.tagName.startsWith('3.')) {
resolvers = [
IosSymbolResolver(_symbolsBucket, prefix),
MacOSSymbolResolver(_symbolsBucket, prefix)
];
} else {
_log.warning('No symbol resolvers registered for ${version.tagName}');
return [];
}

assert(resolvers.isNotEmpty);
final archives = List<String>.empty(growable: true);
for (var resolver in resolvers) {
final files = await resolver.listArchives();
if (files.isEmpty) {
_log.warning(
'Flutter ${version.tagName}: no debug symbols found by ${resolver.runtimeType}');
} else {
_log.fine(
'Flutter ${version.tagName}: ${resolver.runtimeType} found debug symbols: ${files.map((v) => path.basename(v))}');
archives.addAll(files);
}
}

return archives;
}

Stream<List<int>> download(
String path,
) =>
_symbolsBucket.read(path);
}
14 changes: 14 additions & 0 deletions scripts/flutter_symbol_collector/lib/src/flutter_version.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:http/http.dart' as http;

class FlutterVersion {
final String tagName;

FlutterVersion(this.tagName);

bool get isPreRelease => tagName.endsWith('.pre');

Future<String> getEngineVersion() => http
.get(Uri.https('raw.githubusercontent.com',
'flutter/flutter/$tagName/bin/internal/engine.version'))
.then((value) => value.body.trim());
}
48 changes: 48 additions & 0 deletions scripts/flutter_symbol_collector/lib/src/symbol_resolver.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:gcloud/storage.dart';

abstract class SymbolResolver {
final String _prefix;
final Bucket _bucket;
final _resolvedFiles = List<String>.empty(growable: true);

SymbolResolver(this._bucket, String prefix)
: _prefix = prefix.endsWith('/')
? prefix.substring(0, prefix.length - 1)
: prefix;

Future<void> tryResolve(String path) async {
path = '$_prefix/$path';
final matches = await _bucket
.list(prefix: path)
.where((v) => v.isObject)
.where((v) => v.name == path) // because it's a prefix search
.map((v) => v.name)
.toList();
if (matches.isNotEmpty) {
_resolvedFiles.add(matches.single);
}
}

Future<List<String>> listArchives();
}

class IosSymbolResolver extends SymbolResolver {
IosSymbolResolver(super.bucket, super.prefix);

@override
Future<List<String>> listArchives() async {
await tryResolve('ios-release/Flutter.dSYM.zip');
return _resolvedFiles;
}
}

class MacOSSymbolResolver extends SymbolResolver {
MacOSSymbolResolver(super.bucket, super.prefix);

@override
Future<List<String>> listArchives() async {
// darwin-x64-release directory contains a fat (arm64+x86_64) binary.
await tryResolve('darwin-x64-release/FlutterMacOS.dSYM.zip');
return _resolvedFiles;
}
}
18 changes: 18 additions & 0 deletions scripts/flutter_symbol_collector/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: flutter_symbol_collector
description: Internal tool to collect Flutter debug symbols and upload them to Sentry
publish_to: none

environment:
sdk: ^3.0.0

dependencies:
gcloud: ^0.8.11
github: ^9.19.0
http: ^1.1.0
logging: ^1.2.0
path: ^1.8.3


dev_dependencies:
lints: ^2.0.0
test: ^1.21.0
78 changes: 78 additions & 0 deletions scripts/flutter_symbol_collector/test/downloader_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:flutter_symbol_collector/flutter_symbol_collector.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';

void main() {
Logger.root.level = Level.ALL;
late FlutterSymbolDownloader sut;

setUp(() {
sut = FlutterSymbolDownloader();
});

test('$FlutterVersion.isPrerelease()', () async {
expect(FlutterVersion('v1.16.3').isPreRelease, false);
expect(FlutterVersion('v1.16.3.pre').isPreRelease, true);
expect(FlutterVersion('3.16.0-9.0').isPreRelease, false);
expect(FlutterVersion('3.16.0-9.0.pre').isPreRelease, true);
});

test('listFlutterVersions() returns a stable list', () async {
final versions = await sut.listFlutterVersions().take(3).toList();
expect(versions.map((v) => v.tagName),
equals(['v1.16.3', 'v1.16.2', 'v1.16.1']));
});

test('listFlutterVersions() fetches items across multiple API page requests',
() async {
// the page size defaults to 30 at the moment, see listFlutterVersions()
final versions = await sut.listFlutterVersions().take(105).toList();
expect(versions.length, equals(105));
});

test('Engine versions match expected values', () async {
final versions = await sut.listFlutterVersions().take(3).toList();
final engines = List.empty(growable: true);
for (var v in versions) {
engines.add("${v.tagName} => ${await v.getEngineVersion()}");
}
expect(
engines,
equals([
'v1.16.3 => b2bdeb3f0f1683f3e0562f491b5e316240dfbc2c',
'v1.16.2 => 2d42c74a348d98d2fd372a91953c104e58f185cd',
'v1.16.1 => 216c420a2c06e5266a60a768b3fd0b660551cc9c'
]));
});

test('listSymbolArchives() supports expected platforms', () async {
final archives = await sut.listSymbolArchives(FlutterVersion('3.13.4'));
const prefix = 'flutter/9064459a8b0dcd32877107f6002cc429a71659d1';
expect(
archives,
equals([
'$prefix/ios-release/Flutter.dSYM.zip',
'$prefix/darwin-x64-release/FlutterMacOS.dSYM.zip'
]));
});

test('listSymbolArchives() supports expected platforms', () async {
final archives = await sut.listSymbolArchives(FlutterVersion('3.13.4'));
const prefix = 'flutter/9064459a8b0dcd32877107f6002cc429a71659d1';
expect(
archives,
equals([
'$prefix/ios-release/Flutter.dSYM.zip',
'$prefix/darwin-x64-release/FlutterMacOS.dSYM.zip'
]));
});

test('download() downloads the file', () async {
// No need to download a large archive, just some small file to test this.
final content = await sut
.download('test.txt')
.map(String.fromCharCodes)
.reduce((a, b) => '$a$b');
expect(content, equals('test\n'));
});
}