338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			338 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
| 
 | |
| 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");
 | |
|         // }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 直接发布版本
 | |
|         /// </summary>
 | |
|         /// <param name="appPath"></param>
 | |
|         /// <param name="apiKey"></param>
 | |
|         public static void Publish(string appPath, string apiKey = "")
 | |
|         {
 | |
|             string bash_path = EvnCheck();
 | |
|             if (string.IsNullOrEmpty(bash_path))
 | |
|             {
 | |
|                 ShowDialog("找不到 Bash 执行文件", $"Bash文件不存在: {bash_path}!");
 | |
|                 return;
 | |
|             }
 | |
|             if (!File.Exists(appPath))
 | |
|             {
 | |
|                 ShowDialog("找不到包体文件", $"包体文件不存在: {appPath}!");
 | |
|                 return;
 | |
|             }
 | |
|             
 | |
|             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);
 | |
|         }
 | |
| 
 | |
|         /// <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)
 | |
|                 {
 | |
|                     var url = PgyerAPI.GetDownloadUrl(res.ShortUrl());
 | |
|                     ShowDialog($"==== 上传成功 ({PgyerAPI.Version}) ====", $"包体 {res.BuildVersion()} ({res.BuildVersionCode()}) 上传成功!\n下载链接:{url}", () =>
 | |
|                     {
 | |
|                         Debug.Log($"INSTALL URL:{url}"); // output url
 | |
|                         Application.OpenURL(url);
 | |
|                     });
 | |
| 
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             ShowDialog($"==== 上传失败 ({PgyerAPI.Version}) ====", $"上传文件失败, 查看详细日志: \n{logPath}", () =>
 | |
|             {
 | |
| #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;
 | |
|     }
 | |
| } |