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  }