You've already forked CC-Framework.CrashReport
feat: move bugly build settings into crashreport
This commit is contained in:
651
Assets/Editor/BuglyAndroidSymbolUtility.cs
Normal file
651
Assets/Editor/BuglyAndroidSymbolUtility.cs
Normal file
@@ -0,0 +1,651 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user