refactor(symbols): share upload flow
This commit is contained in:
+2
-115
@@ -16,124 +16,11 @@ $Root = Split-Path -Parent $ScriptDir
|
|||||||
Set-Location $Root
|
Set-Location $Root
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($SourceRoot)) {
|
if ([string]::IsNullOrEmpty($SourceRoot)) {
|
||||||
$SearchRoot = $Root
|
& dart run scripts/upload_symbols.dart $Platform
|
||||||
}
|
|
||||||
elseif ([System.IO.Path]::IsPathRooted($SourceRoot)) {
|
|
||||||
$SearchRoot = $SourceRoot
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$SearchRoot = [System.IO.Path]::GetFullPath((Join-Path $Root $SourceRoot))
|
& dart run scripts/upload_symbols.dart $Platform $SourceRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
$BuildRoot = Join-Path $SearchRoot 'build'
|
|
||||||
$SymbolRoot = Join-Path (Join-Path $SearchRoot 'debug-info') $Platform
|
|
||||||
$DryRun = -not [string]::IsNullOrEmpty($env:BUGS_UPLOAD_DRY_RUN)
|
|
||||||
|
|
||||||
$SearchRoots = [System.Collections.Generic.List[string]]::new()
|
|
||||||
function Add-ExistingRoot([string]$Path) {
|
|
||||||
if (Test-Path -Path $Path -PathType Container) {
|
|
||||||
$SearchRoots.Add($Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Add-ExistingRoot $SymbolRoot
|
|
||||||
|
|
||||||
switch ($Platform) {
|
|
||||||
{ $_ -eq 'windows-x64' -or $_ -eq 'windows-arm64' } {
|
|
||||||
Add-ExistingRoot (Join-Path $BuildRoot 'windows')
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
Write-Error "unknown platform: $Platform"
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Has-SymbolFile {
|
|
||||||
foreach ($RootPath in $SearchRoots) {
|
|
||||||
$First = Get-ChildItem -Path $RootPath -File -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
||||||
if ($null -ne $First) {
|
|
||||||
return $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Has-SymbolFile)) {
|
|
||||||
Write-Error "no symbols found for platform $Platform"
|
|
||||||
exit 3
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($env:SENTRY_URL)) {
|
|
||||||
$env:SENTRY_URL = if ([string]::IsNullOrEmpty($env:BUGS_URL)) { 'https://bugs.plezy.app' } else { $env:BUGS_URL }
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($env:SENTRY_RELEASE)) {
|
|
||||||
$env:SENTRY_RELEASE = "plezy@$(git rev-parse --short HEAD)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($env:SENTRY_LOG_LEVEL)) {
|
|
||||||
$env:SENTRY_LOG_LEVEL = 'info'
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($env:SENTRY_AUTH_TOKEN) -and -not [string]::IsNullOrEmpty($env:BUGS_ADMIN_TOKEN)) {
|
|
||||||
$env:SENTRY_AUTH_TOKEN = $env:BUGS_ADMIN_TOKEN
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $DryRun -and [string]::IsNullOrEmpty($env:SENTRY_AUTH_TOKEN)) {
|
|
||||||
Write-Error 'SENTRY_AUTH_TOKEN or BUGS_ADMIN_TOKEN env var required'
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$DartSymbolMapPath = $env:SENTRY_DART_SYMBOL_MAP_PATH
|
|
||||||
if ([string]::IsNullOrEmpty($DartSymbolMapPath)) {
|
|
||||||
$Candidates = @(
|
|
||||||
(Join-Path $SymbolRoot 'obfuscation.map.json'),
|
|
||||||
(Join-Path (Join-Path $BuildRoot 'app\obfuscation') "$Platform.map.json"),
|
|
||||||
(Join-Path (Join-Path $BuildRoot 'app') 'obfuscation.map.json')
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach ($Candidate in $Candidates) {
|
|
||||||
if (Test-Path -Path $Candidate -PathType Leaf) {
|
|
||||||
$DartSymbolMapPath = $Candidate
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$PluginArgs = @(
|
|
||||||
"--sentry-define=release=$($env:SENTRY_RELEASE)",
|
|
||||||
"--sentry-define=url=$($env:SENTRY_URL)",
|
|
||||||
"--sentry-define=build_path=$BuildRoot"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (-not [string]::IsNullOrEmpty($env:SENTRY_DIST)) {
|
|
||||||
$PluginArgs += "--sentry-define=dist=$($env:SENTRY_DIST)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Test-Path -Path $SymbolRoot -PathType Container) {
|
|
||||||
$PluginArgs += "--sentry-define=symbols_path=$SymbolRoot"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not [string]::IsNullOrEmpty($DartSymbolMapPath)) {
|
|
||||||
$PluginArgs += "--sentry-define=dart_symbol_map_path=$DartSymbolMapPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($DryRun) {
|
|
||||||
Write-Host "dry-run: would upload symbols for $Platform"
|
|
||||||
Write-Host "dry-run: release=$($env:SENTRY_RELEASE)"
|
|
||||||
Write-Host "dry-run: dist=$($env:SENTRY_DIST)"
|
|
||||||
Write-Host "dry-run: source_root=$SearchRoot"
|
|
||||||
Write-Host "dry-run: build_path=$BuildRoot"
|
|
||||||
Write-Host "dry-run: symbols_path=$SymbolRoot"
|
|
||||||
Write-Host "dry-run: dart_symbol_map_path=$DartSymbolMapPath"
|
|
||||||
foreach ($RootPath in $SearchRoots) {
|
|
||||||
Get-ChildItem -Path $RootPath -File -Recurse -ErrorAction SilentlyContinue | ForEach-Object { Write-Host $_.FullName }
|
|
||||||
}
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "uploading symbols for $Platform release $($env:SENTRY_RELEASE) dist $($env:SENTRY_DIST)"
|
|
||||||
& dart run sentry_dart_plugin @PluginArgs
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-114
@@ -5,121 +5,9 @@
|
|||||||
# Platforms: macos | ios | android-apk | android-aab | linux-x64 | linux-arm64
|
# Platforms: macos | ios | android-apk | android-aab | linux-x64 | linux-arm64
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PLATFORM="${1:?platform arg required}"
|
: "${1:?platform arg required}"
|
||||||
DRY_RUN="${BUGS_UPLOAD_DRY_RUN:-}"
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
|
exec dart run scripts/upload_symbols.dart "$@"
|
||||||
SOURCE_ROOT="${2:-$ROOT}"
|
|
||||||
if [[ "$SOURCE_ROOT" != /* ]]; then
|
|
||||||
SOURCE_ROOT="$ROOT/$SOURCE_ROOT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
BUILD_ROOT="$SOURCE_ROOT/build"
|
|
||||||
SYMBOL_ROOT="$SOURCE_ROOT/debug-info/$PLATFORM"
|
|
||||||
DART_SYMBOL_MAP_PATH="${SENTRY_DART_SYMBOL_MAP_PATH:-}"
|
|
||||||
|
|
||||||
if [ -z "$DART_SYMBOL_MAP_PATH" ]; then
|
|
||||||
for candidate in \
|
|
||||||
"$SYMBOL_ROOT/obfuscation.map.json" \
|
|
||||||
"$BUILD_ROOT/app/obfuscation/$PLATFORM.map.json" \
|
|
||||||
"$BUILD_ROOT/app/obfuscation.map.json"; do
|
|
||||||
if [ -f "$candidate" ]; then
|
|
||||||
DART_SYMBOL_MAP_PATH="$candidate"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
SEARCH_ROOTS=()
|
|
||||||
|
|
||||||
add_existing_root() {
|
|
||||||
if [ -d "$1" ]; then
|
|
||||||
SEARCH_ROOTS+=("$1")
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
add_existing_root "$SYMBOL_ROOT"
|
|
||||||
|
|
||||||
case "$PLATFORM" in
|
|
||||||
macos)
|
|
||||||
add_existing_root "$BUILD_ROOT/macos"
|
|
||||||
;;
|
|
||||||
ios)
|
|
||||||
add_existing_root "$BUILD_ROOT/ios"
|
|
||||||
add_existing_root "$SOURCE_ROOT/ios/build"
|
|
||||||
;;
|
|
||||||
linux-x64|linux-arm64)
|
|
||||||
add_existing_root "$BUILD_ROOT/linux"
|
|
||||||
;;
|
|
||||||
android-apk|android-aab)
|
|
||||||
add_existing_root "$BUILD_ROOT/app"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "unknown platform: $PLATFORM" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
found_symbol_file() {
|
|
||||||
local first
|
|
||||||
if [ "${#SEARCH_ROOTS[@]}" -eq 0 ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
first="$(find "${SEARCH_ROOTS[@]}" -type f -print -quit 2>/dev/null || true)"
|
|
||||||
[ -n "$first" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
if ! found_symbol_file; then
|
|
||||||
echo "no symbols found for platform ${PLATFORM}" >&2
|
|
||||||
exit 3
|
|
||||||
fi
|
|
||||||
|
|
||||||
export SENTRY_URL="${SENTRY_URL:-${BUGS_URL:-https://bugs.plezy.app}}"
|
|
||||||
export SENTRY_RELEASE="${SENTRY_RELEASE:-plezy@$(git rev-parse --short HEAD)}"
|
|
||||||
export SENTRY_LOG_LEVEL="${SENTRY_LOG_LEVEL:-info}"
|
|
||||||
|
|
||||||
if [ -z "${SENTRY_AUTH_TOKEN:-}" ] && [ -n "${BUGS_ADMIN_TOKEN:-}" ]; then
|
|
||||||
export SENTRY_AUTH_TOKEN="$BUGS_ADMIN_TOKEN"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$DRY_RUN" ] && [ -z "${SENTRY_AUTH_TOKEN:-}" ]; then
|
|
||||||
echo "SENTRY_AUTH_TOKEN or BUGS_ADMIN_TOKEN env var required" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
PLUGIN_ARGS=(
|
|
||||||
"--sentry-define=release=${SENTRY_RELEASE}"
|
|
||||||
"--sentry-define=url=${SENTRY_URL}"
|
|
||||||
"--sentry-define=build_path=${BUILD_ROOT}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if [ -n "${SENTRY_DIST:-}" ]; then
|
|
||||||
PLUGIN_ARGS+=("--sentry-define=dist=${SENTRY_DIST}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "$SYMBOL_ROOT" ]; then
|
|
||||||
PLUGIN_ARGS+=("--sentry-define=symbols_path=${SYMBOL_ROOT}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$DART_SYMBOL_MAP_PATH" ]; then
|
|
||||||
PLUGIN_ARGS+=("--sentry-define=dart_symbol_map_path=${DART_SYMBOL_MAP_PATH}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$DRY_RUN" ]; then
|
|
||||||
echo "dry-run: would upload symbols for ${PLATFORM}"
|
|
||||||
echo "dry-run: release=${SENTRY_RELEASE}"
|
|
||||||
echo "dry-run: dist=${SENTRY_DIST:-}"
|
|
||||||
echo "dry-run: source_root=${SOURCE_ROOT}"
|
|
||||||
echo "dry-run: build_path=${BUILD_ROOT}"
|
|
||||||
echo "dry-run: symbols_path=${SYMBOL_ROOT}"
|
|
||||||
echo "dry-run: dart_symbol_map_path=${DART_SYMBOL_MAP_PATH}"
|
|
||||||
find "${SEARCH_ROOTS[@]}" -type f -print 2>/dev/null || true
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "uploading symbols for ${PLATFORM} release ${SENTRY_RELEASE} dist ${SENTRY_DIST:-}"
|
|
||||||
dart run sentry_dart_plugin "${PLUGIN_ARGS[@]}"
|
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
typedef SymbolUploader =
|
||||||
|
Future<int> Function({
|
||||||
|
required List<String> arguments,
|
||||||
|
required Map<String, String> environment,
|
||||||
|
required String workingDirectory,
|
||||||
|
});
|
||||||
|
|
||||||
|
const _defaultSentryUrl = 'https://bugs.plezy.app';
|
||||||
|
|
||||||
|
Future<void> main(List<String> arguments) async {
|
||||||
|
final scriptDirectory = File.fromUri(Platform.script).parent;
|
||||||
|
final result = await runUploadSymbols(arguments, repositoryRoot: scriptDirectory.parent);
|
||||||
|
if (result != 0) {
|
||||||
|
exitCode = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> runUploadSymbols(
|
||||||
|
List<String> arguments, {
|
||||||
|
required Directory repositoryRoot,
|
||||||
|
Map<String, String>? environment,
|
||||||
|
SymbolUploader uploader = _runSentryPlugin,
|
||||||
|
Future<String?> Function()? revisionProvider,
|
||||||
|
StringSink? output,
|
||||||
|
StringSink? errors,
|
||||||
|
}) async {
|
||||||
|
final out = output ?? stdout;
|
||||||
|
final err = errors ?? stderr;
|
||||||
|
if (arguments.isEmpty || arguments.first.isEmpty) {
|
||||||
|
err.writeln('platform arg required');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final platform = arguments.first;
|
||||||
|
final root = path.normalize(path.absolute(repositoryRoot.path));
|
||||||
|
final sourceArgument = arguments.length > 1 ? arguments[1] : '';
|
||||||
|
final sourceRoot = sourceArgument.isEmpty
|
||||||
|
? root
|
||||||
|
: path.normalize(path.isAbsolute(sourceArgument) ? sourceArgument : path.join(root, sourceArgument));
|
||||||
|
final buildRoot = path.join(sourceRoot, 'build');
|
||||||
|
final symbolRoot = path.join(sourceRoot, 'debug-info', platform);
|
||||||
|
|
||||||
|
final searchRoots = <String>[];
|
||||||
|
void addExistingRoot(String candidate) {
|
||||||
|
if (Directory(candidate).existsSync()) {
|
||||||
|
searchRoots.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addExistingRoot(symbolRoot);
|
||||||
|
switch (platform) {
|
||||||
|
case 'macos':
|
||||||
|
addExistingRoot(path.join(buildRoot, 'macos'));
|
||||||
|
case 'ios':
|
||||||
|
addExistingRoot(path.join(buildRoot, 'ios'));
|
||||||
|
addExistingRoot(path.join(sourceRoot, 'ios', 'build'));
|
||||||
|
case 'linux-x64' || 'linux-arm64':
|
||||||
|
addExistingRoot(path.join(buildRoot, 'linux'));
|
||||||
|
case 'android-apk' || 'android-aab':
|
||||||
|
addExistingRoot(path.join(buildRoot, 'app'));
|
||||||
|
case 'windows-x64' || 'windows-arm64':
|
||||||
|
addExistingRoot(path.join(buildRoot, 'windows'));
|
||||||
|
default:
|
||||||
|
err.writeln('unknown platform: $platform');
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
final symbolFiles = _discoverFiles(searchRoots);
|
||||||
|
if (symbolFiles.isEmpty) {
|
||||||
|
err.writeln('no symbols found for platform $platform');
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
final childEnvironment = Map<String, String>.of(environment ?? Platform.environment);
|
||||||
|
final dryRun = _isNotEmpty(childEnvironment['BUGS_UPLOAD_DRY_RUN']);
|
||||||
|
childEnvironment['SENTRY_URL'] = _firstNotEmpty([
|
||||||
|
childEnvironment['SENTRY_URL'],
|
||||||
|
childEnvironment['BUGS_URL'],
|
||||||
|
_defaultSentryUrl,
|
||||||
|
]);
|
||||||
|
childEnvironment['SENTRY_LOG_LEVEL'] = _firstNotEmpty([childEnvironment['SENTRY_LOG_LEVEL'], 'info']);
|
||||||
|
if (!_isNotEmpty(childEnvironment['SENTRY_AUTH_TOKEN']) && _isNotEmpty(childEnvironment['BUGS_ADMIN_TOKEN'])) {
|
||||||
|
childEnvironment['SENTRY_AUTH_TOKEN'] = childEnvironment['BUGS_ADMIN_TOKEN']!;
|
||||||
|
}
|
||||||
|
if (!dryRun && !_isNotEmpty(childEnvironment['SENTRY_AUTH_TOKEN'])) {
|
||||||
|
err.writeln('SENTRY_AUTH_TOKEN or BUGS_ADMIN_TOKEN env var required');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isNotEmpty(childEnvironment['SENTRY_RELEASE'])) {
|
||||||
|
final revision = await (revisionProvider?.call() ?? _gitRevision(root));
|
||||||
|
if (!_isNotEmpty(revision)) {
|
||||||
|
err.writeln('failed to determine SENTRY_RELEASE from git');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
childEnvironment['SENTRY_RELEASE'] = 'plezy@$revision';
|
||||||
|
}
|
||||||
|
|
||||||
|
var dartSymbolMapPath = childEnvironment['SENTRY_DART_SYMBOL_MAP_PATH'] ?? '';
|
||||||
|
if (dartSymbolMapPath.isEmpty) {
|
||||||
|
for (final candidate in [
|
||||||
|
path.join(symbolRoot, 'obfuscation.map.json'),
|
||||||
|
path.join(buildRoot, 'app', 'obfuscation', '$platform.map.json'),
|
||||||
|
path.join(buildRoot, 'app', 'obfuscation.map.json'),
|
||||||
|
]) {
|
||||||
|
if (File(candidate).existsSync()) {
|
||||||
|
dartSymbolMapPath = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final release = childEnvironment['SENTRY_RELEASE']!;
|
||||||
|
final dist = childEnvironment['SENTRY_DIST'] ?? '';
|
||||||
|
final pluginArguments = <String>[
|
||||||
|
'--sentry-define=release=$release',
|
||||||
|
'--sentry-define=url=${childEnvironment['SENTRY_URL']}',
|
||||||
|
'--sentry-define=build_path=$buildRoot',
|
||||||
|
if (dist.isNotEmpty) '--sentry-define=dist=$dist',
|
||||||
|
if (Directory(symbolRoot).existsSync()) '--sentry-define=symbols_path=$symbolRoot',
|
||||||
|
if (dartSymbolMapPath.isNotEmpty) '--sentry-define=dart_symbol_map_path=$dartSymbolMapPath',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
out.writeln('dry-run: would upload symbols for $platform');
|
||||||
|
out.writeln('dry-run: release=$release');
|
||||||
|
out.writeln('dry-run: dist=$dist');
|
||||||
|
out.writeln('dry-run: source_root=$sourceRoot');
|
||||||
|
out.writeln('dry-run: build_path=$buildRoot');
|
||||||
|
out.writeln('dry-run: symbols_path=$symbolRoot');
|
||||||
|
out.writeln('dry-run: dart_symbol_map_path=$dartSymbolMapPath');
|
||||||
|
for (final file in symbolFiles) {
|
||||||
|
out.writeln(file);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.writeln('uploading symbols for $platform release $release dist $dist');
|
||||||
|
try {
|
||||||
|
final result = await uploader(arguments: pluginArguments, environment: childEnvironment, workingDirectory: root);
|
||||||
|
if (result != 0) {
|
||||||
|
err.writeln('symbol upload failed for $platform (exit code $result)');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} on ProcessException catch (error) {
|
||||||
|
err.writeln('failed to start symbol upload: $error');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _discoverFiles(List<String> roots) {
|
||||||
|
final files = <String>[];
|
||||||
|
for (final root in roots) {
|
||||||
|
try {
|
||||||
|
files.addAll(
|
||||||
|
Directory(root)
|
||||||
|
.listSync(recursive: true, followLinks: false)
|
||||||
|
.whereType<File>()
|
||||||
|
.map((file) => path.normalize(path.absolute(file.path))),
|
||||||
|
);
|
||||||
|
} on FileSystemException {
|
||||||
|
// Match the wrappers' previous best-effort discovery behavior.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files.sort();
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> _gitRevision(String root) async {
|
||||||
|
try {
|
||||||
|
final result = await Process.run('git', const ['rev-parse', '--short', 'HEAD'], workingDirectory: root);
|
||||||
|
if (result.exitCode == 0) {
|
||||||
|
return (result.stdout as String).trim();
|
||||||
|
}
|
||||||
|
} on ProcessException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _runSentryPlugin({
|
||||||
|
required List<String> arguments,
|
||||||
|
required Map<String, String> environment,
|
||||||
|
required String workingDirectory,
|
||||||
|
}) async {
|
||||||
|
final process = await Process.start(
|
||||||
|
Platform.resolvedExecutable,
|
||||||
|
['run', 'sentry_dart_plugin', ...arguments],
|
||||||
|
workingDirectory: workingDirectory,
|
||||||
|
environment: environment,
|
||||||
|
includeParentEnvironment: false,
|
||||||
|
mode: ProcessStartMode.inheritStdio,
|
||||||
|
);
|
||||||
|
return process.exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isNotEmpty(String? value) => value != null && value.isNotEmpty;
|
||||||
|
|
||||||
|
String _firstNotEmpty(List<String?> values) => values.firstWhere(_isNotEmpty)!;
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
|
import '../../scripts/upload_symbols.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Directory repository;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
repository = await Directory.systemTemp.createTemp('plezy_upload_symbols_');
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
await repository.delete(recursive: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('discovers symbols and passes the complete upload request to the plugin', () async {
|
||||||
|
final symbolRoot = Directory(path.join(repository.path, 'debug-info', 'linux-x64'))..createSync(recursive: true);
|
||||||
|
final archive = File(path.join(symbolRoot.path, 'symbols.zip'))..writeAsStringSync('symbols');
|
||||||
|
final symbolMap = File(path.join(symbolRoot.path, 'obfuscation.map.json'))..writeAsStringSync('{}');
|
||||||
|
late List<String> uploadArguments;
|
||||||
|
late Map<String, String> uploadEnvironment;
|
||||||
|
late String uploadWorkingDirectory;
|
||||||
|
|
||||||
|
final result = await runUploadSymbols(
|
||||||
|
const ['linux-x64'],
|
||||||
|
repositoryRoot: repository,
|
||||||
|
environment: const {
|
||||||
|
'BUGS_ADMIN_TOKEN': 'admin-token',
|
||||||
|
'BUGS_URL': 'https://bugs.example.test',
|
||||||
|
'SENTRY_RELEASE': 'plezy@test',
|
||||||
|
'SENTRY_DIST': 'test-linux',
|
||||||
|
},
|
||||||
|
uploader: ({required arguments, required environment, required workingDirectory}) async {
|
||||||
|
uploadArguments = arguments;
|
||||||
|
uploadEnvironment = environment;
|
||||||
|
uploadWorkingDirectory = workingDirectory;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result, 0);
|
||||||
|
expect(archive.existsSync(), isTrue);
|
||||||
|
expect(uploadWorkingDirectory, path.normalize(path.absolute(repository.path)));
|
||||||
|
expect(uploadEnvironment['SENTRY_AUTH_TOKEN'], 'admin-token');
|
||||||
|
expect(uploadEnvironment['SENTRY_LOG_LEVEL'], 'info');
|
||||||
|
expect(uploadArguments, [
|
||||||
|
'--sentry-define=release=plezy@test',
|
||||||
|
'--sentry-define=url=https://bugs.example.test',
|
||||||
|
'--sentry-define=build_path=${path.join(repository.path, 'build')}',
|
||||||
|
'--sentry-define=dist=test-linux',
|
||||||
|
'--sentry-define=symbols_path=${symbolRoot.path}',
|
||||||
|
'--sentry-define=dart_symbol_map_path=${symbolMap.path}',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rejects missing platform, symbols, and credentials', () async {
|
||||||
|
final errors = StringBuffer();
|
||||||
|
expect(await runUploadSymbols(const [], repositoryRoot: repository, errors: errors), 1);
|
||||||
|
expect(errors.toString(), contains('platform arg required'));
|
||||||
|
|
||||||
|
errors.clear();
|
||||||
|
expect(
|
||||||
|
await runUploadSymbols(
|
||||||
|
const ['windows-x64'],
|
||||||
|
repositoryRoot: repository,
|
||||||
|
environment: const {'SENTRY_RELEASE': 'plezy@test'},
|
||||||
|
errors: errors,
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
expect(errors.toString(), contains('no symbols found'));
|
||||||
|
|
||||||
|
Directory(path.join(repository.path, 'debug-info', 'windows-x64')).createSync(recursive: true);
|
||||||
|
File(path.join(repository.path, 'debug-info', 'windows-x64', 'symbols.zip')).writeAsStringSync('symbols');
|
||||||
|
errors.clear();
|
||||||
|
expect(
|
||||||
|
await runUploadSymbols(
|
||||||
|
const ['windows-x64'],
|
||||||
|
repositoryRoot: repository,
|
||||||
|
environment: const {'SENTRY_RELEASE': 'plezy@test'},
|
||||||
|
errors: errors,
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
expect(errors.toString(), contains('SENTRY_AUTH_TOKEN or BUGS_ADMIN_TOKEN env var required'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('propagates an HTTP upload failure without network access', () async {
|
||||||
|
final symbolRoot = Directory(path.join(repository.path, 'debug-info', 'android-apk'))..createSync(recursive: true);
|
||||||
|
File(path.join(symbolRoot.path, 'symbols.zip')).writeAsStringSync('symbols');
|
||||||
|
final errors = StringBuffer();
|
||||||
|
|
||||||
|
final result = await runUploadSymbols(
|
||||||
|
const ['android-apk'],
|
||||||
|
repositoryRoot: repository,
|
||||||
|
environment: const {'SENTRY_AUTH_TOKEN': 'token', 'SENTRY_RELEASE': 'plezy@test'},
|
||||||
|
errors: errors,
|
||||||
|
uploader: ({required arguments, required environment, required workingDirectory}) async => 22,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result, 22);
|
||||||
|
expect(errors.toString(), contains('symbol upload failed for android-apk (exit code 22)'));
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user