bump_deps.lua 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #!/usr/bin/env -S nvim -l
  2. -- Usage:
  3. -- ./scripts/bump_deps.lua -h
  4. assert(vim.fn.executable('gh') == 1)
  5. assert(vim.fn.executable('sed') == 1)
  6. local required_branch_prefix = 'bump-'
  7. local commit_prefix = 'build(deps): '
  8. local repos = {
  9. 'luajit/luajit',
  10. 'libuv/libuv',
  11. 'luvit/luv',
  12. 'neovim/unibilium',
  13. 'juliastrings/utf8proc',
  14. 'tree-sitter/tree-sitter',
  15. 'tree-sitter/tree-sitter-c',
  16. 'tree-sitter-grammars/tree-sitter-lua',
  17. 'tree-sitter-grammars/tree-sitter-vim',
  18. 'neovim/tree-sitter-vimdoc',
  19. 'tree-sitter-grammars/tree-sitter-query',
  20. 'tree-sitter-grammars/tree-sitter-markdown',
  21. 'bytecodealliance/wasmtime',
  22. 'uncrustify/uncrustify',
  23. }
  24. local zig_mode = {
  25. luajit = false,
  26. uncrustify = false,
  27. wasmtime = false,
  28. unibilium = 'nested',
  29. utf8proc = 'nested',
  30. libuv = 'nested',
  31. }
  32. local dependency_table = {} --- @type table<string, string>
  33. for _, repo in pairs(repos) do
  34. dependency_table[vim.fs.basename(repo)] = repo
  35. end
  36. local function die(msg)
  37. print(msg)
  38. vim.cmd('cquit 1')
  39. end
  40. -- Executes and returns the output of `cmd`, or nil on failure.
  41. -- if die_on_fail is true, process dies with die_msg on failure
  42. local function _run(cmd, die_on_fail, die_msg)
  43. local rv = vim.system(cmd):wait()
  44. if rv.code ~= 0 then
  45. if die_on_fail then
  46. die(die_msg)
  47. end
  48. return nil
  49. end
  50. return vim.trim(rv.stdout)
  51. end
  52. -- Run a command, return nil on failure
  53. local function run(cmd)
  54. return _run(cmd, false, '')
  55. end
  56. -- Run a command, die on failure with err_msg
  57. local function run_die(cmd, err_msg)
  58. -- print(vim.inspect(table.concat(cmd, ' ')))
  59. return _run(cmd, true, err_msg)
  60. end
  61. local nvim_src_dir = run({ 'git', 'rev-parse', '--show-toplevel' })
  62. local deps_file = nvim_src_dir .. '/' .. 'cmake.deps/deps.txt'
  63. --- @param repo string
  64. --- @param ref string
  65. local function get_archive_info(repo, ref)
  66. local temp_dir = os.getenv('TMPDIR') or os.getenv('TEMP') or '/tmp'
  67. local archive_name = ref .. '.tar.gz'
  68. local archive_path = temp_dir .. '/' .. archive_name
  69. local archive_url = 'https://github.com/' .. repo .. '/archive/' .. archive_name
  70. run_die(
  71. { 'curl', '-sfL', archive_url, '-o', archive_path },
  72. 'Failed to download archive from GitHub'
  73. )
  74. local shacmd = (
  75. vim.fn.executable('sha256sum') == 1 and { 'sha256sum', archive_path }
  76. or { 'shasum', '-a', '256', archive_path }
  77. )
  78. local archive_sha = run(shacmd):gmatch('%w+')()
  79. return { url = archive_url, sha = archive_sha }
  80. end
  81. local function get_gh_commit_sha(repo, ref)
  82. local full_repo = string.format('https://github.com/%s.git', repo)
  83. local tag_exists = run_die({ 'git', 'ls-remote', full_repo, 'refs/tags/' .. ref }) ~= ''
  84. -- We'd rather use the git tag over commit sha if possible
  85. if tag_exists then
  86. return ref
  87. end
  88. local sha = assert(
  89. run_die(
  90. { 'gh', 'api', 'repos/' .. repo .. '/commits/' .. ref, '--jq', '.sha' },
  91. 'Failed to get commit hash from GitHub. Not a valid ref?'
  92. )
  93. )
  94. return sha
  95. end
  96. local function update_deps_file(symbol, kind, value)
  97. run_die({
  98. 'sed',
  99. '-i',
  100. '-e',
  101. 's/' .. symbol .. '_' .. kind .. '.*$' .. '/' .. symbol .. '_' .. kind .. ' ' .. value .. '/',
  102. deps_file,
  103. }, 'Failed to write ' .. deps_file)
  104. end
  105. local function ref(name, _ref)
  106. local repo = dependency_table[name]
  107. local symbol = string.gsub(name, 'tree%-sitter', 'treesitter'):gsub('%-', '_')
  108. local symbol_upper = symbol:upper()
  109. run_die(
  110. { 'git', 'diff', '--quiet', 'HEAD', '--', deps_file },
  111. deps_file .. ' has uncommitted changes'
  112. )
  113. _ref = get_gh_commit_sha(repo, _ref)
  114. local archive = get_archive_info(repo, _ref)
  115. local comment = string.sub(_ref, 1, 9)
  116. local checked_out_branch = assert(run({ 'git', 'rev-parse', '--abbrev-ref', 'HEAD' }))
  117. if not checked_out_branch:match('^' .. required_branch_prefix) then
  118. print(
  119. "Current branch '"
  120. .. checked_out_branch
  121. .. "' doesn't seem to start with "
  122. .. required_branch_prefix
  123. )
  124. print('Checking out to bump-' .. name)
  125. run_die({ 'git', 'checkout', '-b', 'bump-' .. name }, 'git failed to create branch')
  126. end
  127. print('Updating ' .. name .. ' to ' .. archive.url .. '\n')
  128. update_deps_file(symbol_upper, 'URL', archive.url:gsub('/', '\\/'))
  129. update_deps_file(symbol_upper, 'SHA256', archive.sha)
  130. run_die({ 'git', 'add', deps_file })
  131. local zig = zig_mode[symbol]
  132. if zig ~= false then
  133. if zig == 'nested' then
  134. -- don't care about un-cding, "git commit" doesn't care and then we die
  135. vim.fn.chdir('deps/' .. symbol)
  136. end
  137. -- note: get_gh_commit_sha is likely superfluous with zig. but use the same resolved hash, for consistency.
  138. run_die({
  139. 'zig',
  140. 'fetch',
  141. '--save=' .. symbol,
  142. 'git+https://github.com/' .. repo .. '#' .. _ref,
  143. })
  144. run_die({ 'git', 'add', 'build.zig.zon' })
  145. end
  146. run_die({
  147. 'git',
  148. 'commit',
  149. '-m',
  150. commit_prefix .. 'bump ' .. name .. ' to ' .. comment,
  151. }, 'git failed to commit')
  152. end
  153. local function usage()
  154. local this_script = tostring(vim.fs.basename(_G.arg[0]))
  155. local script_exe = './' .. this_script
  156. local help = ([=[
  157. Bump Nvim dependencies
  158. Usage: %s [options]
  159. Bump to HEAD, tagged version or commit:
  160. %s luv --head
  161. %s luv --ref 1.43.0-0
  162. %s luv --ref abc123
  163. Options:
  164. -h, --help show this message and exit.
  165. --list list all dependencies
  166. Dependency Options:
  167. --ref <ref> bump to a specific commit or tag.
  168. --head bump to a current head.
  169. ]=]):format(script_exe, script_exe, script_exe, script_exe)
  170. print(help)
  171. end
  172. local function list_deps()
  173. local l = 'Dependencies:\n'
  174. for k in vim.spairs(dependency_table) do
  175. l = string.format('%s\n%s%s', l, string.rep(' ', 2), k)
  176. end
  177. print(l)
  178. end
  179. do
  180. local args = {}
  181. local i = 1
  182. while i <= #_G.arg do
  183. if _G.arg[i] == '-h' or _G.arg[i] == '--help' then
  184. args.h = true
  185. elseif _G.arg[i] == '--list' then
  186. args.list = true
  187. elseif _G.arg[i] == '--ref' then
  188. args.ref = _G.arg[i + 1]
  189. i = i + 1
  190. elseif _G.arg[i] == '--head' then
  191. args.ref = 'HEAD'
  192. elseif vim.startswith(_G.arg[i], '--') then
  193. die(string.format('Invalid argument %s\n', _G.arg[i]))
  194. else
  195. args.dep = _G.arg[i]
  196. end
  197. i = i + 1
  198. end
  199. if args.h then
  200. usage()
  201. elseif args.list then
  202. list_deps()
  203. elseif args.ref then
  204. ref(args.dep, args.ref)
  205. else
  206. die('missing required arg\n')
  207. end
  208. end