使用jenkins打包unity工程

news2024/9/21 11:29:14
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.coloradmin.cn/o/2152204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV_距离变换的图像分割和Watershed算法详解

在学习watershed算法的时候&#xff0c;书写代码总会出现一些错误&#xff1a; 上述代码运行报错&#xff0c;显示OpenCV(4.10.0) Error: Assertion failed (src.type() CV_8UC3 && dst.type() CV_32SC1) in cv::watershed 查找资料&#xff1a;目前已解决 这个错…

idea 编辑器常用插件集合

SequenceDiagram 用于生成时序图的插件&#xff0c;支持一键生成功能。 使用&#xff1a;选择某个具体的方法&#xff0c;点击右键菜单&#xff0c;选择“Sequence Diagram” 便可生成相应的时序图 例子&#xff1a; 效果&#xff1a; Code Iris Code Iris可以根据代码自动…

c++day3 手动封装一个顺序表(SeqList),分文件编译实现

要求: 有私有成员&#xff1a;顺序表数组的起始地址 ptr、 顺序表的总长度&#xff1a;size、顺序表的实际长度&#xff1a;len 成员函数&#xff1a;初始化 init(int n) 判空&#xff1a;empty 判满&#xff1a;full 尾插&#xff1a;push_back 插入&#xff1a;insert&…

优数:助力更高效的边缘计算

在数字化时代的浪潮中&#xff0c;数据已成为企业最宝贵的资产之一。随着物联网&#xff08;IoT&#xff09;设备的激增和5G技术的兴起&#xff0c;我们正迅速步入一个新时代&#xff0c;在这个时代中&#xff0c;数据不仅在量上爆炸性增长&#xff0c;更在速度和实时性上提出了…

Hadoop里面MapReduce的序列化与Java序列化比较

什么是序列化&#xff1f; jvm中的一个对象&#xff0c;不是类&#xff0c;假如你想把一个对象&#xff0c;保存到磁盘上&#xff0c;必须序列化&#xff0c;你把文件中的对象进行恢复&#xff0c;是不是的反序列化。 假如你想把对象发送给另一个服务器&#xff0c;需要通过网…

线性dp 总结详解

就是感觉之前 dp 的 blog 太乱了整理一下。 LIS(最长上升子序列) 例题 给定一个整数序列&#xff0c;找到它的所有严格递增子序列中最长的序列&#xff0c;输出其长度。 思路 拿到题目&#xff0c;大家第一时间想到的应该是的暴力(dp)做法&#xff1a; #include <bits/s…

基于Windows系统以tomcat为案例,讲解如何新增自启动服务,定时重启服务。

文章目录 引言I 设置服务自启动的常规操作II 安装多个tomcat服务,并设置自启动。III 定时重启服务引言 为了同一个版本安装多个tomcat服务,并设置自启动。使用Windows的任务计划程序来创建一个定时任务,用于重启Tomcat服务。I 设置服务自启动的常规操作 运行窗口输入control…

2024双11有哪些值得入手的好物?2024年双十一好物推荐

随着2024年双十一购物狂欢节的临近&#xff0c;消费者们正摩拳擦掌&#xff0c;准备迎接这场年度最大的网购盛会。面对琳琅满目的促销信息和令人眼花缭乱的商品&#xff0c;如何在海量商品中精准锁定那些真正值得购买的好物&#xff0c;成为每位精明买家的首要任务。本文旨在为…

牛啊,GitHub 代理加速图文教程

大家好&#xff0c;众所周知&#xff0c;GitHub 在国内访问速度堪忧&#xff0c;经常出现访问不了的情况&#xff0c;如果我们去 clone 代码&#xff0c;网速非常差。今天教大家如何给 GitHub 进行加速。 要用到我开发的开源项目 Cloudflare Workers Proxy&#xff0c;它是一个…

视频压缩篇:适用于 Windows 的 10 款最佳视频压缩器

视频压缩器现在对许多想要减小视频大小的视频编辑者来说非常有名。但是&#xff0c;并非所有可以在网上找到的视频压缩器都能产生最佳输出。因此&#xff0c;我们搜索了可以无损压缩视频的最出色的视频压缩器应用程序。本文列出了您可以在离线、在线和手机上使用的十大最佳视频…

2024华为杯研赛D题保姆级教程思路分析+教程

2024年中国研究生数学建模竞赛D题保姆级教程思路分析 D题&#xff1a;大数据驱动的地理综合问题&#xff08;数学分析&#xff0c;统计学&#xff09; 关键词&#xff1a;地理、气候、统计&#xff08;细致到此题&#xff1a;统计指标、统计模型、统计结果解释&#xff09; …

无线领夹麦克风哪个降噪好?一文搞懂麦克风什么牌子的音质效果好

对于视频拍摄、直播来说&#xff0c;一款好的拾音麦克风是不可或缺的。作为一位数码博主&#xff0c;也是会经常拍摄视频讲解&#xff0c;早期没有使用麦克风时&#xff0c;声音不够清晰&#xff0c;而且周围环境音也会同时被收录&#xff0c;导致整个音频的音质效果极差&#…

【多线程】CAS的原理及应用,看这篇文章就够啦

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;多线程 / javaEE初阶 一、CAS概述 CAS&#xff08;Compare and Swap&#xff09;&#xff0c;中文译为 “比较并交换” &#xff0c;是一种无锁算法中常用的原子操作。CAS通常用于实现线程之间的同…

力扣之1459.矩形面积

1. 1459.矩形面积 1.1 题干 表: Points ---------------------- | Column Name | Type | ---------------------- | id | int | | x_value | int | | y_value | int | ---------------------- id 是该表中具有唯一值的列。 每个点都用二维坐标 (x_value, y_value) 表示。 编…

【力扣每日一题——2374. 边积分最高的节点】python

2374. 边积分最高的节点 给你一个有向图&#xff0c;图中有 n 个节点&#xff0c;节点编号从 0 到 n - 1 &#xff0c;其中每个节点都 恰有一条 出边。 图由一个下标从 0 开始、长度为 n 的整数数组 edges 表示&#xff0c;其中 edges[i] 表示存在一条从节点 i 到节点 edges[…

大模型训练实战经验总结

在当今AI技术飞速发展的背景下&#xff0c;定制化大模型的自主训练已成为满足特定行业需求、保障数据安全、提升模型应用效能的关键途径。本文将深度剖析这一过程的核心价值与实践智慧&#xff0c;从数据隐私保护、模型透明度增强&#xff0c;到数据预处理的精细操作&#xff0…

记录一次fs配置导致串线的问题

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 fs在实际的使用过程中也会经常碰到莫名其妙的问题&#xff0c;大部分都是配置问题。 环境 CentOS 7.9 freeswitch 1.10.7 docker 26.1.1 问题描述 组网方案如下。其中的fs-reg是注册服务器&#xff0c;fs1和fs2是…

NEES(Normalized Estimation Error Squared 归一化估计误差平方)

目录 NEES的计算步骤 解释 示例 代码 与RMSE的区别 NEES(Normalized Estimation Error Squared)是一种用于评估状态估计精度的指标,通常用于比较估计值与真实值之间的差异。计算NEES的步骤如下: NEES的计算步骤 获取状态估计: 设定目标的真实状态为。设定状态估计为…

Ubuntu 20.04安装pycharm2022及配置快捷方式

一、下载与安装 1. 下载 在 官网 下载所需版本&#xff0c;如&#xff1a;下载 2022.3.3 - Linux (tar.gz) 2. 安装 设置自定义安装路径(推荐在 /opt/ 路径下)并安装 mkdir -p ~/Documents/software/pycharm/ cd ~/Documents/software/pycharm/ mv ~/Downloads/pycharm-c…

使用 Puppeteer-Cluster 和代理进行高效网络抓取: 完全指南

文章目录 一、介绍&#xff1f;二、什么是 Puppeteer-Cluster&#xff1f;三、为什么代理在网络抓取中很重要&#xff1f;四、 为什么使用带代理的 Puppeteer-Cluster&#xff1f;五、分步指南&#xff1a; 带代理的 Puppeteer 群集5.1. 步骤 1&#xff1a;安装所需程序库5.2. …