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