| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Guru.Editor | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     using System.IO; | 
					
						
							|  |  |  |     using UnityEditor; | 
					
						
							|  |  |  |     using UnityEngine; | 
					
						
							|  |  |  |     using System; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public class PgyerAPI | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         public const string Version = "1.0.0"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         internal static readonly string DefaultBashPathWin = "C:\\Program Files\\Git\\bin\\bash.exe"; | 
					
						
							|  |  |  |         internal static readonly string DefaultBashPathMac = "/bin/bash"; | 
					
						
							|  |  |  |         internal static readonly string ShellFile = "pgyer_upload.sh"; | 
					
						
							|  |  |  |         private static readonly string GuruAPIKey = "20a3d1106b802abbd84ec687eedf17eb"; | 
					
						
							|  |  |  |         private static readonly string PgyerHost = "https://www.pgyer.com"; | 
					
						
							|  |  |  |         internal static string WorkingDir => $"{Application.dataPath.Replace("Assets", "Library")}/guru_publish"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static string GetDownloadUrl(string shortUrl) => $"{PgyerHost}/{shortUrl}"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// 发布产品到蒲公英平台 | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="packagePath"></param> | 
					
						
							|  |  |  |         /// <param name="apiKey"></param> | 
					
						
							|  |  |  |         /// <param name="bashPath"></param> | 
					
						
							|  |  |  |         /// <param name="callback"></param> | 
					
						
							|  |  |  |         public static void PublishToPgyer(string packagePath, string apiKey = "", string bashPath = "", | 
					
						
							|  |  |  |             Action<string> callback = null) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (File.Exists(packagePath)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 Debug.Log($"=== START PUBLISH APP: {packagePath}"); | 
					
						
							|  |  |  |                 CheckWorkingDir(); | 
					
						
							|  |  |  |                 CallPublishShell(packagePath, apiKey, bashPath, callback); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private static void CheckWorkingDir() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (!Directory.Exists(WorkingDir)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 Directory.CreateDirectory(WorkingDir); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var file = $"{WorkingDir}/{ShellFile}"; | 
					
						
							|  |  |  |             if (!File.Exists(file)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var from = GetShellPath(); | 
					
						
							|  |  |  |                 if (File.Exists(from)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     File.Copy(from, file); | 
					
						
							|  |  |  | #if UNITY_EDITOR_OSX | 
					
						
							|  |  |  |                     RunCmd("chmod", $"+x {file}", workpath: WorkingDir); | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     Debug.LogError($"[Publisher] Source shell file not found :{from}"); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// 获取 CMD 命令路径 | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <returns></returns> | 
					
						
							|  |  |  |         private static string GetShellPath() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var path = ""; | 
					
						
							|  |  |  |             var guids = AssetDatabase.FindAssets($"{nameof(PgyerAPI)} t:script"); | 
					
						
							|  |  |  |             if (guids.Length > 0) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 path = Path.Combine(Directory.GetParent(AssetDatabase.GUIDToAssetPath(guids[0])).FullName, ShellFile); | 
					
						
							|  |  |  |                 return Path.GetFullPath(path); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             path = Path.GetFullPath( | 
					
						
							|  |  |  |                 $"{Application.dataPath.Replace("Assets", "Packages")}/com.guru.unity.sdk.core/Editor/BuildTool/{ShellFile}"); | 
					
						
							|  |  |  |             return path; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private static void RunCmd(string cmd, string args, Action<string> callback = null, string workpath = "") | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             System.Diagnostics.Process process = new System.Diagnostics.Process(); | 
					
						
							|  |  |  |             process.StartInfo.FileName = cmd; | 
					
						
							|  |  |  |             process.StartInfo.Arguments = args; | 
					
						
							|  |  |  |             process.StartInfo.UseShellExecute = false; | 
					
						
							|  |  |  |             process.StartInfo.RedirectStandardOutput = true; | 
					
						
							|  |  |  |             process.StartInfo.RedirectStandardError = true; | 
					
						
							|  |  |  |             process.StartInfo.CreateNoWindow = true; | 
					
						
							|  |  |  |             if (!string.IsNullOrEmpty(workpath)) process.StartInfo.WorkingDirectory = workpath; | 
					
						
							|  |  |  |             process.Start(); | 
					
						
							|  |  |  |             string log = process.StandardOutput.ReadToEnd(); | 
					
						
							|  |  |  |             callback?.Invoke(log); | 
					
						
							|  |  |  |             process.Close(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// 在 mac 下进行发布 | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="packagePath"></param> | 
					
						
							|  |  |  |         /// <param name="apiKey"></param> | 
					
						
							|  |  |  |         /// <param name="bashPath"></param> | 
					
						
							|  |  |  |         /// <param name="callback"></param> | 
					
						
							|  |  |  |         private static void CallPublishShell(string packagePath, string apiKey = "", string bashPath = "", | 
					
						
							|  |  |  |             Action<string> callback = null) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (string.IsNullOrEmpty(bashPath)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  | #if UNITY_EDITOR_OSX | 
					
						
							|  |  |  |                 bashPath = DefaultBashPathMac; | 
					
						
							|  |  |  | #elif UNITY_EDITOR_WIN | 
					
						
							|  |  |  |                 bashPath = DefaultBashPathWin.Replace("\\", "/"); | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!File.Exists(bashPath)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 string msg = $"Error: Bash file not found at path: {bashPath}! skip publishing!"; | 
					
						
							|  |  |  |                 Debug.LogError(msg); | 
					
						
							|  |  |  |                 callback?.Invoke(msg); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             packagePath = packagePath.Replace("\\", "/"); | 
					
						
							|  |  |  |             if (string.IsNullOrEmpty(apiKey)) apiKey = GuruAPIKey; | 
					
						
							|  |  |  |             var args = $"-c \"./{ShellFile} -k {apiKey} {packagePath}\""; | 
					
						
							|  |  |  |             // Debug.Log(bashPath); | 
					
						
							|  |  |  |             // Debug.Log(args); | 
					
						
							|  |  |  |             // Debug.Log(WorkingDir); | 
					
						
							|  |  |  |             RunCmd(bashPath, args, callback, WorkingDir); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     /// <summary> | 
					
						
							|  |  |  |     /// Guru 包体上传工具 | 
					
						
							|  |  |  |     /// </summary> | 
					
						
							|  |  |  |     public class GuruPublishHelper | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Check Env and Exe files | 
					
						
							|  |  |  |         private static string EvnCheck() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (!Directory.Exists(PgyerAPI.WorkingDir)) | 
					
						
							|  |  |  |                 Directory.CreateDirectory(PgyerAPI.WorkingDir); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             // #1 --- read from cached file with available path | 
					
						
							|  |  |  |             string bash_path = ""; | 
					
						
							|  |  |  |             var envFile = $"{PgyerAPI.WorkingDir}/.env"; | 
					
						
							|  |  |  |             if (File.Exists(envFile)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 bash_path = File.ReadAllText(envFile); | 
					
						
							|  |  |  |                 return bash_path; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // #2 --- Try to find bash exe file from default path | 
					
						
							|  |  |  |             bash_path = PgyerAPI.DefaultBashPathMac; | 
					
						
							|  |  |  | #if UNITY_EDITOR_WIN | 
					
						
							|  |  |  |             bash_path = PgyerAPI.DefaultBashPathWin; | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  |             if (File.Exists(bash_path)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 bash_path = bash_path.Replace("\\", "/"); | 
					
						
							|  |  |  |                 File.WriteAllText(envFile, bash_path); | 
					
						
							|  |  |  |                 return bash_path; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // #3 --- Try to let user select bash exe file from disk | 
					
						
							|  |  |  |             string title = "选择 bash 可执行文件"; | 
					
						
							|  |  |  |             string despath = "/bin"; | 
					
						
							|  |  |  |             string exts = "*"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if UNITY_EDITOR_WIN | 
					
						
							|  |  |  |             despath = "C:\\Program Files\\"; | 
					
						
							|  |  |  |             title = $"选择 bash 可执行文件, 例如: {despath}\\Git\\bin\\bash.exe"; | 
					
						
							|  |  |  |             exts = "exe"; | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  |             bash_path = EditorUtility.OpenFilePanel(title, despath, exts); | 
					
						
							|  |  |  |             if (File.Exists(bash_path)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 File.WriteAllText(envFile, bash_path.Replace("\\", "/")); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return bash_path; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         [MenuItem("Guru/Publish/Android APK...")] | 
					
						
							|  |  |  |         private static void EditorPublishAPK() | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             SelectAndPublish(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // [MenuItem("Guru/Publish/Publish Release AAB...")] | 
					
						
							|  |  |  |         // private static void EditorPublishAAB() | 
					
						
							|  |  |  |         // { | 
					
						
							|  |  |  |         //     SelectAndPublish("aab"); | 
					
						
							|  |  |  |         // } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-06 10:56:29 +00:00
										 |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// 直接发布版本 | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="appPath"></param> | 
					
						
							|  |  |  |         /// <param name="apiKey"></param> | 
					
						
							|  |  |  |         public static void Publish(string appPath, string apiKey = "") | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |         { | 
					
						
							|  |  |  |             string bash_path = EvnCheck(); | 
					
						
							|  |  |  |             if (string.IsNullOrEmpty(bash_path)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 ShowDialog("找不到 Bash 执行文件", $"Bash文件不存在: {bash_path}!"); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-03-06 10:56:29 +00:00
										 |  |  |             if (!File.Exists(appPath)) | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2024-03-06 10:56:29 +00:00
										 |  |  |                 ShowDialog("找不到包体文件", $"包体文件不存在: {appPath}!"); | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							| 
									
										
										
										
											2024-03-06 10:56:29 +00:00
										 |  |  |             PgyerAPI.PublishToPgyer(appPath, apiKey, bash_path, OnResponse); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// 选择文件及发布版本 | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="extension"></param> | 
					
						
							|  |  |  |         /// <param name="apiKey"></param> | 
					
						
							|  |  |  |         public static void SelectAndPublish(string extension = "apk", string apiKey = "") | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             string file = EditorUtility.OpenFilePanel("选择包体", $"~/Downloads", extension); | 
					
						
							|  |  |  |             Publish(file, apiKey); | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// Show system dialogs | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="title"></param> | 
					
						
							|  |  |  |         /// <param name="body"></param> | 
					
						
							|  |  |  |         /// <param name="callback"></param> | 
					
						
							|  |  |  |         /// <param name="cancelAction"></param> | 
					
						
							|  |  |  |         /// <param name="okName"></param> | 
					
						
							|  |  |  |         /// <param name="cancelName"></param> | 
					
						
							|  |  |  |         private static void ShowDialog(string title, string body, Action callback = null, Action cancelAction = null, string okName= "OK", string cancelName = "") | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (EditorUtility.DisplayDialog(title, body, okName, cancelName)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 callback?.Invoke(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 cancelAction?.Invoke(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /// <summary> | 
					
						
							|  |  |  |         /// On pgyer response callback | 
					
						
							|  |  |  |         /// </summary> | 
					
						
							|  |  |  |         /// <param name="log"></param> | 
					
						
							|  |  |  |         private static void OnResponse(string log) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var logPath = $"{PgyerAPI.WorkingDir}/log.txt"; | 
					
						
							|  |  |  |             File.WriteAllText(logPath, log); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             bool success = log.Contains(ResponseObject.HeadTag); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (success) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var json = log.Substring(log.IndexOf(ResponseObject.HeadTag, StringComparison.Ordinal)); | 
					
						
							|  |  |  |                 var res = ResponseObject.Parse(json); | 
					
						
							|  |  |  |                 if (res != null) | 
					
						
							|  |  |  |                 { | 
					
						
							| 
									
										
										
										
											2024-03-06 13:11:47 +00:00
										 |  |  |                     var url = PgyerAPI.GetDownloadUrl(res.ShortUrl()); | 
					
						
							|  |  |  |                     ShowDialog($"==== 上传成功 ({PgyerAPI.Version}) ====", $"包体 {res.BuildVersion()} ({res.BuildVersionCode()}) 上传成功!\n下载链接:{url}", () => | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |                     { | 
					
						
							| 
									
										
										
										
											2024-03-06 13:11:47 +00:00
										 |  |  |                         Debug.Log($"INSTALL URL:{url}"); // output url | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |                         Application.OpenURL(url); | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							| 
									
										
										
										
											2024-03-06 13:11:47 +00:00
										 |  |  |             ShowDialog($"==== 上传失败 ({PgyerAPI.Version}) ====", $"上传文件失败, 查看详细日志: \n{logPath}", () => | 
					
						
							| 
									
										
										
										
											2024-03-06 09:43:55 +00:00
										 |  |  |             { | 
					
						
							|  |  |  | #if UNITY_EDITOR_OSX | 
					
						
							|  |  |  |                 EditorUtility.RevealInFinder(PgyerAPI.WorkingDir); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  | #endif | 
					
						
							|  |  |  |                 Application.OpenURL(PgyerAPI.WorkingDir); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [Serializable] | 
					
						
							|  |  |  |     internal class ResponseObject | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         public int code; | 
					
						
							|  |  |  |         public string message; | 
					
						
							|  |  |  |         public PublishData data; | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         public static readonly string HeadTag = "{\"code\":"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public string BuildVersion() => data?.buildVersion ?? "0.0.0"; | 
					
						
							|  |  |  |         public string BuildVersionCode() => data?.buildVersionNo ?? "0"; | 
					
						
							|  |  |  |         public string ShortUrl() => data?.buildShortcutUrl ?? "#"; | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         public static bool IsValid(string json) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return json.Contains(HeadTag); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         public static ResponseObject Parse(string json) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (string.IsNullOrEmpty(json)) return null; | 
					
						
							|  |  |  |             if (!IsValid(json)) return null; | 
					
						
							|  |  |  |             return JsonUtility.FromJson<ResponseObject>(json); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [Serializable] | 
					
						
							|  |  |  |     internal class PublishData | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         public string buildIdentifier; | 
					
						
							|  |  |  |         public string buildQRCodeURL; | 
					
						
							|  |  |  |         public string buildShortcutUrl; | 
					
						
							|  |  |  |         public string buildName; | 
					
						
							|  |  |  |         public string buildVersion; | 
					
						
							|  |  |  |         public string buildVersionNo; | 
					
						
							|  |  |  |         public string buildUpdated; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |