当前位置: 首页 > article >正文

使用jenkins打包unity工程

Apache配置
  • 安装:arch arm64 brew install httpd
  • 开启:brew services start httpd
  • 重启:brew services restart httpd
  • 停止:brew services stop httpd
  • 配置文件路径:/opt/homebrew/etc/httpd/httpd.conf,默认监听8080端口,更改端口号后,要重启一下brew services restart httpd
  • SSL文件路径 /opt/homebrew/etc/httpd/extra/httpd-ssl.conf,默认监听 8443,Apache 默认使用 443 端口来处理 HTTPS 请求。由于 1024 以下的端口通常需要超级用户权限才能绑定,因此如果您希望 Apache 在没有 `sudo` 权限的情况下运行,您需要将其配置为使用高于 1024 的端口(如 8443) 
  • 服务器文件存放目录 /opt/homebrew/var/www,把文件放到这个文件夹下,别人可以通过访问ip+port+相对地址访问
Jenkins配置
  • 安装:Jenkins自动打包并部署到远程服务器_jenkins客户端打包好处-CSDN博客
  • 局域网ip访问不了jenkins问题:jenkins局域网无法访问 - 简书
  • 批量删除构建历史:
Jenkins.instance.getItemByFullName(jobName).builds.findAll {
  it.number <= maxNumber
}.each {
  it.delete()
}
  • Jenkins的参数一切皆字符串,bool类型参数也是字符串,通过when{expression{return BoolParam.toBoolean()}}判断
  • Unity 访问命令行参数:
  • string[] args = System.Environment.GetCommandLineArgs();    
    //每个空格都是一个参数 比如 -project testPath customParam1:111 customParam2:222
    static string GetSingleCommandlineArgs(string[] args, string key)
    {
            string value = String.Empty;
            foreach (var arg in args)
            {
                Debug.Log("命令行参数:args:" + arg);
                if (arg.Contains(key))
                {
                    value = arg;
                    break;
                }
            }
    
            return value;
    }
  • Unity iOS后处理方法,包括更改Build Phase的顺序:
  • #if UNITY_IOS
    
    using UnityEngine;
    using UnityEditor;
    using UnityEditor.Callbacks;
    using System.IO;
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using AMPSDK.Utils;
    using UnityEditor.iOS.Xcode;
    using UnityEditor.iOS.Xcode.Extensions;
    
    
    namespace EditorBuildTool
    {
        public static class AviaXCodeSetter
        {
            private const string BtApplePayMerchantId = "";
            private const string UtApplePayMerchantId = "";
            private const string TeamId = "";
            private const string AppGroup = "";
            private const string DeepLinkDomain = "";
            private const string CrashlyticsShellScriptPhaseName = "Crashlytics Run Script";
            private const string EmbedAppExtensionsPhaseName = "Embed App Extensions";
    
            public static bool IsUploadIpa = false;
    
    
            private static string TrustFrameworkPath => Path.Combine(Application.dataPath, "../Archive/iOS/Trustly");
    
            private static string GoogleInfoPlistPath =>
                Path.Combine(Application.dataPath, "../Archive/iOS/Test/GoogleService-Info.plist");
    
            [PostProcessBuild(Int32.MaxValue - 1)]
            public static void OnPostProcessBuild(BuildTarget target, string path2BuildProject)
            {
                if (target != BuildTarget.iOS)
                    return;
    
                Debug.LogWarning(path2BuildProject);
                ModifyProjectSettings(path2BuildProject);
                ModifyPodProjectSettings(path2BuildProject);
                Process(path2BuildProject);
            }
    
            static void ModifyProjectSettings(string path2BuildProject)
            {
                string projPath = path2BuildProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
                PBXProject project = new PBXProject();
                project.ReadFromFile(projPath);
    
                AddNotificationTargets(project, path2BuildProject); //临时屏蔽
    
                SubModifyProjectSettings(path2BuildProject, project);
    
                File.WriteAllText(projPath, project.WriteToString());
            }
    
            static void AddNotificationTargets(PBXProject pbxProject, string path2BuildProject)
            {
                string notificationTestPath =
                    Path.Combine(Application.dataPath, "../Archive/iOS/NotificationTest/NotificationService");
    
                Directory.CreateDirectory(Path.Combine(path2BuildProject, "NotificationService"));
    
                CopyFileToPath(path2BuildProject, $"{notificationTestPath}/Info.plist",
                    "NotificationService/Info.plist", pbxProject, true);
    
                CopyFileToPath(path2BuildProject, $"{notificationTestPath}/NotificationService.h",
                    "NotificationService/NotificationService.h", pbxProject, true);
    
                string mPath = CopyFileToPath(path2BuildProject, $"{notificationTestPath}/NotificationService.m",
                    "NotificationService/NotificationService.m", pbxProject, true);
    
    
                string guid = pbxProject.AddAppExtension(pbxProject.GetUnityMainTargetGuid(), "NotificationService",
                    $"{Application.identifier}.NotificationServices", "NotificationService/Info.plist");
    
                pbxProject.AddFileToBuild(guid, mPath);
    
                #region 子模块的build setting
    
                pbxProject.SetBuildProperty(guid, "CODE_SIGN_IDENTITY", "Apple Development");
                pbxProject.SetBuildProperty(guid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
                pbxProject.SetBuildProperty(guid, "ARCHS", "arm64");
                pbxProject.SetBuildProperty(guid, "GENERATE_INFOPLIST_FILE", "YES");
                pbxProject.SetBuildProperty(guid, "CURRENT_PROJECT_VERSION", "0");
                pbxProject.SetBuildProperty(guid, "MARKETING_VERSION", "1.2");
                pbxProject.SetBuildProperty(guid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
                pbxProject.SetTeamId(guid, TeamId);
                pbxProject.AddFrameworkToProject(guid, "UserNotifications.framework", false);
    
                CopyFileToPath(path2BuildProject, $"{notificationTestPath}/NotificationService.entitlements",
                    "NotificationService/NotificationService.entitlements", pbxProject, true);
                pbxProject.AddBuildPropertyForConfig(pbxProject.BuildConfigByName(guid, "Debug"),
                    "CODE_SIGN_ENTITLEMENTS", "NotificationService/NotificationService.entitlements");
                pbxProject.AddBuildPropertyForConfig(pbxProject.BuildConfigByName(guid, "Release"),
                    "CODE_SIGN_ENTITLEMENTS", "NotificationService/NotificationService.entitlements");
                pbxProject.AddBuildPropertyForConfig(pbxProject.BuildConfigByName(guid, "ReleaseForRunning"),
                    "CODE_SIGN_ENTITLEMENTS", "NotificationService/NotificationService.entitlements");
                pbxProject.AddBuildPropertyForConfig(pbxProject.BuildConfigByName(guid, "ReleaseForProfiling"),
                    "CODE_SIGN_ENTITLEMENTS", "NotificationService/NotificationService.entitlements");
                
                if (IsUploadIpa)
                {
                    pbxProject.SetBuildProperty(guid, "CODE_SIGN_STYLE", "Manual");
                    pbxProject.SetBuildProperty(guid, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "钥匙串中证书的名字");
                    pbxProject.SetBuildProperty(guid, "CODE_SIGN_IDENTITY", "钥匙串中证书的名字");
    
                    pbxProject.SetBuildProperty(guid, "PROVISIONING_PROFILE_SPECIFIER", "Unity SDK Demo Provision Notification");
                }
                else
                {
                    pbxProject.SetBuildProperty(guid, "CODE_SIGN_STYLE", "Automatic");
                }
    
                #endregion
    
    
                #region 子模块的entitlement
    
                string relativeEntitlementFilePath = "NotificationService/NotificationService.entitlements";
                string absoluteEntitlementFilePath = path2BuildProject + "/" + relativeEntitlementFilePath;
    
                PlistDocument notifyEntitlement = new PlistDocument();
                if (!string.IsNullOrEmpty(AppGroup))
                {
                    pbxProject.AddCapability(guid, PBXCapabilityType.AppGroups);
                    string appGroupPlist = "com.apple.security.application-groups";
                    var appGroupArray = new PlistElementArray();
                    appGroupArray.AddString(AppGroup);
                    notifyEntitlement.root[appGroupPlist] = appGroupArray;
                }
                else
                {
                    AviaLogger.AMPLogWarn("app group 为空");
                }
    
                notifyEntitlement.WriteToFile(absoluteEntitlementFilePath);
                ModifyEntitlementFile(absoluteEntitlementFilePath);
    
                #endregion
    
    
                #region 子模块的info.plist
    
                string plistPath = $"{path2BuildProject}/NotificationService/Info.plist";
                PlistDocument plist = new PlistDocument();
                plist.ReadFromString(File.ReadAllText(plistPath));
                PlistElementDict infoDict = plist.root;
                PlistElementDict bmDict;
                if (!infoDict.values.ContainsKey("NSAppTransportSecurity"))
                    bmDict = infoDict.CreateDict("NSAppTransportSecurity");
                else
                    bmDict = infoDict.values["NSAppTransportSecurity"].AsDict();
                bmDict.SetBoolean("NSAllowsArbitraryLoads", true);
    
                infoDict.SetString("CFBundleDisplayName", "AviaNotificationServiceExtension");
                infoDict.SetString("CFBundleVersion", PlayerSettings.iOS.buildNumber);
                File.WriteAllText(plistPath, plist.WriteToString());
    
                #endregion
            }
    
    
            private static void ModifyPodProjectSettings(string path2BuildProject)
            {
                string projPath = path2BuildProject + "/Pods/Pods.xcodeproj/project.pbxproj";
                PBXProject project = new PBXProject();
                project.ReadFromFile(projPath);
                SubModifyPodProjectSettings(path2BuildProject, project);
    
                File.WriteAllText(projPath, project.WriteToString());
            }
    
    
            private static string CopyFileToPath(string path2BuildProject, string fileAbsolutePath, string fileReactivePath,
                PBXProject project = null, bool addToProject = true)
            {
                string newPath = Path.Combine(path2BuildProject, fileReactivePath);
    
                if (File.Exists(fileAbsolutePath))
                {
                    if (File.Exists(newPath))
                    {
                        File.Delete(newPath);
                    }
    
                    File.Copy(fileAbsolutePath, newPath);
                    if (addToProject)
                    {
                        return project?.AddFile(newPath, fileReactivePath, PBXSourceTree.Source);
                    }
                }
                else
                {
                    Debug.LogWarning("文件不存在:" + fileAbsolutePath);
                }
    
                return "";
            }
    
    
            private static string CopyDirectoryToPath(string path2BuildProject, string fileAbsolutePath,
                string fileReactivePath,
                PBXProject project = null, bool addToProject = true)
            {
                string newPath = Path.Combine(path2BuildProject, fileReactivePath);
    
                if (Directory.Exists(fileAbsolutePath))
                {
                    if (Directory.Exists(newPath))
                    {
                        Directory.Delete(newPath);
                    }
    
                    FileUtil.CopyFileOrDirectory(fileAbsolutePath, newPath);
                    if (addToProject)
                    {
                        return project?.AddFile(newPath, fileReactivePath, PBXSourceTree.Source);
                    }
                }
                else
                {
                    Debug.LogWarning("文件夹不存在:" + fileAbsolutePath);
                }
    
                return "";
            }
    
            private static void SubModifyPodProjectSettings(string path2BuildProject, PBXProject project)
            {
                string brainTreeDropInGuid = project.TargetGuidByName("BraintreeDropIn-BraintreeDropIn-Localization");
                project.SetTeamId(brainTreeDropInGuid, TeamId);
    
                string checkoutFrameGuid = project.TargetGuidByName("Frames-Frames");
                if (!string.IsNullOrEmpty(checkoutFrameGuid))
                {
                    project.SetTeamId(checkoutFrameGuid, TeamId);
                }
    
                string adyenFrameGuid = project.TargetGuidByName("Adyen-Adyen");
                if (!string.IsNullOrEmpty(adyenFrameGuid))
                {
                    project.SetTeamId(adyenFrameGuid, TeamId);
                    string adyenActionFrameGuid = project.TargetGuidByName("Adyen-AdyenActions");
                    project.SetTeamId(adyenActionFrameGuid, TeamId);
                    string adyenCardFrameGuid = project.TargetGuidByName("Adyen-AdyenCard");
                    project.SetTeamId(adyenCardFrameGuid, TeamId);
                }
    
    
                string awsCoreGuid = project.TargetGuidByName("AWSCore");
                if (!string.IsNullOrEmpty(awsCoreGuid))
                    project.SetBuildProperty(awsCoreGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
    
                string awsS3Guid = project.TargetGuidByName("AWSS3");
                if (!string.IsNullOrEmpty(awsS3Guid))
                    project.SetBuildProperty(awsS3Guid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
    
                string masonryGuid = project.TargetGuidByName("Masonry");
                if (!string.IsNullOrEmpty(masonryGuid))
                    project.SetBuildProperty(masonryGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
    
                string openUdidGuid = project.TargetGuidByName("OpenUDID");
                if (!string.IsNullOrEmpty(openUdidGuid))
                    project.SetBuildProperty(openUdidGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
    
                string reachabilityGuid = project.TargetGuidByName("Reachability");
                if (!string.IsNullOrEmpty(reachabilityGuid))
                    project.SetBuildProperty(reachabilityGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
    
                string uicKeyChainStoreGuid = project.TargetGuidByName("UICKeyChainStore");
                if (!string.IsNullOrEmpty(uicKeyChainStoreGuid))
                    project.SetBuildProperty(uicKeyChainStoreGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
            }
    
            private static void SubModifyProjectSettings(string path2BuildProject, PBXProject project)
            {
                string unityFrameworkTargetGuid = project.GetUnityFrameworkTargetGuid();
                string unityMainTargetGuid = project.GetUnityMainTargetGuid();
                string unityProjectGuid = project.ProjectGuid();
                string iosFolderPath = Path.Combine(Application.dataPath, $"../Archive/iOS/Test");
                if (!Directory.Exists(iosFolderPath))
                {
                    Debug.LogError($"haven't found folder:{iosFolderPath}");
                    return;
                }
    
                //var info = new DirectoryInfo(iosFolderPath);
                //iosFolderPath = info.FullName;
                // 拷贝Podfile文件到工程
                //CopyToPath(path2BuildProject, $"{iosFolderPath}/Podfile", "Podfile");
                // 签名信息(可以没有,打包机上ExportOptions里有配置,但不用打包机的情况下,还是可以加一下的)ps:加了也没关系,还是打包机优先级最高
    
                if (!string.IsNullOrEmpty(TeamId))
                    project.SetTeamId(unityMainTargetGuid, TeamId);
                else
                    AviaLogger.AMPLogWarn("team id 为空");
    
                CopyFileToPath(path2BuildProject, GoogleInfoPlistPath, "GoogleService-Info.plist", project, true);
    
                #region 添加framework
    
                project.AddFrameworkToProject(unityFrameworkTargetGuid, "AdSupport.framework", false);
                project.AddFrameworkToProject(unityFrameworkTargetGuid, "AppTrackingTransparency.framework", false);
                project.AddFrameworkToProject(unityFrameworkTargetGuid, "Photos.framework", false);
                project.AddFrameworkToProject(unityFrameworkTargetGuid, "UserNotifications.framework", false);
    
                // 添加非系统框架,及文件
                string payFrameworkGuid = CopyDirectoryToPath(path2BuildProject,
                    $"{TrustFrameworkPath}/PayWithMyBank.xcframework",
                    "Frameworks/PayWithMyBank.xcframework", project, true);
    
                // if (!string.IsNullOrEmpty(payFrameworkGuid))
                // {
                //     project.AddFileToEmbedFrameworks(unityIPhoneGUID, payFrameworkGuid);
                //     project.AddFileToEmbedFrameworks(unityFrameworkGUID, payFrameworkGuid);
                // }
                // else
                // {
                //     Debug.LogWarning("trust frame work 不存在");
                // }
    
                #endregion
    
                #region 调整BuildSettings
    
                project.SetBuildProperty(unityMainTargetGuid, "CODE_SIGN_IDENTITY", "Apple Development");
                project.SetBuildProperty(unityMainTargetGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
                project.SetBuildProperty(unityFrameworkTargetGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
                project.SetBuildProperty(unityProjectGuid, "IPHONEOS_DEPLOYMENT_TARGET", "12.0");
                project.SetBuildProperty(unityMainTargetGuid, "GCC_C_LANGUAGE_STANDARD", "c99");
                project.SetBuildProperty(unityFrameworkTargetGuid, "GCC_C_LANGUAGE_STANDARD", "c99");
                project.SetBuildProperty(unityProjectGuid, "GCC_C_LANGUAGE_STANDARD", "c99");
                project.SetBuildProperty(unityFrameworkTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "NO");
    
    
                Debug.Log("----------build ipa--------------"+IsUploadIpa);
                if (IsUploadIpa)
                {
                    project.SetBuildProperty(unityProjectGuid, "CODE_SIGN_STYLE", "Manual");
                    project.SetBuildProperty(unityMainTargetGuid, "CODE_SIGN_STYLE", "Manual");
                    project.SetBuildProperty(unityMainTargetGuid, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "钥匙串中证书的名字");
                    project.SetBuildProperty(unityMainTargetGuid, "CODE_SIGN_IDENTITY", "钥匙串中证书的名字");
                    project.SetBuildProperty(unityMainTargetGuid, "PROVISIONING_PROFILE_SPECIFIER", "provision 文件的名字,不带后缀");
                }
                else
                {
                    project.SetBuildProperty(unityMainTargetGuid, "CODE_SIGN_STYLE", "Automatic");
                }
                
                var token = project.GetBuildPropertyForAnyConfig(unityProjectGuid, "USYM_UPLOAD_AUTH_TOKEN");
                if (string.IsNullOrEmpty(token))
                {
                    token = "FakeToken";
                }
    
                project.SetBuildProperty(unityMainTargetGuid, "USYM_UPLOAD_AUTH_TOKEN", token);
                project.SetBuildProperty(unityProjectGuid, "USYM_UPLOAD_AUTH_TOKEN", token);
                project.SetBuildProperty(unityFrameworkTargetGuid, "USYM_UPLOAD_AUTH_TOKEN", token);
    
                project.SetBuildProperty(unityMainTargetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
                project.SetBuildProperty(unityFrameworkTargetGuid, "GCC_ENABLE_OBJC_EXCEPTIONS", "YES");
    
                project.SetBuildProperty(unityMainTargetGuid, "ENABLE_BITCODE", "FALSE");
                project.SetBuildProperty(unityFrameworkTargetGuid, "ENABLE_BITCODE", "FALSE");
    
                project.AddBuildProperty(unityProjectGuid, "OTHER_LDFLAGS", "-ObjC -ld_classic");
    
                project.SetBuildProperty(unityMainTargetGuid, "CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES",
                    "YES");
                project.SetBuildProperty(unityFrameworkTargetGuid, "CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES",
                    "YES");
    
                // entitlements : Apple Pay Keychain ...
                CopyFileToPath(path2BuildProject, $"{iosFolderPath}/Unity-iPhoneDebug.entitlements",
                    "Unity-iPhoneDebug.entitlements", project, true);
                project.AddBuildPropertyForConfig(project.BuildConfigByName(unityMainTargetGuid, "Debug"),
                    "CODE_SIGN_ENTITLEMENTS", "Unity-iPhoneDebug.entitlements");
                CopyFileToPath(path2BuildProject, $"{iosFolderPath}/Unity-iPhoneRelease.entitlements",
                    "Unity-iPhoneRelease.entitlements", project, true);
                project.AddBuildPropertyForConfig(project.BuildConfigByName(unityMainTargetGuid, "Release"),
                    "CODE_SIGN_ENTITLEMENTS", "Unity-iPhoneRelease.entitlements");
                project.AddBuildPropertyForConfig(project.BuildConfigByName(unityMainTargetGuid, "ReleaseForProfiling"),
                    "CODE_SIGN_ENTITLEMENTS", "Unity-iPhoneRelease.entitlements");
                project.AddBuildPropertyForConfig(project.BuildConfigByName(unityMainTargetGuid, "ReleaseForRunning"),
                    "CODE_SIGN_ENTITLEMENTS", "Unity-iPhoneRelease.entitlements");
    
                #endregion
    
                // Info.plist
                AddCapability(project, path2BuildProject);
            }
    
            private static void AddCapability(PBXProject project, string pathToBuiltProject)
            {
                string target = project.GetUnityMainTargetGuid();
                // Need Create entitlements
    
                #region 修改info.plist
    
                string plistPath = pathToBuiltProject + "/Info.plist";
                PlistDocument plist = new PlistDocument();
                plist.ReadFromString(File.ReadAllText(plistPath));
                PlistElementDict infoDict = plist.root;
    
                project.AddCapability(target, PBXCapabilityType.BackgroundModes);
                PlistElementArray bmArray;
    
                if (!infoDict.values.ContainsKey("UIBackgroundModes"))
                    bmArray = infoDict.CreateArray("UIBackgroundModes");
                else
                    bmArray = infoDict.values["UIBackgroundModes"].AsArray();
                bmArray.values.Clear();
                bmArray.AddString("remote-notification");
    
                PlistElementDict bmDict;
                if (!infoDict.values.ContainsKey("NSAppTransportSecurity"))
                    bmDict = infoDict.CreateDict("NSAppTransportSecurity");
                else
                    bmDict = infoDict.values["NSAppTransportSecurity"].AsDict();
    
                if (bmDict.values.ContainsKey("NSAllowsArbitraryLoadsInWebContent"))
                    bmDict.values.Remove("NSAllowsArbitraryLoadsInWebContent");
    
    
                if (AviaLogger.IsOpenLog)
                {
                    if (infoDict.values.ContainsKey("UIFileSharingEnabled"))
                        infoDict.values.Remove("UIFileSharingEnabled");
                    infoDict.SetBoolean("UIFileSharingEnabled", true);
                }
    
                infoDict.SetString("NSUserTrackingUsageDescription", "广告追踪权限");
                infoDict.SetString("NSLocationWhenInUseUsageDescription", "地理位置权限");
                infoDict.SetString("NSPhotoLibraryUsageDescription", "相册权限");
                infoDict.SetString("NSCameraUsageDescription", "相机权限");
                infoDict.SetBoolean("ITSAppUsesNonExemptEncryption", false);
    
                infoDict.CreateDict("NSLocationTemporaryUsageDescriptionDictionary")
                    .SetString("GetPreciseLocation", "此应用程序需要临时访问您的位置信息以提供更准确的服务。");
    
                if (!infoDict.values.ContainsKey("LSApplicationQueriesSchemes"))
                    infoDict.CreateArray("LSApplicationQueriesSchemes").AddString("com.venmo.touch.v2");
                else
                    infoDict.values["LSApplicationQueriesSchemes"].AsArray().AddString("com.venmo.touch.v2");
    
    
                if (!infoDict.values.ContainsKey("CFBundleURLTypes"))
                    infoDict.CreateArray("CFBundleURLTypes").AddDict().CreateArray("CFBundleURLSchemes");
    
                else
                    infoDict.values["CFBundleURLTypes"].AsArray().AddDict().CreateArray("CFBundleURLSchemes");
    
                PlistElementArray urlSchemes =
                    infoDict["CFBundleURLTypes"].AsArray().values[1].AsDict()["CFBundleURLSchemes"].AsArray();
                urlSchemes.AddString("${PRODUCT_BUNDLE_IDENTIFIER}.payments");
                urlSchemes.AddString("${PRODUCT_BUNDLE_IDENTIFIER}.trustly");
                urlSchemes.AddString("${PRODUCT_BUNDLE_IDENTIFIER}.adyenCashApp");
                urlSchemes.AddString("${PRODUCT_BUNDLE_IDENTIFIER}.afLink");
    
                infoDict.SetString("AppIdentifierPrefix", "$(AppIdentifierPrefix)");
                //infoDict.SetString("CFBundleIdentifier", _bundleId);
                File.WriteAllText(plistPath, plist.WriteToString());
    
                #endregion
    
    
                #region 修改entitlement
    
                string releaseEntitlementFilePath = pathToBuiltProject + "/Unity-iPhoneRelease.entitlements";
                string absoluteEntitlementFilePath = pathToBuiltProject + "/Unity-iPhoneDebug.entitlements";
                PlistDocument tempEntitlements = new PlistDocument();
    
                string keychainAccessGroups = "keychain-access-groups";
                var arr = new PlistElementArray();
                arr.values.Add(new PlistElementString($"$(AppIdentifierPrefix){Application.identifier}"));
                arr.values.Add(new PlistElementString("$(AppIdentifierPrefix)AviagamesUniqueDevice"));
                tempEntitlements.root[keychainAccessGroups] = arr;
    
                project.AddCapability(target, PBXCapabilityType.InAppPurchase);
                if (!string.IsNullOrEmpty(BtApplePayMerchantId))
                {
                    string applePayments = "com.apple.developer.in-app-payments";
                    var payArr = (tempEntitlements.root[applePayments] = new PlistElementArray()) as PlistElementArray;
                    payArr?.values.Add(new PlistElementString(BtApplePayMerchantId));
                    payArr?.values.Add(new PlistElementString(UtApplePayMerchantId));
                }
                else
                {
                    AviaLogger.AMPLogWarn("merchant Id 为空");
                }
    
                string keyPushNotifications = "aps-environment";
                tempEntitlements.root[keyPushNotifications] = new PlistElementString("development");
    
                project.AddCapability(target, PBXCapabilityType.PushNotifications);
                project.AddCapability(target, PBXCapabilityType.KeychainSharing);
    
                project.AddCapability(target, PBXCapabilityType.AccessWiFiInformation);
                tempEntitlements.root["com.apple.developer.networking.wifi-info"] = new PlistElementBoolean(true);
    
    
                if (!string.IsNullOrEmpty(DeepLinkDomain))
                {
                    project.AddCapability(target, PBXCapabilityType.AssociatedDomains);
                    var associateDomains = new PlistElementArray();
                    associateDomains.AddString(DeepLinkDomain);
                    tempEntitlements.root["com.apple.developer.associated-domains"] = associateDomains;
                }
                else
                {
                    AviaLogger.AMPLogWarn("deep link 为空");
                }
    
                if (!string.IsNullOrEmpty(AppGroup))
                {
                    project.AddCapability(target, PBXCapabilityType.AppGroups);
                    string appGroupKey = "com.apple.security.application-groups";
                    var appGroupArr = new PlistElementArray();
                    appGroupArr.values.Add(new PlistElementString(AppGroup));
                    tempEntitlements.root[appGroupKey] = appGroupArr;
                }
                else
                {
                    AviaLogger.AMPLogWarn("app group 为空");
                }
    
                tempEntitlements.WriteToFile(absoluteEntitlementFilePath);
                tempEntitlements.root[keyPushNotifications] = new PlistElementString("production");
                tempEntitlements.WriteToFile(releaseEntitlementFilePath);
    
                ModifyEntitlementFile(absoluteEntitlementFilePath);
    
                #endregion
            }
    
            private static void ModifyEntitlementFile(string absoluteEntitlementFilePath)
            {
                if (!File.Exists(absoluteEntitlementFilePath)) return;
    
                try
                {
                    StreamReader reader = new StreamReader(absoluteEntitlementFilePath);
                    var content = reader.ReadToEnd().Trim();
                    reader.Close();
    
                    var needFindString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
                    var changeString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" +
                                       "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
                    Debug.Log("entitlement更改之前: " + content);
                    content = content.Replace(needFindString, changeString);
                    Debug.Log("entitlement更改之后: " + content);
                    StreamWriter writer = new StreamWriter(new FileStream(absoluteEntitlementFilePath, FileMode.Create));
                    writer.WriteLine(content);
                    writer.Flush();
                    writer.Close();
                }
                catch (Exception e)
                {
                    Debug.Log("ModifyEntitlementFile - Failed: " + e.Message);
                }
            }
    
            #region 修改build phase
    
            private static void Process(string path)
            {
                string projectPath = PBXProject.GetPBXProjectPath(path);
                PBXProject project = new PBXProject();
                project.ReadFromFile(projectPath);
    
                string mainTargetGuild = project.GetUnityMainTargetGuid();
                //获取所有的build phase guid
                string[] buildPhases = project.GetAllBuildPhasesForTarget(mainTargetGuild);
    
                //根据build phase name获取 guid
                string crashlyticsShellScriptPhaseGuid =
                    GetBuildPhaseGuid(project, buildPhases, CrashlyticsShellScriptPhaseName);
                if (string.IsNullOrEmpty(crashlyticsShellScriptPhaseGuid))
                {
                    DebugLog($"\"{CrashlyticsShellScriptPhaseName}\" phase guid not found.");
                    //return;
                }
    
                string embedAppExtensionsPhaseGuid = GetBuildPhaseGuid(project, buildPhases, EmbedAppExtensionsPhaseName);
                if (string.IsNullOrEmpty(embedAppExtensionsPhaseGuid))
                {
                    DebugLog($"\"{EmbedAppExtensionsPhaseName}\" phase guid not found.");
                    //return;
                }
    
    
                Type projectType = project.GetType();
                //获取nativeTargets属性
                PropertyInfo nativeTargetsProperty =
                    projectType.GetProperty("nativeTargets", BindingFlags.NonPublic | BindingFlags.Instance);
    
                //获取nativeTargets属性值,是个数组
                object nativeTargets = nativeTargetsProperty?.GetValue(project);
                if (nativeTargets == null)
                {
                    DebugLog($"属性'nativeTargets'没找到");
                    return;
                }
    
                DebugLog("nativeTargets 值为:" + nativeTargets);
    
    
                //获取nativeTargets的类
                Type nativeTargetsType = nativeTargets.GetType();
                DebugLog("nativeTargets 类名为:" + nativeTargetsType.FullName);
    
                //获取nativeTargets的类的索引器方法
                MethodInfo indexerMethod = nativeTargetsType.GetMethod("get_Item");
                if (indexerMethod == null)
                {
                    DebugLog($"Method 'get_Item' of {nativeTargetsType.FullName} type not found.");
                    return;
                }
    
                //调用nativeTargets的类的索引器方法,参数为target guid,返回为PBXNativeTargetData,也就是target的数据,比如Unity-iPhone、UnityFramework
                object pbxNativeTargetData = indexerMethod.Invoke(nativeTargets, new object[] { mainTargetGuild });
    
                //获取main target(PBXNativeTargetData)的phase 字段,它是GUIDList类型,也就是所有phases的所有GUID
                FieldInfo phasesField = pbxNativeTargetData.GetType().GetField("phases");
                object phases = phasesField?.GetValue(pbxNativeTargetData);
    
                //获取GUIDList的m_List private字段
                FieldInfo listField = phases?.GetType().GetField("m_List", BindingFlags.NonPublic | BindingFlags.Instance);
                if (!(listField?.GetValue(phases) is List<string> guidList))
                {
                    DebugLog($"Field 'm_List' not found.");
                    return;
                }
    
                //------下面开始调整顺序,前面只是做校验多一点-------
                //build phase在xcode中的顺序,就是它在GUIDList中的顺序
                guidList.Remove(crashlyticsShellScriptPhaseGuid);
                guidList.Insert(guidList.IndexOf(embedAppExtensionsPhaseGuid) + 1, crashlyticsShellScriptPhaseGuid);
                DebugLog(
                    $"Insert {CrashlyticsShellScriptPhaseName} phase {crashlyticsShellScriptPhaseGuid} after {EmbedAppExtensionsPhaseName} phase {embedAppExtensionsPhaseGuid}");
    
                project.WriteToFile(projectPath);
    
                void DebugLog(string message) => Debug.Log($"[更改build phase 顺序] {message}");
            }
    
            /// <summary>
            /// 根据
            /// </summary>
            /// <param name="project"></param>
            /// <param name="buildPhases"></param>
            /// <param name="buildPhaseName"></param>
            /// <returns></returns>
            static string GetBuildPhaseGuid(PBXProject project, string[] buildPhases, string buildPhaseName)
            {
                foreach (string buildPhaseGuid in buildPhases)
                {
                    if (project.GetBuildPhaseName(buildPhaseGuid) == buildPhaseName)
                    {
                        return buildPhaseGuid;
                    }
                }
    
                return null;
            }
    
            #endregion
        }
    }
    #endif
 打包命令行
def PROJECT_PATH = "${GIT_PRO_PATH}/${UNITY_PROJECT_NAME}"                                                               
def BUILD_IOS_PATH = "${PROJECT_PATH}/BuildTool/buildios.sh"                                   
def XCODE_ARCHIVE_NAME = 'unity_sdk.xcarchive'
def XCODE_ARCHIVE_PROJECT_PATH = "${IPA_PATH}/../archive_proj/${XCODE_ARCHIVE_NAME}"
def XCODE_BUILD_PROJECT_PATH = "${IPA_PATH}/../xcode_proj"
def PACKAGE_SHARE_PATH = "/opt/homebrew/var/www/unity_sdk_output"
def UNITY_LOG_FILE = "${PROJECT_PATH}/Logs/unity_sdk_log.txt"
def ARCHIVE_LOG_FILE = "${PROJECT_PATH}/Logs/archive.txt"
def SHARE_URL = "http://10.240.0.216:8090/unity_sdk_output"
def BUNDLE_NUM_FILE="${PROJECT_PATH}/Archive/Setting/BuildVersion.user"
def FAIRGUARD = "${PROJECT_PATH}/BuildTool/FairGuard_iOS_3.3.4/fairguardbuild"
def user
node {
    //需要安装插件 build var user
    wrap([$class: 'BuildUser']) 
    {
        user = env.BUILD_USER_ID
    }
}
pipeline {
        agent any
        options {
            //lock(label: 'DevLock', quantity: 1)            
            disableConcurrentBuilds()
            timeout(time: 45, unit: 'MINUTES')
        }
        environment 
        {
            BUILD_TIMESTAMP = sh(script: 'echo $(date +"%Y_%m_%d-%H_%M_%S")', returnStdout: true).trim()
            TEST_VALUE = "${Test}" 
        }

        stages {

            stage('git拉取代码更新') {
                steps {
                    sh """
                        cd ${GIT_PRO_PATH}
                        git clean -fd
                        git stash push ${BUNDLE_NUM_FILE}
                        git reset --hard HEAD 
                        git stash pop
                        git checkout ${BRANCH}
                        git clean -fd
                        git pull --force 
                        git submodule foreach --recursive 'git reset HEAD . || :'
                        git submodule foreach --recursive 'git checkout -- . || :'
                        git submodule update --init --recursive
                        git submodule foreach --recursive git clean -d -f -f -x
                    """
                }
            }

            stage('生成共享地址') {
                steps{
                    sh """
                        rm -rf ${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}
                        mkdir ${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}
                        echo ${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}
                    """
                }
            }
            stage('Unity打包') {
                when{
                    expression{return IS_BUILD_IPA.toBoolean()}
                    //expression{return false}
                }
                steps {
                    sh """
                        set -e
                        ${UNITY_2020_3_33} -projectPath $PROJECT_PATH -executeMethod BuildTools.BuildIOS  xcodeProjectPath:${XCODE_BUILD_PROJECT_PATH} env:${ENV} isUploadIpa:${IS_UPLOAD_IPA}  -quit -batchmode -logFile ${UNITY_LOG_FILE} 2>&1 | tee ${UNITY_LOG_FILE} -buildTarget iOS
                        cp ${UNITY_LOG_FILE} ${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}/unity_sdk_log.txt
                    """
                }
            }
           
            stage('Xcode工程 Clean') {
                when{
                  expression{return IS_BUILD_IPA.toBoolean()}
                }
                steps {
                    sh """
                        cd ${XCODE_BUILD_PROJECT_PATH}
                        xcodebuild -workspace Unity-iPhone.xcworkspace -scheme "Unity-iPhone" clean
                    """                    
                }
            }

            stage('XCODE加固') {
                when{
                  expression{return false}
                }
                steps {
                    script {
                                            if("${jiagu}" == 'true'){
                                                        XCODE_BUILD_PATH = "${XCODE_DIR_PATH}/BuildSuccess"
                                                    sh """ 
                                                            ${FAIRGUARD} -p ${XCODE_DIR_PATH}/Unity-iPhone.xcworkspace -s Unity-iPhone
                                                                mv ${XCODE_BUILD_PATH}/RTB-* ${XCODE_BUILD_PATH}/rtb.xcarchive
                                                    """
                                            }
                    }
                }
            }

           stage('Xcode工程 Archive') {
                when{
                  expression{return IS_BUILD_IPA.toBoolean()}
                  //expression{return false}
                }
                steps {
                    sh """
                        set -e
                        cd ${XCODE_BUILD_PROJECT_PATH}  
                        rm -f ${ARCHIVE_LOG_FILE}
                        rm -rf ${XCODE_ARCHIVE_PROJECT_PATH}
                        xcodebuild archive -workspace Unity-iPhone.xcworkspace -scheme "Unity-iPhone" -configuration "Release" -archivePath "${XCODE_ARCHIVE_PROJECT_PATH}" -destination "generic/platform=iOS" -allowProvisioningUpdates -allowProvisioningDeviceRegistration | tee ${ARCHIVE_LOG_FILE}  2>&1 

                        cp ${ARCHIVE_LOG_FILE} ${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}/Archive.txt
                        cat ${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}/Archive.txt
                    """
                }
            }

            stage('Xcode工程 Export') {
                when{
                  expression{return IS_BUILD_IPA.toBoolean()}
                  //expression{return false}
                }
                steps {
                    script{
                       def exportionPlist
                       
                       if (IS_UPLOAD_IPA.toBoolean()) {
                            exportionPlist = "${PROJECT_PATH}/BuildTool/ExportOptions_Upload.plist"
                        } 
                        else {
                            exportionPlist = "${PROJECT_PATH}/BuildTool/ExportOptions_Dev.plist"
                        }
                        
                       sh """
                        set -e
                        cd ${XCODE_BUILD_PROJECT_PATH} 
                        rm -rf "${IPA_PATH}/*"
                        
                        
                        xcodebuild -exportArchive -archivePath  "${XCODE_ARCHIVE_PROJECT_PATH}" -exportPath ${IPA_PATH} -exportOptionsPlist ${exportionPlist} -allowProvisioningUpdates -allowProvisioningDeviceRegistration
                    """ 
                    }
                }
            }

            stage('IPA 拷贝') {
                when{
                  expression{return IS_BUILD_IPA.toBoolean()}
                  //expression{return false}
                }
                steps {
                  script{
                    def ipa = sh(script: 'find ${IPA_PATH} -name "*.ipa" -print -quit', returnStdout: true).trim()
                    sh """
                        echo "${ipa}"
                        cp ${ipa} "${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}/"
                    """
                  }
                }
            }

            stage('IPA 上传TestFlight') {
                when{
                  //expression{return IS_UPLOAD_IPA.toBoolean()}
                  expression{return false}
                }
                steps {
                    script{
                      def sharePath= "${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}"
                      echo "${sharePath}"
                    
                      def ipa = sh(script: "find '${sharePath}' -name '*.ipa' -print -quit", returnStdout: true).trim()
                      sh """ 
                       set -e
                       xcrun altool --validate-app --type ios -f ${ipa} -u zhangruiguo@aviagames.com -p faoy-vcit-xyub-wqmr --verbose | tee "${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}/validate_log.txt"
                      if !xcrun altool --upload-app --type ios -f ${ipa} -u zhangruiguo@aviagames.com -p faoy-vcit-xyub-wqmr --verbose | tee "${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}/upload_log.txt"; then
                         echo "上传TestFlight失败"
                         exit 1
                      fi
                      """ 
                    }
                }
            }
        }
        post {
            always {
                echo "用户信息:${user}"

            }
            success{
                echo "构建成功"
                script{
                    def ipa = sh(script: "find '${PACKAGE_SHARE_PATH}/${BUILD_TIMESTAMP}' -name '*.ipa' -print -quit", returnStdout: true).trim()
                    def ipaUrl = sh(script: "echo '${ipa}' | sed 's|^${PACKAGE_SHARE_PATH}|${SHARE_URL}|'", returnStdout: true).trim()
                    def BUNDLE_NUM=sh(script:"cat ${BUNDLE_NUM_FILE}", returnStdout: true).trim()
                    sh """
                        curl -X POST -H "Content-Type: application/json" \
                        -d '{
                            "msg_type": "post",
                            "content": {
                                "post": {
                                    "zh_cn": {
                                        "title": "构建成功",
                                        "content": [
                                            [{"tag":"a", "text":"包体地址","href":"${ipaUrl}"}],
                                            [{"tag":"text", "text":"构建环境:${ENV}   版本号[1.2(${BUNDLE_NUM})]"}],
                                            [{"tag":"text", "text":"构建人:${user}"}]
                                        ]
                                    }
                                }
                            }
                        }' \
                        https地址
                    """
                }   
            }
            failure {
                echo "构建失败"
                script{
                    sh """
                        curl -X POST -H "Content-Type: application/json" \
                        -d '{
                            "msg_type": "post",
                            "content": {
                                "post": {
                                    "zh_cn": {
                                        "title": "构建失败",
                                        "content": [
                                            [{"tag":"a", "text":"失败详情","href":"${env.BUILD_URL}/console"}],
                                            [{"tag":"text", "text":"构建环境:${ENV}   版本号[1.2(${BUNDLE_NUM})]"}],
                                            [{"tag":"text", "text":"构建人:${user}"}]
                                        ]
                                    }
                                }
                            }
                        }' \
                        https地址
                    """
                }   
            }
        }
}
xcode

ExportOptions.plist文件的作用,就是在export包时的配置,这里面可以配置成导出后,并上传到TestFlight,就不用再单独上传了

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- 如果不上传,设为export -->
	<key>destination</key>
	<string>upload</string>
	<key>generateAppStoreInformation</key>
	<false/>
	<key>manageAppVersionAndBuildNumber</key>
	<true/>
	<key>method</key>
    <!-- 如果不上传,设为app-store/ad-hoc/enterprise -->
	<string>app-store-connect</string>
	<key>provisioningProfiles</key>
    <dict>
    <key>包的bundle id</key>
    <string>provision名字</string>
    <key>内嵌包的bundle id</key>
    <string>provision名字</string>
    </dict>
	<key>signingStyle</key>
	<string>manual</string>
	<key>stripSwiftSymbols</key>
	<true/>
	<key>teamID</key>
	<string></string>
	<key>testFlightInternalTestingOnly</key>
	<false/>
    <!-- 如果不上传,设为false -->
	<key>uploadSymbols</key>
	<true/>
</dict>
</plist>
 Groovy语法


✅ jenkins的bool类型参数,在groovy里面是字符串,如果要判断,使用BoolParam.toBoolean()

✅ when{expression{return BoolParam.toBoolean()}}表示该阶段执不执行
✅def 变量赋值需要双引号"",比如def ttt="${test}"
✅sh"""  ""","""  """表示可写多行shell,且shell中不能定义变量,需要在script{}里面通过def定义变量,然后在sh里面通过${}引用
✅script里面的脚本,通过双引号包裹命令,单引号引用变量方式
比如 def ipa = sh(script: "find '${sharePath}' -name '*.ipa' -print -quit", returnStdout: true).trim()
✅unity_sdk.xcarchive 是个文件夹,删除用rm -rf
✅git update-index --assume-unchanged <file> 对本地文件忽略跟踪,比如分支上有这个文件,但是忽略本地的跟踪,但是git reset --hard HEAD 之后,还是会更改,所以先stash 再pop


http://www.kler.cn/a/313589.html

相关文章:

  • 超子物联网HAL库笔记:定时器[外部模式]篇
  • 使用 Vue 配合豆包MarsCode 实现“小恐龙酷跑“小游戏
  • Python如何用正则表达式匹配并处理文件名
  • 羊城杯2020Easyphp
  • jQuery笔记
  • EDUCODER头哥 基于MVC模式的用户登录
  • LeetCode118:杨辉三角
  • Spring Boot- 配置文件问题
  • 【JavaScript】数据结构之链表(双指针、滑动窗口)
  • 切换淘宝最新镜像源npm详细讲解
  • 计算机毕业设计选题推荐-4S店试驾平台-小程序/App
  • 过采样和欠采样
  • C++ 字符串最后一个单词的长度(牛客网)
  • # wps必须要登录激活才能使用吗?
  • 摄影学习平台
  • 【Linux】简易日志系统
  • Web前端开发
  • PHP 数组排序类型介绍
  • 基于微信小程序的剧本杀游玩一体化平台
  • [数据结构]算法复杂度详解
  • 代码随想录算法训练营Day7
  • 基于MySQL全量备份+GTID同步的主从架构恢复数据至指定时间点
  • Linux--禁止root用户通过ssh直接登录
  • Java项目实战II基于Java+Spring Boot+MySQL的网上租贸系统设计与实现(开发文档+源码+数据库)
  • 情感AI:科技赋能情感计算的新时代
  • SpringBoot:token是用来鉴权的,那session的作用是什么?