打包测试脚本.md 25 KB

#灵感/代码缓存

BuildApp.cs

using HTFW.BuildPackage;
using UnityEditor;
using System;
using System.IO;
using Cysharp.Threading.Tasks;
using HTFW.Edit.BuildPackage;
using xasset.editor;
using HTFW.HotUpdate;
using HTFW.Edit.HotUpdate;
using Newtonsoft.Json;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Net;
using UnityEngine;


public class BuildApp
{

    #region 时间戳差值

    /// <summary>
    /// 将秒数时间戳转换为多久之前。传入时间戳t(t= 当前时间戳() - 指定时间的时间戳 )
    /// </summary>
    /// <param name="t"></param>
    /// <returns></returns>
    public static string GetTimeLongAgo(double t)
    {
        string str = "";
        double num;
        if (t < 60)
        {
            str = string.Format("{0}秒", t);
        }
        else if (t >= 60 && t < 3600)
        {
            num = Math.Floor(t / 60);
            str = string.Format("{0}分钟", num);
        }
        else if (t >= 3600 && t < 86400)
        {
            num = Math.Floor(t / 3600);
            str = string.Format("{0}小时", num);
        }
        else if (t > 86400 && t < 2592000)
        {
            num = Math.Floor(t / 86400);
            str = string.Format("{0}天", num);
        }
        else if (t > 2592000 && t < 31104000)
        {
            num = Math.Floor(t / 2592000);
            str = string.Format("{0}月", num);
        }
        else
        {
            num = Math.Floor(t / 31104000);
            str = string.Format("{0}年", num);
        }
        return str;
    }

    #endregion

    #region 文件处理

    public static void CopyDirectory(string sourceDir, string targetDir)
    {
        DirectoryInfo dir = new DirectoryInfo(sourceDir);
        DirectoryInfo[] dirs = dir.GetDirectories();
 
        // If the source directory does not exist, throw an exception.
        if (!dir.Exists)
        {
            throw new DirectoryNotFoundException($"Source directory does not exist or could not be found: {sourceDir}");
        }
 
        // If the destination directory does not exist, create it.
        if (!Directory.Exists(targetDir))
        {
            Directory.CreateDirectory(targetDir);
        }
 
        // Get the files in the directory and copy them to the new location.
        FileInfo[] files = dir.GetFiles();
        foreach (FileInfo file in files)
        {
            string tempPath = Path.Combine(targetDir, file.Name);
            file.CopyTo(tempPath, false);
        }
 
        // If copying subdirectories, copy them and their contents to the new location.
        foreach (DirectoryInfo subdir in dirs)
        {
            string tempPath = Path.Combine(targetDir, subdir.Name);
            CopyDirectory(subdir.FullName, tempPath);
        }
    }

    #endregion

    #region 网络处理

    public static JObject PostJson(string url, Dictionary<string, object> param)
    {
        string paramStr = JsonConvert.SerializeObject(param);
        byte[] data = Encoding.UTF8.GetBytes(paramStr);
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "application/json";

        Stream newStream = request.GetRequestStream();
        newStream.Write(data, 0, data.Length);
        newStream.Close();

        request.ServicePoint.Expect100Continue = false;
        request.ProtocolVersion = HttpVersion.Version11;

        var response = (HttpWebResponse)request.GetResponse();
        var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return JObject.Parse(responseString);
        }

        return null;
    }
    
    public static JObject GetJson(string url)
    {
        var request = (HttpWebRequest)WebRequest.Create(url);
        var response = (HttpWebResponse)request.GetResponse();
        var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return JObject.Parse(responseString);
        }
        return null;
    }

    /// <summary>
    /// 发送消息到飞书机器人
    /// </summary>
    public static void PostLark()
    {
        
        // if (Directory.Exists(RemoteHotUpdateRootPath))
        // {
        //     Directory.Delete(RemoteHotUpdateRootPath, true);
        // }
        
        // CopyDirectory(SourceHotUpdateRootPath, RemoteHotUpdateRootPath);
        
        Dictionary<string, object> dict = new Dictionary<string, object>();
        Dictionary<string,string> content = new Dictionary<string, string>();

        string timeDesc = GetTimeLongAgo((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000 - startTime);

        if (!isUpdate)
        {
            content.Add("text","本次打包完成,是整包\n" +
                               $"本次打包时间:{timeDesc}\n" +
                               $"对应的SVN分支是:{svn}\n" +
                               $"安卓迭代的版本号是:{bundleVersionCode}\n" +
                               $"本次打包对应分支:{Branch}\n" +
                               $"本次打包版本描述:{Description}\n" +
                               $"本次打包对应大版本:{Version}\n" +
                               $"本次打包最小强更版本:{MinHotUpdateVersion}\n" +
                               $"版本信息,是否上传到正式后台:{isUploadToReleaseServer}\n" +
                               $"安装包下载路径:{HFSRemotePath}{PlayerPrefs.GetString(AppPackageNameKey)}");
        }
        else
        {
            content.Add("text","本次打包完成,是热更包\n" +
                               $"本次热更时间:{timeDesc}\n" +
                               $"对应的SVN分支是:{svn}\n" +
                               $"本次对应热更版本号:{HotUpdateVersion}\n" +
                               $"安卓迭代的版本号是:{bundleVersionCode}\n" +
                               $"本次打包对应分支:{Branch}\n" +
                               $"本次打包版本描述:{Description}\n" +
                               $"本次打包对应大版本:{Version}\n" +
                               $"本次打包最小强更版本:{MinHotUpdateVersion}\n" +
                               $"安装包下载路径:{HFSRemotePath}{PlayerPrefs.GetString(AppPackageNameKey)}");
        }
        
        
        dict.Add("msg_type", "text");
        dict.Add("content", content);
        
        string url = "https://open.larksuite.com/open-apis/bot/v2/hook/94281312-1071-4de8-a640-0878e8aab174";
        PostJson(url, dict).ToString();
    }

    #endregion

    private static string AppPackageNameKey = "AppPackageNameKey";
    
    
    public static string svn = String.Empty; //svn分支地址
    public static BuildPackageConfig.BuildTargetType buildTarget; //目标平台
    public static int bundleVersionCode; //Android迭代版本号(每次打整包此处值++)
    public static BuildPackageConfig.AndroidBuildType androidBuildType; //打包类型
    public static bool isUploadToReleaseServer; //是否版本信息上传到正式后台(false:内网,true:外网)
    public static string Branch = String.Empty; //版本分支,需要在 http://192.168.1.31:8888/platform_new/#/login?redirect=/index 确认,是否存在该分支
    public static string Version = String.Empty; //打包 大版本
    public static bool isUpdate = false; //是否热更 布尔
    public static string _update = String.Empty; //是否热更 文本显示
    public static string HotUpdateVersion = String.Empty; //热更版本号
    public static string MinHotUpdateVersion = String.Empty; //最小热更版本 小于这个版本 会强制热更
    public static string Description = String.Empty; //版本描述
    public static string HFSRemotePath = String.Empty; //HFS IP服务器
    public static string HFSLocalPath = String.Empty;
    public static BuildPackageConst.ResServerType assetsServerType; //上传资源服务器类型
    
    public static string appName = String.Empty; //app名字

    private static long startTime; // 开始打包时间(以秒为单位)
    
    #region 打整包
    
    


    public static void BuildAPK()
    {
        
        startTime = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;

        string[] args = Environment.GetCommandLineArgs();
        

        foreach (var s in args)
        {
            if (s.Contains("--svn_"))
            {
                string _svn = s.Split('_')[1];
                svn = _svn;
            }
            else if (s.Contains("--BuildTarget:"))
            {
                string _buildTarget = s.Split(':')[1];
                if (_buildTarget == "Android")
                {
                    buildTarget = BuildPackageConfig.BuildTargetType.Android;
                }
                else if (_buildTarget == "Windows")
                {
                    buildTarget = BuildPackageConfig.BuildTargetType.Windows;
                }
                else if (_buildTarget == "Ios")
                {
                    buildTarget = BuildPackageConfig.BuildTargetType.Ios;
                }
            }
            else if (s.Contains("--BundleVersionCode:"))
            {
                string _bundleVersionCode = s.Split(':')[1];
                bundleVersionCode = int.Parse(_bundleVersionCode);
            }
            else if (s.Contains("--AndroidBuildType:"))
            {
                string _AndroidBuildType = s.Split(':')[1];
                if (_AndroidBuildType == "开发包")
                {
                    androidBuildType = BuildPackageConfig.AndroidBuildType.开发包;
                }
                else if (_AndroidBuildType == "谷歌包")
                {
                    androidBuildType = BuildPackageConfig.AndroidBuildType.谷歌包;
                }
            }
            else if (s.Contains("--Branch:"))
            {
                string _Branch = s.Split(':')[1];
                Branch = _Branch;
            }
            else if (s.Contains("--Version:"))
            {
                string _Version = s.Split(':')[1];
                Version = _Version;
            }
            else if (s.Contains("--HotUpdate:"))
            {
                _update = s.Split(':')[1];
                bool.TryParse(_update, out isUpdate);
            }
            else if (s.Contains("--IsUploadToReleaseServer:"))
            {
                string _IsUploadToReleaseServer = s.Split(':')[1];
                bool.TryParse(_IsUploadToReleaseServer, out isUploadToReleaseServer);
            }
            else if (s.Contains("--HotUpdateVersion:"))
            {
                string _HotUpdateVersion = s.Split(':')[1];
                HotUpdateVersion = _HotUpdateVersion;
            }
            else if (s.Contains("--MinHotUpdateVersion:"))
            {
                string _MinHotUpdateVersion = s.Split(':')[1];
                MinHotUpdateVersion = _MinHotUpdateVersion;
            }
            else if (s.Contains("--Description:"))
            {
                string _Description = s.Split(':')[1];
                Description = _Description;
            }
            else if (s.Contains("--HFSRemotePath_"))
            {
                string _HFSRemotePath = s.Split('_')[1];
                HFSRemotePath = _HFSRemotePath;
            }
            else if (s.Contains("--HFSLocalPath."))
            {
                string _HFSLocalPath = s.Split('.')[1];
                HFSLocalPath = _HFSLocalPath;
            }
            else if (s.Contains("--AssetsServerType."))
            {
                string _AssetsServerType = s.Split(':')[1];
                if (_AssetsServerType == "测试资源服HFS")
                {
                    assetsServerType = BuildPackageConst.ResServerType.测试资源服HFS;
                }
                else
                {
                    assetsServerType = BuildPackageConst.ResServerType.正式资源服AmazonCDN;
                }
            }
        
        }
        
        
        BuildPackageConfig buildPackageConfig =
            AssetDatabase.LoadAssetAtPath<BuildPackageConfig>("Assets/Scripts/BuildPackage/BuildPackageConfig.asset");
            SetupGameMode(buildPackageConfig.CurrentBuildType == BuildPackageConfig.AndroidBuildType.开发包);
            HotUpdateUtil.ClearLastPackageVersionInfo();

            buildPackageConfig.AssetsServerType = assetsServerType;
            
            if (buildPackageConfig.AssetsServerType == BuildPackageConst.ResServerType.测试资源服HFS)
            {
                buildPackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("HFSConfig");
                buildPackageConfig.VersionInfo.RemoteAssetsHost = HFSRemotePath;
                buildPackageConfig.CDNCfg.RemoteAssetsHost = HFSRemotePath;
            }
            else
            {
                buildPackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("AmazonCDNConfig");
            }
            
            
            buildPackageConfig.BuildTarget = buildTarget;
            buildPackageConfig.BundleVersionCode = bundleVersionCode;
            buildPackageConfig.CurrentBuildType = androidBuildType;
            buildPackageConfig.IsUploadToReleaseServer = isUploadToReleaseServer;
            buildPackageConfig.VersionInfo.Branch = Branch;
            buildPackageConfig.VersionInfo.Version = Version;
            buildPackageConfig.VersionInfo.HotUpdateVersion = HotUpdateVersion;
            buildPackageConfig.VersionInfo.MinHotUpdateVersion = MinHotUpdateVersion;
            buildPackageConfig.VersionInfo.Description = Description;
            
            

            bool hasExtension = buildPackageConfig.BuildTarget != BuildPackageConfig.BuildTargetType.Windows;
            if (!isUpdate)
            {
                appName = BuildPackageEditorUtil.GetPackageName(buildPackageConfig, hasExtension).Replace(" ","");
                PlayerPrefs.SetString(AppPackageNameKey,appName);
            }
            
            
            EditorUtility.SetDirty(buildPackageConfig);
            AssetDatabase.SaveAssetIfDirty(buildPackageConfig);
            
        if (isUpdate)
        {
            BuildHotUpdate();
            return;
        }
        
        //设置CustomSetting
        BuildPackageEditorUtil.SetupCustomSetting(buildPackageConfig);
        //设置ProjectSetting
        BuildPackageEditorUtil.SetupProjectSetting(buildPackageConfig);
        BuildPackageEditorUtil.UploadPackageVersionInfo(!buildPackageConfig.IsUploadToReleaseServer,
            buildPackageConfig.VersionInfo);
        var uploadCDN = CDNBase.GetCDN(buildPackageConfig.CDNCfg);
        
    
        //监听资源打包,资源有变更时,执行回调上传资源
        Builder.PostprocessBuildBundles = changes =>
        {
            //设置 DownloadURL,确保上传updateinfo.json前,下载地址已经设置正确
            Builder.SetUpdateInfoDownloadURL(buildPackageConfig.VersionInfo.GetRemoteAssetsRootPath(true));
            BuildHotUpdatePackageEditorUtil.UploadAA(true, changes, buildPackageConfig.VersionInfo, uploadCDN,
                isSuccess => { uploadCDN.Destroy(); },
                false);
        };
        
        //打整包AA资源
        AssetDatabase.Refresh();
        BuildPackageEditorUtil.BuildAA(buildPackageConfig.VersionInfo);
        AssetDatabase.Refresh();

        //保存版本信息到本地
        BuildPackageEditorUtil.SavePackageVersionInfo(buildPackageConfig.VersionInfo);
        AssetDatabase.Refresh();
        
        if (buildPackageConfig.BuildTarget == BuildPackageConfig.BuildTargetType.Android)
        {
            EditorUserBuildSettings.androidCreateSymbolsZip = buildPackageConfig.CreateSymbolsZip;
        }
        else
        {
            EditorUserBuildSettings.androidCreateSymbolsZip = false;
        }
        
        //设置包体资源为完整资源
        if (buildPackageConfig.CompleteResPkg)
        {
            BuildPackageEditorUtil.SetAssetsSplitModeToIncludeAll();
        }
        
        //开始打整包
        AssetDatabase.Refresh();
        BuildPlayer(appName,buildPackageConfig.IsDevelopmentBuild);
        //打开打包文件夹
        // EditorUtility.RevealInFinder(output);
        //还原游戏模式设置
        SetupGameMode(true);
        
        //还原包体资源设置
        if (buildPackageConfig.CompleteResPkg)
        {
            BuildPackageEditorUtil.ResetAssetsSplitMode();
        }
        //刷新
        AssetDatabase.Refresh();

        PostLark();
    }
    
    public static bool SetupGameMode(bool isDevelopMode)
    {
        string scriptPath = "Assets/Scripts/ConstDefine/GlobalConst.cs";
        string developmentStr =
            "public static Eliminate.GlobalEnum.GameMode GameMode = Eliminate.GlobalEnum.GameMode.Development;";
        string releaseStr =
            "public static Eliminate.GlobalEnum.GameMode GameMode = Eliminate.GlobalEnum.GameMode.Release;";
        if (!File.Exists(scriptPath))
        {
            return false;
        }

        string content = File.ReadAllText(scriptPath);
        if (isDevelopMode)
        {
            if (content.Contains(developmentStr))
                return true;
            else if (content.Contains(releaseStr))
                content = content.Replace(releaseStr, developmentStr);
            else
            {
                return false;
            }
        }
        else
        {
            if (content.Contains(developmentStr))
                content = content.Replace(developmentStr, releaseStr);
            else if (content.Contains(releaseStr))
                return true;
            else
            {
                return false;
            }
        }

        File.WriteAllText(scriptPath, content);
        return true;
    }
    
    /// <summary>
    /// 打包(运行文件)
    /// </summary>
    /// <param name="cfg">打包配置文件</param>
    /// <returns></returns>
    public static void BuildPlayer(string appName, bool isDevelopmentBuild)
    {
        
        string output = $"{HFSLocalPath}/{appName}";
        // string output = BuildPackageEditorUtil.GetLocalPackageFullPath(cfg);
        if (File.Exists(output))
        {
            File.Delete(output);
        }

        var buildOptions = isDevelopmentBuild ? BuildOptions.Development : BuildOptions.None;
        BuildPipeline.BuildPlayer(BuildPackageEditorUtil.FindEnableEditorScenes(), output, EditorUserBuildSettings.activeBuildTarget,
            buildOptions);
    }
    

    #endregion

    #region 打热更包
    
    static bool isUploadAllResUpdate = true;
    static bool isFinishUploadRes = false;
    static bool isUploadResSuccess = false;
    

    public static void BuildHotUpdate()
    {


        string assetPath = "Assets/Scripts/HotUpdate/Edit/BuildHotUpdatePackageConfig.asset";
        BuildHotUpdatePackageConfig buildHotUpdatePackageConfig =
            AssetDatabase.LoadAssetAtPath<BuildHotUpdatePackageConfig>(assetPath);
        // Internal_BuildPackageHotUpdate(buildHotUpdatePackageConfig);

        buildHotUpdatePackageConfig.AssetsServerType = assetsServerType;
        if (buildHotUpdatePackageConfig.AssetsServerType == BuildPackageConst.ResServerType.测试资源服HFS)
        {
            buildHotUpdatePackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("HFSConfig");
            buildHotUpdatePackageConfig.VersionInfo.RemoteAssetsHost = HFSRemotePath;
            buildHotUpdatePackageConfig.CDNCfg.RemoteAssetsHost = HFSRemotePath;
        }
        else
        {
            buildHotUpdatePackageConfig.CDNCfg = BuildPackageEditorUtil.FindCDNConfig("AmazonCDNConfig");
        }

            
        //初始化VersionInfo
        buildHotUpdatePackageConfig.VersionInfo.InitCDNConfig(buildHotUpdatePackageConfig.CDNCfg.RemoteAssetsHost,
            buildHotUpdatePackageConfig.CDNCfg.RemoteHotUpdateRootPath);

        buildHotUpdatePackageConfig.IsUploadToReleaseServer = isUploadToReleaseServer;
        buildHotUpdatePackageConfig.VersionInfo.Branch = Branch;
        buildHotUpdatePackageConfig.VersionInfo.Version = Version;
        buildHotUpdatePackageConfig.VersionInfo.HotUpdateVersion = HotUpdateVersion;
        buildHotUpdatePackageConfig.VersionInfo.MinHotUpdateVersion = MinHotUpdateVersion;
        buildHotUpdatePackageConfig.VersionInfo.Description = Description;

        // buildHotUpdatePackageConfig.VersionInfo.HotUpdateVersion = (int.Parse(BuildPackageEditorUtil.LoadPackageVersionInfo().HotUpdateVersion) + 1).ToString(); //热更版本号 自增

        EditorUtility.SetDirty(buildHotUpdatePackageConfig);
        AssetDatabase.SaveAssetIfDirty(buildHotUpdatePackageConfig);

        //保存版本信息到本地
        BuildPackageEditorUtil.SavePackageVersionInfo(buildHotUpdatePackageConfig.VersionInfo);
        AssetDatabase.Refresh();
        

        //-------------------- 版本信息检查 --------------------//
        if (buildHotUpdatePackageConfig.CDNCfg == null || !buildHotUpdatePackageConfig.CDNCfg.IsEnable || !buildHotUpdatePackageConfig.CDNCfg.IsValid())
        {
            // Debug.LogError("需要有效 CDN 配置!");
            return;
        }

        //是否使用正式后台
        buildHotUpdatePackageConfig.VersionInfo.IsReleaseServer = buildHotUpdatePackageConfig.IsUploadToReleaseServer;
            
        //检测版本信息填写是否合法
        // Debug.Log("开始检测版本信息填写是否合法...");
        if (!CheckPackageVersionInfoValid(buildHotUpdatePackageConfig.VersionInfo))
        {
            // Debug.LogError("版本信息填写不合法, 打包失败!");
            return;
        }

        // Debug.Log("版本信息填写合法.");
            
        //-------------------- 打热更包 --------------------//
        // Debug.Log("开始检查打包资源前,本地资源状态跟远端是否一致...");
        // if (!CheckLocalResStatusHotUpdate(buildHotUpdatePackageConfig))
        // {
        //     return;
        // }
            
        //-------------------- 监听资源打包--------------------//
        var uploadCDN = CDNBase.GetCDN(buildHotUpdatePackageConfig.CDNCfg);
        //监听资源打包,打包完成后执行后处理,上传热更AA资源到CDN
        Builder.PostprocessBuildBundles = changes =>
        {
            //设置 DownloadURL,确保上传updateinfo.json前,下载地址已经设置正确
            Builder.SetUpdateInfoDownloadURL(buildHotUpdatePackageConfig.VersionInfo.GetRemoteAssetsRootPath(true));
            BuildHotUpdatePackageEditorUtil.UploadAA(true, changes, buildHotUpdatePackageConfig.VersionInfo, uploadCDN,
                isSuccess =>
                {
                    isFinishUploadRes = true;
                    isUploadResSuccess = isSuccess;
                    uploadCDN.Destroy();
                },
                false);
        };
            
        //打热更AA资源
        // Debug.Log("开始打热更AA资源...");
        BuildHotUpdatePackageEditorUtil.BuildAA(buildHotUpdatePackageConfig.VersionInfo);
        // Debug.Log("热更AA资源打包完成!");
            
        //等待资源上传逻辑完成
        // while (!isFinishUploadRes)
        // {
        //     UniTask.Delay(100); // 每隔一段时间进行检查,避免阻塞主线程
        // }
        //由于内部使用了Unity的网络请求Api,所以必须回到unity主线程调用该异步函数
        UploadPackageVersionInfoHotUpdate(isUploadResSuccess, buildHotUpdatePackageConfig.VersionInfo,buildHotUpdatePackageConfig).Forget();
        
    }
    
    public static bool CheckPackageVersionInfoValid(PackageVersionInfo info)
    {
        if (info == null)
        {
            // Debug.LogError("版本信息不能为空!");
            return false;
        }

        if (!info.CheckValid())
        {
            // Debug.LogError("版本信息输入有误!");
            return false;
        }

        return true;
    }
    
    
    /// <summary>
    /// 上传版本信息
    /// </summary>
    /// <param name="isUploadResSuccess"></param>
    /// <param name="info"></param>
    static async UniTask UploadPackageVersionInfoHotUpdate(bool isUploadResSuccess, PackageVersionInfo info,BuildHotUpdatePackageConfig buildHotUpdatePackageConfig)
    {
        if (isUploadResSuccess)
        {
            //上传版本信息到后台
            // Debug.Log("上传版本信息到后台...");
            if (!await BuildPackageEditorUtil.UploadPackageVersionInfo(!buildHotUpdatePackageConfig.IsUploadToReleaseServer, info)){
            }
        }
        else
        {
            // Debug.LogError($"上传资源失败!热更包打包失败.");
        }

        PostLark();
    }
    

    #endregion
    
}