2
0

autoupdate.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import("core.package.package")
  2. import("core.base.semver")
  3. import("core.base.hashset")
  4. import("devel.git")
  5. import("packages", {alias = "packages_util"})
  6. function _load_package(packagename, packagedir, packagefile)
  7. local funcinfo = debug.getinfo(package.load_from_repository)
  8. if funcinfo and funcinfo.nparams == 3 then -- >= 2.7.8
  9. return package.load_from_repository(packagename, packagedir, {packagefile = packagefile})
  10. else
  11. -- deprecated
  12. return package.load_from_repository(packagename, nil, packagedir, packagefile)
  13. end
  14. end
  15. function _get_all_packages(pattern)
  16. local packages = _g.packages
  17. if not packages then
  18. packages = {}
  19. for _, packagedir in ipairs(os.dirs(path.join("packages", "*", "*"))) do
  20. local packagename = path.filename(packagedir)
  21. if not pattern or packagename:match(pattern) then
  22. local packagefile = path.join(packagedir, "xmake.lua")
  23. local instance = _load_package(packagename, packagedir, packagefile)
  24. local basename = instance:get("base")
  25. if instance and basename then
  26. local basedir = path.join("packages", basename:sub(1, 1):lower(), basename:lower())
  27. local basefile = path.join(basedir, "xmake.lua")
  28. instance._BASE = _load_package(basename, basedir, basefile)
  29. end
  30. if instance then
  31. table.insert(packages, instance)
  32. end
  33. end
  34. end
  35. _g.packages = packages
  36. end
  37. return packages
  38. end
  39. function _is_pending(instance, version)
  40. local branch = "autoupdate-" .. instance:name() .. "-" .. version
  41. local repourl = "[email protected]:xmake-io/xmake-repo.git"
  42. local is_pending = false
  43. local remote_branches = os.iorun("git ls-remote --head %s", repourl)
  44. if remote_branches then
  45. for _, remote_branch in ipairs(remote_branches:split("\n")) do
  46. remote_branch = remote_branch:split("%s")[2]
  47. if remote_branch == "refs/heads/" .. branch then
  48. is_pending = true
  49. break
  50. end
  51. end
  52. end
  53. return is_pending
  54. end
  55. function _update_version(instance, version, shasum)
  56. local branch = "autoupdate-" .. instance:name() .. "-" .. version
  57. local branch_current = os.iorun("git branch --show-current"):trim()
  58. local repourl = "[email protected]:xmake-io/xmake-repo.git"
  59. os.vexec("git reset --hard HEAD")
  60. os.vexec("git clean -fdx")
  61. os.execv("git", {"branch", "-D", branch}, {try = true})
  62. os.vexec("git checkout dev")
  63. os.vexec("git pull %s dev", repourl)
  64. os.vexec("git branch %s", branch)
  65. os.vexec("git checkout %s", branch)
  66. local inserted = false
  67. local scriptfile = path.join(instance:scriptdir(), "xmake.lua")
  68. local version_current
  69. if os.isfile(scriptfile) then
  70. io.gsub(scriptfile, "add_versions%(\"(.-)\",%s+\"(.-)\"%)", function (v, h)
  71. if not version_current or semver.compare(v, version_current) > 0 then
  72. version_current = v
  73. end
  74. if not inserted then
  75. inserted = true
  76. return string.format('add_versions("%s", "%s")\n add_versions("%s", "%s")', version, shasum, v, h)
  77. end
  78. end)
  79. end
  80. if not inserted then
  81. local versionfiles = instance:get("versionfiles")
  82. if versionfiles then
  83. for _, versionfile in ipairs(table.wrap(versionfiles)) do
  84. if not os.isfile(versionfile) then
  85. versionfile = path.join(instance:scriptdir(), versionfile)
  86. end
  87. if os.isfile(versionfile) then
  88. io.insert(versionfile, 1, string.format("%s %s", version, shasum))
  89. inserted = true
  90. end
  91. end
  92. end
  93. end
  94. if inserted then
  95. local body = string.format("New version of %s detected (package version: %s, last github version: %s)",
  96. instance:name(), version_current, version)
  97. os.vexec("git add .")
  98. os.vexec("git commit -a -m \"Update %s to %s\"", instance:name(), version)
  99. os.vexec("git push %s %s:%s", repourl, branch, branch)
  100. os.vexec("gh pr create --label \"auto-update\" --title \"Auto-update %s to %s\" --body \"%s\" -R xmake-io/xmake-repo -B dev -H %s",
  101. instance:name(), version, body, branch)
  102. end
  103. os.vexec("git reset --hard HEAD")
  104. os.vexec("git checkout %s", branch_current)
  105. end
  106. function _report_issue(instance)
  107. local package_name = instance:name()
  108. local curr_open_issue = os.iorun("gh issue list --label \"help wanted\" --label \"auto-update\" --search \"in:title [auto-update] %s requires manual handling\" -R xmake-io/xmake-repo --json number",
  109. package_name)
  110. if curr_open_issue == "[]\n" then
  111. local body = string.format("Failed to get tags of %s, which may be due to changes in repository visibility.",
  112. package_name)
  113. local title = "[auto-update] " .. package_name .. " requires manual handling."
  114. os.vexec("gh issue create --title \"%s\" --body \"%s\" --label \"help wanted,auto-update\" -R xmake-io/xmake-repo",
  115. title, body)
  116. else
  117. print("Found a known open issue #%s for package %s", curr_open_issue:trim(), package_name)
  118. end
  119. end
  120. function main(pattern)
  121. local count = 0
  122. local maxcount = 5
  123. local instances = _get_all_packages(pattern)
  124. if #instances < maxcount then
  125. maxcount = #instances
  126. end
  127. math.randomseed(os.time())
  128. while count < maxcount and #instances > 0 do
  129. local idx = math.random(#instances)
  130. local instance = instances[idx]
  131. local checkupdate_filepath = path.join(instance:scriptdir(), "checkupdate.lua")
  132. if not os.isfile(checkupdate_filepath) then
  133. checkupdate_filepath = path.join(os.scriptdir(), "checkupdate.lua")
  134. end
  135. local updated = false
  136. if os.isfile(checkupdate_filepath) then
  137. local checkupdate = import("checkupdate", {rootdir = path.directory(checkupdate_filepath), anonymous = true})
  138. local version, shasum = checkupdate(instance)
  139. if version == false then
  140. _report_issue(instance)
  141. elseif version and shasum and not _is_pending(instance, version) then
  142. cprint("package(%s): new version ${bright}%s${clear} found, shasum: ${bright}%s", instance:name(), version, shasum)
  143. _update_version(instance, version, shasum)
  144. updated = true
  145. end
  146. end
  147. if updated then
  148. count = count + 1
  149. end
  150. table.remove(instances, idx)
  151. end
  152. end