#!/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 check_tags() local repidx, oknum = 0, 0 local linesep = "===============================================================================\n" local redb, rede = string.char(0x1b) .. "[31m", string.char(0x1b) .. "[0m" while true do repidx = repidx + 1 local rinfo = g_repinfo[repidx] 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() local okay, output, comid = nil, nil, nil if rinfo.tag then io.stdout:write(gfmt("INFO: checking tag in repository: %s => %s\n", rinfo.path, rinfo.tag)) io.stdout:flush() okay, comid = invoker.invoke(invoker.OUTPUT + invoker.TRIMEND, "git", "log", "--pretty=%H", "-1") if okay ~= 0 or type(comid) ~= "string" or #comid ~= 40 then comid = nil end okay, output = invoker.invoke(invoker.OUTPUT + invoker.TRIMEND, "/bin/sh", "-c", gfmt("git show --stat --pretty=%%H \"%s\" | head -n 1", rinfo.tag)) if okay ~= 0 or type(output) ~= "string" or comid ~= output then io.stdout:write(gfmt("%sERROR: tag not found in: %s => %s%s\n", redb, rinfo.path, rinfo.tag, rede)) io.stdout:flush() else oknum = oknum + 1 io.stdout:write(gfmt("INFO: tag found in repository: %s => %s\n", rinfo.path, rinfo.tag)) io.stdout:flush() end else okay, output = invoker.invoke(invoker.OUTPUT, "git", "branch") if okay ~= 0 or type(output) ~= "string" or not string.find(output, gfmt("* %s\n", rinfo.branch), 1, true) then io.stderr:write(gfmt("%sERROR: branch not checked out in '%s' => %s%s\n", redb, rinfo.path, rinfo.branch, rede)) io.stderr:flush() return false else oknum = oknum + 1 io.stdout:write(gfmt("INFO: branch found in repository: %s => %s\n", rinfo.path, rinfo.branch)) io.stdout:flush() end end 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 = check_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)