#!/opt/Lua51/bin/lua -- Created by jiaqiang.ye@lnxall.com -- Simple Git Repository Branch-Tag Management -- 2025/07/28 local cjson = require "cjson" local invoker = require "invoker" local gfmt = string.format -- name of environment variable local TAGMAN_MAXSIZE = 0x100000 -- table holding Git repository branch/tag information local g_repinfo = {} local g_pathidx = {} -- directory path for `wlos_core local g_topdir = invoker.getcwd() -- configuration Lua script local g_cfgfile = nil local function goodstr(s) if type(s) ~= "string" then return false end if string.len(s) == 0 then return false end return true end local function create_tags() local repidx, oknum = 0, 0 local linesep = "===============================================================================\n" while true do repidx = repidx + 1 local rinfo, okay = g_repinfo[repidx], nil if not rinfo then break end -- Goto TOPDIR directory if invoker.chdir(g_topdir) ~= 0 then io.stderr:write(gfmt("Error, failed to go back to TOPDIR: %s\n", g_topdir)) io.stderr:flush() return false end -- Then goto git repository if invoker.chdir(rinfo.path) ~= 0 then io.stderr:write(gfmt("Error, failed to goto directory: %s\n", rinfo.path)) io.stderr:flush() return false end io.stdout:write(linesep) io.stdout:write(linesep) io.stdout:flush() if rinfo.tag then io.stdout:write(gfmt("INFO: checking tag in repository: %s => %s\n", rinfo.path, rinfo.tag)) io.stdout:flush() okay = invoker.invoke(invoker.NOSTDIO, "git", "show", rinfo.tag) if okay ~= 0 then io.stdout:write(gfmt("INFO: Creating tag in repository: %s => %s\n", rinfo.path, rinfo.tag)) io.stdout:flush() invoker.invoke(0, "git", "fetch", "--all", "--tags") okay = invoker.invoke(0, "git", "checkout", rinfo.branch) if okay == 0 then okay = invoker.invoke(0, "git", "pull", "--rebase", "origin", rinfo.branch) end if okay == 0 then okay = invoker.invoke(0, "git", "tag", rinfo.tag) end else io.stdout:write(gfmt("INFO: tag already found in repository: %s => %s\n", rinfo.path, rinfo.tag)) io.stdout:flush() -- okay = invoker.invoke(0, "git", "checkout", rinfo.tag) end if okay == 0 then okay = invoker.invoke(0, "git", "push", "origin", rinfo.tag) end if okay ~= 0 then io.stderr:write(gfmt("Error, failed to create and push tag in '%s' => %s\n", rinfo.path, rinfo.tag)) io.stderr:flush() return false end else invoker.invoke(0, "git", "fetch", "--all", "--tags") okay = invoker.invoke(0, "git", "checkout", rinfo.branch) if okay == 0 then okay = invoker.invoke(0, "git", "pull", "--rebase", "origin", rinfo.branch) end if okay ~= 0 then io.stderr:write(gfmt("Error, failed to checkout branch in '%s' => %s\n", rinfo.path, rinfo.branch)) io.stderr:flush() return false end end oknum = oknum + 1 end return oknum end local function tagman_load() local cfgd = invoker.readfile(g_cfgfile, TAGMAN_MAXSIZE) if not cfgd then io.stderr:write(gfmt("Error, failed to read file: %s\n", g_cfgfile)) io.stderr:flush() return false end local okay, jdat = pcall(cjson.decode, cfgd) cfgd = nil -- free memory early if not okay or type(jdat) ~= "table" then io.stderr:write(gfmt("Error, failed to decode as JSON: %s\n", g_cfgfile)) io.stderr:flush() return false end local repos = jdat["repolist"] if type(repos) ~= "table" then io.stderr:write(gfmt("Error, repolist not found in %s\n", g_cfgfile)) io.stderr:flush() return false end local repidx = 0 while true do repidx = repidx + 1 local rinfo = repos[repidx] if type(rinfo) ~= "table" then break end -- extract Git repo information local path_g, branch, tagv = rinfo['path'], rinfo['branch'], rinfo['tag'] if not goodstr(path_g) then io.stderr:write(gfmt("Error, invalid git repository information at index: %d\n", repidx)) io.stderr:flush() return false end local dotgit = gfmt("%s/.git", path_g) if invoker.statfile(path_g) ~= "directory" or invoker.statfile(dotgit) ~= "directory" then io.stderr:write(gfmt("Error, invalid git repository path: %s\n", path_g)) io.stderr:flush() return false end if g_pathidx[path_g] then -- check for git duplication io.stderr:write(gfmt("Error, duplicated git repository: %s\n", path_g)) io.stderr:flush() return false end if not goodstr(branch) then io.stderr:write(gfmt("Error, branch not specified for %s\n", path_g)) io.stderr:flush() return false end if not goodstr(tagv) then tagv = nil end g_pathidx[path_g] = repidx g_repinfo[repidx] = { ["path"] = path_g, ["branch"] = branch, ["tag"] = tagv, } end return true end local function createtag_init(jfile) invoker.setenv("FTOPDIR", nil) invoker.setenv("LANGUAGE", nil) invoker.setenv("LANG", "en_US.UTF-8") if not goodstr(g_topdir) then io.stderr:write("Error, FTOPDIR not found in environment variable.\n") io.stderr:flush() return false end -- determine configuration file if not goodstr(jfile) then io.stderr:write("Error, JSON file not specified.\n") io.stderr:flush() return false end local ftype, fsize = invoker.statfile(jfile) if ftype ~= "regular" then io.stderr:write(gfmt("Error, tagman config file not found: %s\n", jfile)) io.stderr:flush() return false end if fsize == 0 or fsize > TAGMAN_MAXSIZE then io.stderr:write(gfmt("Error, invalid file size: %s\n", jfile)) io.stderr:flush() return false end g_cfgfile = jfile io.stdout:write(gfmt("INFO: using branch/tag-config file: %s\n", jfile)) io.stdout:flush() -- load the configuration file return tagman_load() end if not createtag_init(arg[1]) then os.exit(1) end local numok = create_tags() if numok then io.stdout:write(gfmt("Number of repositories processed: %d\n", numok)) io.stdout:flush() os.exit(0) end os.exit(2)