-- Useful Lua utils function table.reverse(list) local reversed = {} for _,item in pairs(list) do table.insert(reversed, 1, item) end return reversed end function string.chomp(s) return s:gsub("%s*$", "") end function split_string(list, pattern) local return_table = {} for item in string.gmatch(list, pattern) do table.insert(return_table,item) end return return_table end -- Useful Monotone utils function table_mtn_automate(cmd, ...) local result, revs = mtn_automate(cmd, ...) return split_string(revs, "%s*(%S*)%s+") end function parse_certs(certs) local certs_table = {} local cert = "" for cert_chunk,space in string.gmatch(certs .. "\n\n", "(.-)(\n\n)") do cert = cert.. cert_chunk cert_t = parse_basic_io(cert) if(cert_t) then local cert_table = {} cert_chunk = "" for i,name_value in pairs(cert_t) do cert_table[name_value["name"]] = name_value["values"] end table.insert(certs_table, cert_table) end end return certs_table end function trusted_certs(certs) certs = parse_certs(certs) local trusted_certs = {} for _,cert in pairs(certs) do if(cert["name"] and cert["value"]) then name = cert["name"][1] if cert["trust"][1] == "trusted" then if not trusted_certs[name] then trusted_certs[name] = cert["value"] else for _,value in pairs(cert["value"]) do table.insert(trusted_certs[name], value) end end end end end --for n,v in pairs(trusted_certs) do -- print(n,table.concat(v," ")) --end return trusted_certs end list_limit = 2000 function big_toposort(list) local list_limit = math.floor(list_limit/2) * 2 if #list == 0 then return {} elseif #list < list_limit then local result, sorted = mtn_automate("toposort", unpack(list)) return split_string(sorted .. " ", "%s*(%S*)%s+") end local half_limit = list_limit/2 local bucket_count = math.ceil((#list)/half_limit) local buckets = {} for i = 0,(bucket_count-1) do table.insert(buckets,{unpack(list,(i*half_limit)+1,(i+1)*half_limit)}) end for j = 2,bucket_count do local sorted = table_mtn_automate("toposort", unpack(buckets[1]), unpack(buckets[j])) buckets[1] = {unpack(sorted,1,half_limit)} buckets[j] = {unpack(sorted,half_limit+1,list_limit)} end return buckets[1] -- You only get the first items back end function get_branch() local result,branch = mtn_automate("get_option", "branch") return branch:chomp() -- return string.gsub(branch, "%s*$", "") end function get_head_revision(branch) local result,head = mtn_automate("select", "h:" .. branch) return head:chomp() -- return string.gsub(head, "%s*$", "") end -- Changelog unique stuff begins here function get_release_revisions(branch) return get_sorted_revisions("b:" .. branch .. "/c:x-release") --local result,release_revs = mtn_automate("select", "b:" .. branch .. "/c:x-release") --release_revs = split_string(release_revs, "%s*(%S*)%s+") --result,release_revs = mtn_automate("toposort", unpack(release_revs)) --release_revs = split_string(release_revs, "%s*(%S*)%s+") --return release_revs end function select_revisions(selector) local result,revs = mtn_automate("select", selector) revs = split_string(revs, "%s*(%S*)%s+") return revs end function get_sorted_revisions(selector) local revs = select_revisions(selector) revs = big_toposort(revs) return revs end function extract_release_from(rev) local result,certs = mtn_automate("certs", rev) certs = trusted_certs(certs) local release = certs["x-release"][1] local release_table = {} for part in release:gmatch("[%d%a]+") do table.insert(release_table, part) end return release_table end --- Take a table of a version number, return a new version string function autoincrement_release(release_table) if #release_table < 3 then for i = (#release_table + 1),3 do table.insert(release_table,"0") end end major,minor,sub = unpack(release_table,1,3) return string.format("%s.%s.%d",major,minor,tonumber(sub)+1) end function mark_release(release_number, rev) if not release_number then local release_table local rel_revs = get_release_revisions(get_branch()) if(not rel_revs or #rel_revs == 0) then release_table = {0,0,0} else release_table = extract_release_from(rel_revs[#rel_revs]) end release_number = autoincrement_release(release_table) print(string.format("Automatically generating release number \"%s\"", release_number)) end local result,branch if not rev then rev = "h:" end local result, oldrev oldrev=rev rev = get_sorted_revisions(rev) if not rev then print("Can't resolve revision: " .. oldrev) return end if(#rev > 1) then print("Selector " .. oldrev .. " is ambiguous") return end rev = rev[1] mtn_automate("cert", rev, "x-release", release_number) mtn_automate("cert", rev, "changelog", "Marked release " .. release_number) end function output_changelog(out_path) local result, branch, head, release_revs, changelog_revs local certs, out, skip_pattern_list branch = get_branch() release_revs = get_release_revisions(branch) head = get_sorted_revisions("h:") if #release_revs > 0 then changelog_revs = table_mtn_automate("ancestry_difference", head[1], unpack(release_revs)) else changelog_revs = select_revisions("b:" .. branch) end changelog_revs = big_toposort(changelog_revs) changelog_revs = table.reverse(changelog_revs) if #release_revs > 0 then table.insert(changelog_revs,release_revs[#release_revs]) end if out_path == nil then out = io.stdout else out,result = io.open(out_path, "w") if(out == nil) then print("Can't open " .. out_path) return end end local skip_pattern_list = {} local skip_patterns = io.open(get_confdir() .. "/changelog_ignore", "r") if skip_patterns then for pattern in skip_patterns:lines() do table.insert(skip_pattern_list, pattern) end skip_patterns:close() end for _,rev in pairs(changelog_revs) do local result, certs = mtn_automate("certs", rev) cert = trusted_certs(certs) local value value = table.concat(cert["changelog"], "\n") value = string.gsub(value, "%s*$", "") local good=true for _,skip in pairs(skip_pattern_list) do if regex.search(skip, value) then good = false break end end if good then out:write(value .. "\n") end end out:close() end register_command("changelog", "[PATH]", "outputs changelog entries for workspace", "Prints out all of the changelogs for the current branch (possibly starting at the last x-release certificate)", "output_changelog") register_command("release", "[VERSION NUMBER [REVISION]]", "marks revision with a release", "Marks the current head with a release certificate.", "mark_release")