bump_deps.lua 5.6 KB

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