You've already forked CC-Framework.CrashReport
652 lines
26 KiB
C#
652 lines
26 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.IO.Compression;
|
||
using System.Reflection;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using Debug = UnityEngine.Debug;
|
||
|
||
namespace CCFramework.CrashReport.Editor
|
||
{
|
||
public static class BuglyAndroidSymbolUtility
|
||
{
|
||
public delegate void LogHandler(string message, LogType type = LogType.Log);
|
||
|
||
public const string BuglyDownloadUrl = "https://bugly.tds.tencent.com/docs/assets/files/bugly_symbol_oversea_v3.4.23-a449c7a62d7792de76fca1af623584f5.zip";
|
||
public const string BuglyToolGuideUrl = "https://bugly.tds.tencent.com/docs/tutorial/symbol/tool/";
|
||
|
||
private const string SymbolFolderSuffix = "_bugly_symbols";
|
||
private const string SymbolPackageSuffix = "_bugly_symbols.zip";
|
||
private const string UploadStagingDirectory = "Library/BuglySymbolUpload";
|
||
|
||
public static void ApplyAndroidSymbolSettings(BuildProfile profile, CrashReportBuglyProfileSettings settings, LogHandler log)
|
||
{
|
||
if (!IsAndroidProfile(profile) || settings == null || !settings.enableAndroidSymbolArchive)
|
||
{
|
||
return;
|
||
}
|
||
|
||
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Debugging;
|
||
WriteLog(log, "已启用 Android Debugging 符号表生成,构建后会产出 native-debug-symbols.zip。");
|
||
}
|
||
|
||
public static BuglySymbolArchiveResult ArchiveAfterBuild(
|
||
BuildProfile profile,
|
||
CrashReportBuglyProfileSettings settings,
|
||
string buildOutputPath,
|
||
LogHandler log)
|
||
{
|
||
BuglySymbolArchiveResult result = new BuglySymbolArchiveResult();
|
||
if (!IsAndroidProfile(profile) || settings == null || !settings.enableAndroidSymbolArchive)
|
||
{
|
||
result.message = "当前不是 Android 构建或未启用 Bugly 符号表归档。";
|
||
WriteLog(log, result.message);
|
||
return result;
|
||
}
|
||
|
||
string resolvedBuildOutputPath = ResolveFullPath(buildOutputPath);
|
||
string buildDirectory = Path.GetDirectoryName(resolvedBuildOutputPath);
|
||
string buildName = Path.GetFileNameWithoutExtension(resolvedBuildOutputPath);
|
||
string archiveDirectory = Path.Combine(buildDirectory, buildName + SymbolFolderSuffix);
|
||
Directory.CreateDirectory(archiveDirectory);
|
||
|
||
string nativeSymbolPath = FindNativeDebugSymbols(profile);
|
||
string mappingPath = FindMappingFile(profile, resolvedBuildOutputPath);
|
||
|
||
if (!string.IsNullOrWhiteSpace(nativeSymbolPath))
|
||
{
|
||
result.nativeSymbolPath = Path.Combine(archiveDirectory, buildName + "_native-debug-symbols.zip");
|
||
File.Copy(nativeSymbolPath, result.nativeSymbolPath, true);
|
||
WriteLog(log, $"已归档 Bugly SO 符号表:{result.nativeSymbolPath}");
|
||
}
|
||
else
|
||
{
|
||
WriteLog(log, "未找到 native-debug-symbols.zip,Native Crash 可能无法还原。", LogType.Warning);
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(mappingPath))
|
||
{
|
||
result.mappingPath = Path.Combine(archiveDirectory, buildName + "_mapping.txt");
|
||
File.Copy(mappingPath, result.mappingPath, true);
|
||
WriteLog(log, $"已归档 Bugly mapping 文件:{result.mappingPath}");
|
||
}
|
||
|
||
result.archiveDirectory = archiveDirectory;
|
||
result.manifestPath = WriteManifest(profile, resolvedBuildOutputPath, archiveDirectory, result);
|
||
result.packagePath = CreatePackage(archiveDirectory, Path.Combine(buildDirectory, buildName + SymbolPackageSuffix));
|
||
result.hasSymbols = File.Exists(result.nativeSymbolPath) || File.Exists(result.mappingPath);
|
||
result.message = result.hasSymbols ? "Bugly 符号表归档完成。" : "Bugly 符号表归档完成,但没有找到可上传的符号表文件。";
|
||
WriteLog(log, $"Bugly 符号表包:{result.packagePath}");
|
||
return result;
|
||
}
|
||
|
||
public static BuglySymbolUploadResult UploadPreparedSymbols(
|
||
BuildProfile profile,
|
||
CrashReportBuglyProfileSettings settings,
|
||
BuglySymbolArchiveResult archive,
|
||
LogHandler log)
|
||
{
|
||
if (profile == null || settings == null)
|
||
{
|
||
return FailUpload("构建配置为空,无法上传 Bugly 符号表。", log);
|
||
}
|
||
|
||
if (archive == null || !archive.hasSymbols)
|
||
{
|
||
return FailUpload("没有可上传的 Bugly 符号表文件。", log);
|
||
}
|
||
|
||
string javaPath = string.IsNullOrWhiteSpace(settings.buglyJavaPath) ? "java" : settings.buglyJavaPath.Trim();
|
||
string toolPath = ResolveSymbolToolPath(settings);
|
||
if (string.IsNullOrWhiteSpace(settings.buglyAppId))
|
||
{
|
||
return FailUpload("Bugly App ID 为空。", log);
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(settings.buglyAppKey))
|
||
{
|
||
return FailUpload("Bugly App Key 为空。", log);
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(toolPath) || !File.Exists(toolPath))
|
||
{
|
||
return FailUpload($"Bugly 符号表工具不存在:{toolPath}", log);
|
||
}
|
||
|
||
BuglySymbolArchiveResult uploadArchive = PrepareUploadInputs(profile, archive, log);
|
||
List<string> arguments = new List<string>
|
||
{
|
||
"-jar",
|
||
Quote(toolPath),
|
||
"-appid",
|
||
Quote(settings.buglyAppId.Trim()),
|
||
"-appkey",
|
||
Quote(settings.buglyAppKey.Trim()),
|
||
"-version",
|
||
Quote(profile.version),
|
||
"-buildNo",
|
||
Quote(profile.buildNumber.ToString()),
|
||
"-platform",
|
||
"Android"
|
||
};
|
||
|
||
if (PathExists(uploadArchive.nativeSymbolPath))
|
||
{
|
||
arguments.Add("-inputSymbol");
|
||
arguments.Add(Quote(uploadArchive.nativeSymbolPath));
|
||
}
|
||
|
||
if (File.Exists(uploadArchive.mappingPath))
|
||
{
|
||
arguments.Add("-inputMapping");
|
||
arguments.Add(Quote(uploadArchive.mappingPath));
|
||
}
|
||
|
||
string argumentText = string.Join(" ", arguments);
|
||
WriteLog(log, $"开始上传 Bugly 符号表:{javaPath} {MaskUploadArguments(argumentText)}");
|
||
|
||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||
{
|
||
FileName = javaPath,
|
||
Arguments = argumentText,
|
||
UseShellExecute = false,
|
||
RedirectStandardOutput = true,
|
||
RedirectStandardError = true,
|
||
CreateNoWindow = true,
|
||
StandardOutputEncoding = GetBuglyToolOutputEncoding(),
|
||
StandardErrorEncoding = GetBuglyToolOutputEncoding()
|
||
};
|
||
|
||
try
|
||
{
|
||
using (Process process = Process.Start(startInfo))
|
||
{
|
||
string output = process.StandardOutput.ReadToEnd();
|
||
string error = process.StandardError.ReadToEnd();
|
||
process.WaitForExit();
|
||
|
||
BuglySymbolUploadResult result = new BuglySymbolUploadResult
|
||
{
|
||
success = process.ExitCode == 0 && IsBuglyUploadOutputSuccessful(output, error),
|
||
exitCode = process.ExitCode,
|
||
output = output,
|
||
error = error,
|
||
command = javaPath + " " + MaskUploadArguments(argumentText)
|
||
};
|
||
result.logPath = WriteUploadLog(archive.archiveDirectory, result);
|
||
|
||
if (result.success)
|
||
{
|
||
WriteLog(log, $"Bugly 符号表上传完成,日志:{result.logPath}");
|
||
}
|
||
else
|
||
{
|
||
WriteLog(log, $"Bugly 符号表上传失败,ExitCode={result.exitCode},日志:{result.logPath}", LogType.Error);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
return FailUpload($"执行 Bugly 符号表上传失败:{e.Message}", log);
|
||
}
|
||
}
|
||
|
||
public static string BuildUploadCommandTemplate(BuildProfile profile, CrashReportBuglyProfileSettings settings, bool maskSecret)
|
||
{
|
||
if (profile == null || settings == null)
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
string buildOutputPath = ResolveFullPath(BuildPipelineCore.GetExpectedBuildOutputPath(profile));
|
||
string buildDirectory = Path.GetDirectoryName(buildOutputPath);
|
||
string buildName = Path.GetFileNameWithoutExtension(buildOutputPath);
|
||
string archiveDirectory = Path.Combine(buildDirectory, buildName + SymbolFolderSuffix);
|
||
string appKey = string.IsNullOrWhiteSpace(settings.buglyAppKey) ? "<Bugly App Key>" : settings.buglyAppKey.Trim();
|
||
if (maskSecret && !string.IsNullOrWhiteSpace(settings.buglyAppKey))
|
||
{
|
||
appKey = "******";
|
||
}
|
||
|
||
return (string.IsNullOrWhiteSpace(settings.buglyJavaPath) ? "java" : settings.buglyJavaPath.Trim()) +
|
||
" -jar " + Quote(ResolveSymbolToolPath(settings)) +
|
||
" -appid " + Quote(string.IsNullOrWhiteSpace(settings.buglyAppId) ? "<Bugly App ID>" : settings.buglyAppId.Trim()) +
|
||
" -appkey " + Quote(appKey) +
|
||
" -version " + Quote(profile.version) +
|
||
" -buildNo " + Quote(profile.buildNumber.ToString()) +
|
||
" -platform Android" +
|
||
" -inputSymbol " + Quote(Path.Combine(archiveDirectory, buildName + "_native-debug-symbols.zip")) +
|
||
" -inputMapping " + Quote(Path.Combine(archiveDirectory, buildName + "_mapping.txt"));
|
||
}
|
||
|
||
public static string ResolveSymbolToolPath(CrashReportBuglyProfileSettings settings)
|
||
{
|
||
if (settings != null && !string.IsNullOrWhiteSpace(settings.buglySymbolToolPath))
|
||
{
|
||
string configuredPath = ResolveFullPath(settings.buglySymbolToolPath);
|
||
if (File.Exists(configuredPath))
|
||
{
|
||
return configuredPath;
|
||
}
|
||
}
|
||
|
||
string bundledRoot = Path.Combine(GetProjectRoot(), "Tools", "BuglySymbolTool");
|
||
if (!Directory.Exists(bundledRoot))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
string newestPath = string.Empty;
|
||
DateTime newestWriteTime = DateTime.MinValue;
|
||
foreach (string jarPath in Directory.GetFiles(bundledRoot, "*.jar", SearchOption.AllDirectories))
|
||
{
|
||
DateTime writeTime = File.GetLastWriteTimeUtc(jarPath);
|
||
if (writeTime > newestWriteTime)
|
||
{
|
||
newestPath = jarPath;
|
||
newestWriteTime = writeTime;
|
||
}
|
||
}
|
||
|
||
return newestPath;
|
||
}
|
||
|
||
public static string ToProfilePath(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
string fullPath = ResolveFullPath(path);
|
||
string projectRoot = GetProjectRoot().TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||
string rootPrefix = projectRoot + Path.DirectorySeparatorChar;
|
||
if (fullPath.StartsWith(rootPrefix, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return fullPath.Substring(rootPrefix.Length).Replace(Path.DirectorySeparatorChar, '/');
|
||
}
|
||
|
||
return path;
|
||
}
|
||
|
||
private static bool IsAndroidProfile(BuildProfile profile)
|
||
{
|
||
if (profile == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
Type profileType = profile.GetType();
|
||
string targetPlatform = GetStringField(profileType, profile, "targetPlatform");
|
||
string buildTargetGroup = GetStringField(profileType, profile, "buildTargetGroup");
|
||
string buildTarget = GetStringField(profileType, profile, "buildTarget");
|
||
bool hasExplicitPlatformValue =
|
||
!string.IsNullOrWhiteSpace(targetPlatform) ||
|
||
!string.IsNullOrWhiteSpace(buildTargetGroup) ||
|
||
!string.IsNullOrWhiteSpace(buildTarget);
|
||
return IsPlatformValue(targetPlatform, "Android") ||
|
||
IsPlatformValue(buildTargetGroup, "Android") ||
|
||
IsPlatformValue(buildTarget, "Android") ||
|
||
(!hasExplicitPlatformValue && EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android);
|
||
}
|
||
|
||
private static string GetStringField(Type ownerType, object instance, string fieldName)
|
||
{
|
||
FieldInfo field = ownerType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
|
||
return field?.GetValue(instance) as string ?? string.Empty;
|
||
}
|
||
|
||
private static bool IsPlatformValue(string value, string expectedValue)
|
||
{
|
||
return !string.IsNullOrWhiteSpace(value) &&
|
||
string.Equals(value, expectedValue, StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
|
||
private static string FindNativeDebugSymbols(BuildProfile profile)
|
||
{
|
||
string buildType = profile != null && profile.isDevelopment ? "debug" : "release";
|
||
string preferredPath = Path.Combine(
|
||
GetProjectRoot(),
|
||
"Library",
|
||
"Bee",
|
||
"Android",
|
||
"Prj",
|
||
"IL2CPP",
|
||
"Gradle",
|
||
"launcher",
|
||
"build",
|
||
"outputs",
|
||
"native-debug-symbols",
|
||
buildType,
|
||
"native-debug-symbols.zip");
|
||
|
||
return File.Exists(preferredPath)
|
||
? preferredPath
|
||
: FindNewestFile(Path.Combine(GetProjectRoot(), "Library", "Bee", "Android"), "native-debug-symbols.zip");
|
||
}
|
||
|
||
private static string FindMappingFile(BuildProfile profile, string buildOutputPath)
|
||
{
|
||
string buildDirectory = Path.GetDirectoryName(buildOutputPath);
|
||
string newestOutputMapping = FindNewestFile(buildDirectory, "*mapping*.txt");
|
||
if (!string.IsNullOrEmpty(newestOutputMapping))
|
||
{
|
||
return newestOutputMapping;
|
||
}
|
||
|
||
string buildType = profile != null && profile.isDevelopment ? "debug" : "release";
|
||
string preferredRoot = Path.Combine(
|
||
GetProjectRoot(),
|
||
"Library",
|
||
"Bee",
|
||
"Android",
|
||
"Prj",
|
||
"IL2CPP",
|
||
"Gradle",
|
||
"launcher",
|
||
"build",
|
||
"outputs",
|
||
"mapping",
|
||
buildType);
|
||
|
||
string preferredMapping = FindNewestFile(preferredRoot, "mapping.txt");
|
||
return !string.IsNullOrEmpty(preferredMapping)
|
||
? preferredMapping
|
||
: FindNewestFile(Path.Combine(GetProjectRoot(), "Library", "Bee", "Android"), "mapping.txt");
|
||
}
|
||
|
||
private static string FindNewestFile(string root, string fileName)
|
||
{
|
||
if (string.IsNullOrEmpty(root) || !Directory.Exists(root))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
string newestPath = string.Empty;
|
||
DateTime newestWriteTime = DateTime.MinValue;
|
||
foreach (string file in Directory.GetFiles(root, fileName, SearchOption.AllDirectories))
|
||
{
|
||
DateTime writeTime = File.GetLastWriteTimeUtc(file);
|
||
if (writeTime > newestWriteTime)
|
||
{
|
||
newestPath = file;
|
||
newestWriteTime = writeTime;
|
||
}
|
||
}
|
||
|
||
return newestPath;
|
||
}
|
||
|
||
private static string WriteManifest(BuildProfile profile, string buildOutputPath, string archiveDirectory, BuglySymbolArchiveResult result)
|
||
{
|
||
BuglySymbolManifest manifest = new BuglySymbolManifest
|
||
{
|
||
productName = profile.productName,
|
||
bundleIdentifier = profile.bundleIdentifier,
|
||
version = profile.version,
|
||
buildNumber = profile.buildNumber,
|
||
buildOutputPath = buildOutputPath,
|
||
buildOutputSha256 = File.Exists(buildOutputPath) ? GetSHA256Hash(buildOutputPath) : string.Empty,
|
||
nativeSymbolPath = result.nativeSymbolPath,
|
||
nativeSymbolSha256 = File.Exists(result.nativeSymbolPath) ? GetSHA256Hash(result.nativeSymbolPath) : string.Empty,
|
||
mappingPath = result.mappingPath,
|
||
mappingSha256 = File.Exists(result.mappingPath) ? GetSHA256Hash(result.mappingPath) : string.Empty,
|
||
createdAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
|
||
};
|
||
|
||
string manifestPath = Path.Combine(archiveDirectory, "bugly-symbol-manifest.json");
|
||
File.WriteAllText(manifestPath, JsonUtility.ToJson(manifest, true), Encoding.UTF8);
|
||
return manifestPath;
|
||
}
|
||
|
||
private static string CreatePackage(string archiveDirectory, string packagePath)
|
||
{
|
||
if (File.Exists(packagePath))
|
||
{
|
||
File.Delete(packagePath);
|
||
}
|
||
|
||
ZipFile.CreateFromDirectory(
|
||
archiveDirectory,
|
||
packagePath,
|
||
System.IO.Compression.CompressionLevel.Optimal,
|
||
false);
|
||
return packagePath;
|
||
}
|
||
|
||
private static BuglySymbolArchiveResult PrepareUploadInputs(BuildProfile profile, BuglySymbolArchiveResult archive, LogHandler log)
|
||
{
|
||
string stagingRoot = Path.Combine(GetProjectRoot(), UploadStagingDirectory.Replace("/", Path.DirectorySeparatorChar.ToString()));
|
||
string stagingDirectory = Path.Combine(stagingRoot, BuildSafeStagingName(profile) + "_" + DateTime.Now.ToString("yyyyMMddHHmmss"));
|
||
Directory.CreateDirectory(stagingDirectory);
|
||
|
||
BuglySymbolArchiveResult uploadArchive = new BuglySymbolArchiveResult
|
||
{
|
||
hasSymbols = archive.hasSymbols,
|
||
archiveDirectory = archive.archiveDirectory,
|
||
packagePath = archive.packagePath,
|
||
manifestPath = archive.manifestPath,
|
||
message = archive.message
|
||
};
|
||
|
||
if (File.Exists(archive.nativeSymbolPath))
|
||
{
|
||
string symbolDirectory = Path.Combine(stagingDirectory, "symbols");
|
||
Directory.CreateDirectory(symbolDirectory);
|
||
ZipFile.ExtractToDirectory(archive.nativeSymbolPath, symbolDirectory);
|
||
uploadArchive.nativeSymbolPath = symbolDirectory;
|
||
WriteLog(log, $"已解压 Bugly SO 符号表到英文临时目录:{symbolDirectory}");
|
||
}
|
||
|
||
if (File.Exists(archive.mappingPath))
|
||
{
|
||
uploadArchive.mappingPath = Path.Combine(stagingDirectory, "mapping.txt");
|
||
File.Copy(archive.mappingPath, uploadArchive.mappingPath, true);
|
||
}
|
||
|
||
return uploadArchive;
|
||
}
|
||
|
||
private static string BuildSafeStagingName(BuildProfile profile)
|
||
{
|
||
string rawName = profile == null ? "bugly" : profile.profileName + "_" + profile.version + "_" + profile.buildNumber;
|
||
StringBuilder builder = new StringBuilder(rawName.Length);
|
||
foreach (char ch in rawName)
|
||
{
|
||
builder.Append(char.IsLetterOrDigit(ch) || ch == '_' || ch == '-' || ch == '.' ? ch : '_');
|
||
}
|
||
|
||
return builder.ToString();
|
||
}
|
||
|
||
private static string WriteUploadLog(string archiveDirectory, BuglySymbolUploadResult result)
|
||
{
|
||
string logDirectory = Directory.Exists(archiveDirectory) ? archiveDirectory : GetProjectRoot();
|
||
string logPath = Path.Combine(logDirectory, "bugly-symbol-upload.log");
|
||
File.WriteAllText(
|
||
logPath,
|
||
$"Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\nCommand: {result.command}\nExitCode: {result.exitCode}\n--- Output ---\n{result.output}\n--- Error ---\n{result.error}",
|
||
Encoding.UTF8);
|
||
return logPath;
|
||
}
|
||
|
||
private static BuglySymbolUploadResult FailUpload(string message, LogHandler log)
|
||
{
|
||
WriteLog(log, message, LogType.Error);
|
||
return new BuglySymbolUploadResult
|
||
{
|
||
success = false,
|
||
exitCode = -1,
|
||
error = message
|
||
};
|
||
}
|
||
|
||
private static bool PathExists(string path)
|
||
{
|
||
return File.Exists(path) || Directory.Exists(path);
|
||
}
|
||
|
||
private static Encoding GetBuglyToolOutputEncoding()
|
||
{
|
||
try
|
||
{
|
||
return Encoding.GetEncoding("GB18030");
|
||
}
|
||
catch
|
||
{
|
||
return Encoding.Default;
|
||
}
|
||
}
|
||
|
||
private static bool IsBuglyUploadOutputSuccessful(string output, string error)
|
||
{
|
||
string combinedText = (output ?? string.Empty) + "\n" + (error ?? string.Empty);
|
||
return combinedText.IndexOf("plugin execute result status: failure", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||
combinedText.IndexOf("event_res:failure", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||
combinedText.IndexOf("##[error]", StringComparison.OrdinalIgnoreCase) < 0 &&
|
||
combinedText.IndexOf("errorMsg is", StringComparison.OrdinalIgnoreCase) < 0;
|
||
}
|
||
|
||
private static string ResolveFullPath(string path)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(path))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
string normalizedPath = path.Replace("{inproject}: ", string.Empty);
|
||
return Path.IsPathRooted(normalizedPath)
|
||
? Path.GetFullPath(normalizedPath)
|
||
: Path.GetFullPath(Path.Combine(GetProjectRoot(), normalizedPath));
|
||
}
|
||
|
||
private static string GetProjectRoot()
|
||
{
|
||
return Directory.GetParent(Application.dataPath).FullName;
|
||
}
|
||
|
||
private static string Quote(string value)
|
||
{
|
||
return string.IsNullOrEmpty(value) ? "\"\"" : "\"" + value.Replace("\"", "\\\"") + "\"";
|
||
}
|
||
|
||
private static string MaskUploadArguments(string arguments)
|
||
{
|
||
return MaskArgumentValue(arguments, "-appkey");
|
||
}
|
||
|
||
private static string MaskArgumentValue(string arguments, string key)
|
||
{
|
||
int keyIndex = arguments.IndexOf(key, StringComparison.OrdinalIgnoreCase);
|
||
if (keyIndex < 0)
|
||
{
|
||
return arguments;
|
||
}
|
||
|
||
int valueStart = keyIndex + key.Length;
|
||
while (valueStart < arguments.Length && char.IsWhiteSpace(arguments[valueStart]))
|
||
{
|
||
valueStart++;
|
||
}
|
||
|
||
if (valueStart >= arguments.Length)
|
||
{
|
||
return arguments;
|
||
}
|
||
|
||
int tokenEnd = valueStart;
|
||
if (arguments[valueStart] == '"')
|
||
{
|
||
tokenEnd = arguments.IndexOf('"', valueStart + 1);
|
||
return tokenEnd > valueStart
|
||
? arguments.Substring(0, valueStart + 1) + "******" + arguments.Substring(tokenEnd)
|
||
: arguments;
|
||
}
|
||
|
||
while (tokenEnd < arguments.Length && !char.IsWhiteSpace(arguments[tokenEnd]))
|
||
{
|
||
tokenEnd++;
|
||
}
|
||
|
||
return arguments.Substring(0, valueStart) + "******" + arguments.Substring(tokenEnd);
|
||
}
|
||
|
||
private static string GetSHA256Hash(string filePath)
|
||
{
|
||
using (SHA256 sha256 = SHA256.Create())
|
||
using (FileStream stream = File.OpenRead(filePath))
|
||
{
|
||
return BitConverter.ToString(sha256.ComputeHash(stream)).Replace("-", "").ToLowerInvariant();
|
||
}
|
||
}
|
||
|
||
private static void WriteLog(LogHandler log, string message, LogType type = LogType.Log)
|
||
{
|
||
if (log != null)
|
||
{
|
||
log(message, type);
|
||
return;
|
||
}
|
||
|
||
switch (type)
|
||
{
|
||
case LogType.Error:
|
||
case LogType.Exception:
|
||
Debug.LogError(message);
|
||
break;
|
||
case LogType.Warning:
|
||
Debug.LogWarning(message);
|
||
break;
|
||
default:
|
||
Debug.Log(message);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
[Serializable]
|
||
public class BuglySymbolArchiveResult
|
||
{
|
||
public bool hasSymbols;
|
||
public string archiveDirectory;
|
||
public string packagePath;
|
||
public string nativeSymbolPath;
|
||
public string mappingPath;
|
||
public string manifestPath;
|
||
public string message;
|
||
}
|
||
|
||
[Serializable]
|
||
public class BuglySymbolUploadResult
|
||
{
|
||
public bool success;
|
||
public int exitCode;
|
||
public string command;
|
||
public string output;
|
||
public string error;
|
||
public string logPath;
|
||
}
|
||
|
||
[Serializable]
|
||
public class BuglySymbolManifest
|
||
{
|
||
public string productName;
|
||
public string bundleIdentifier;
|
||
public string version;
|
||
public int buildNumber;
|
||
public string buildOutputPath;
|
||
public string buildOutputSha256;
|
||
public string nativeSymbolPath;
|
||
public string nativeSymbolSha256;
|
||
public string mappingPath;
|
||
public string mappingSha256;
|
||
public string createdAt;
|
||
}
|
||
}
|