main.go 5.0 KB

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