github.com/sagernet/sing-box@v1.9.0-rc.20/cmd/internal/update_apple_version/main.go (about) 1 package main 2 3 import ( 4 "os" 5 "path/filepath" 6 "regexp" 7 "strings" 8 9 "github.com/sagernet/sing-box/cmd/internal/build_shared" 10 "github.com/sagernet/sing-box/log" 11 "github.com/sagernet/sing/common" 12 13 "howett.net/plist" 14 ) 15 16 func main() { 17 newVersion := common.Must1(build_shared.ReadTagVersion()) 18 applePath, err := filepath.Abs("../sing-box-for-apple") 19 if err != nil { 20 log.Fatal(err) 21 } 22 common.Must(os.Chdir(applePath)) 23 projectFile := common.Must1(os.Open("sing-box.xcodeproj/project.pbxproj")) 24 var project map[string]any 25 decoder := plist.NewDecoder(projectFile) 26 common.Must(decoder.Decode(&project)) 27 objectsMap := project["objects"].(map[string]any) 28 projectContent := string(common.Must1(os.ReadFile("sing-box.xcodeproj/project.pbxproj"))) 29 newContent, updated0 := findAndReplace(objectsMap, projectContent, []string{"io.nekohasekai.sfa"}, newVersion.VersionString()) 30 newContent, updated1 := findAndReplace(objectsMap, newContent, []string{"io.nekohasekai.sfa.independent", "io.nekohasekai.sfa.system"}, newVersion.String()) 31 if updated0 || updated1 { 32 log.Info("updated version to ", newVersion.VersionString(), " (", newVersion.String(), ")") 33 } 34 var updated2 bool 35 if macProjectVersion := os.Getenv("MACOS_PROJECT_VERSION"); macProjectVersion != "" { 36 newContent, updated2 = findAndReplaceProjectVersion(objectsMap, newContent, []string{"SFM"}, macProjectVersion) 37 if updated2 { 38 log.Info("updated macos project version to ", macProjectVersion) 39 } 40 } 41 if updated0 || updated1 || updated2 { 42 common.Must(os.WriteFile("sing-box.xcodeproj/project.pbxproj", []byte(newContent), 0o644)) 43 } 44 } 45 46 func findAndReplace(objectsMap map[string]any, projectContent string, bundleIDList []string, newVersion string) (string, bool) { 47 objectKeyList := findObjectKey(objectsMap, bundleIDList) 48 var updated bool 49 for _, objectKey := range objectKeyList { 50 matchRegexp := common.Must1(regexp.Compile(objectKey + ".*= \\{")) 51 indexes := matchRegexp.FindStringIndex(projectContent) 52 if len(indexes) < 2 { 53 println(projectContent) 54 log.Fatal("failed to find object key ", objectKey, ": ", strings.Index(projectContent, objectKey)) 55 } 56 indexStart := indexes[1] 57 indexEnd := indexStart + strings.Index(projectContent[indexStart:], "}") 58 versionStart := indexStart + strings.Index(projectContent[indexStart:indexEnd], "MARKETING_VERSION = ") + 20 59 versionEnd := versionStart + strings.Index(projectContent[versionStart:indexEnd], ";") 60 version := projectContent[versionStart:versionEnd] 61 if version == newVersion { 62 continue 63 } 64 updated = true 65 projectContent = projectContent[:versionStart] + newVersion + projectContent[versionEnd:] 66 } 67 return projectContent, updated 68 } 69 70 func findAndReplaceProjectVersion(objectsMap map[string]any, projectContent string, directoryList []string, newVersion string) (string, bool) { 71 objectKeyList := findObjectKeyByDirectory(objectsMap, directoryList) 72 var updated bool 73 for _, objectKey := range objectKeyList { 74 matchRegexp := common.Must1(regexp.Compile(objectKey + ".*= \\{")) 75 indexes := matchRegexp.FindStringIndex(projectContent) 76 if len(indexes) < 2 { 77 println(projectContent) 78 log.Fatal("failed to find object key ", objectKey, ": ", strings.Index(projectContent, objectKey)) 79 } 80 indexStart := indexes[1] 81 indexEnd := indexStart + strings.Index(projectContent[indexStart:], "}") 82 versionStart := indexStart + strings.Index(projectContent[indexStart:indexEnd], "CURRENT_PROJECT_VERSION = ") + 26 83 versionEnd := versionStart + strings.Index(projectContent[versionStart:indexEnd], ";") 84 version := projectContent[versionStart:versionEnd] 85 if version == newVersion { 86 continue 87 } 88 updated = true 89 projectContent = projectContent[:versionStart] + newVersion + projectContent[versionEnd:] 90 } 91 return projectContent, updated 92 } 93 94 func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string { 95 var objectKeyList []string 96 for objectKey, object := range objectsMap { 97 buildSettings := object.(map[string]any)["buildSettings"] 98 if buildSettings == nil { 99 continue 100 } 101 bundleIDObject := buildSettings.(map[string]any)["PRODUCT_BUNDLE_IDENTIFIER"] 102 if bundleIDObject == nil { 103 continue 104 } 105 if common.Contains(bundleIDList, bundleIDObject.(string)) { 106 objectKeyList = append(objectKeyList, objectKey) 107 } 108 } 109 return objectKeyList 110 } 111 112 func findObjectKeyByDirectory(objectsMap map[string]any, directoryList []string) []string { 113 var objectKeyList []string 114 for objectKey, object := range objectsMap { 115 buildSettings := object.(map[string]any)["buildSettings"] 116 if buildSettings == nil { 117 continue 118 } 119 infoPListFile := buildSettings.(map[string]any)["INFOPLIST_FILE"] 120 if infoPListFile == nil { 121 continue 122 } 123 for _, searchDirectory := range directoryList { 124 if strings.HasPrefix(infoPListFile.(string), searchDirectory+"/") { 125 objectKeyList = append(objectKeyList, objectKey) 126 } 127 } 128 129 } 130 return objectKeyList 131 }