f164d5cd5b9cfecc0c35f7e08350a2a768e46457 mspeir Wed Sep 16 11:40:45 2026 -0700 Sort attributes in staticPage.lua so the docs build is reproducible, no Redmine attributes() walked the attribute table with pairs(), which has no defined order, so any element with two or more attributes came out in a different order from one build to the next. Eight identical builds of gb101.md produced two distinct outputs. It renders the same either way, but it makes the install rsync re-push files nobody edited and makes a byte comparison of any docs change meaningless. Sorting the keys fixes it; no page changes semantically. Co-Authored-By: Claude Opus 5 (1M context) diff --git docs/staticPage.lua docs/staticPage.lua index d6ff5208871..d07680a155d 100644 --- docs/staticPage.lua +++ docs/staticPage.lua @@ -21,33 +21,43 @@ return '>' elseif x == '&' then return '&' elseif x == '"' then return '"' elseif x == "'" then return ''' else return x end end) end -- Helper function to convert an attributes table into -- a string that can be put into HTML tags. +-- The keys are sorted first because pairs() has no defined order, so an element +-- with more than one attribute would otherwise come out in a different order +-- from one build to the next. That renders the same, but it makes the output +-- unreproducible and leaves the install rsync re-pushing files nobody edited. local function attributes(attr) + local keys = {} + for x in pairs(attr) do + table.insert(keys, x) + end + table.sort(keys) local attr_table = {} - for x,y in pairs(attr) do + for _,x in ipairs(keys) do + local y = attr[x] if y and y ~= "" then table.insert(attr_table, ' ' .. x .. '="' .. escape(y,true) .. '"') end end return table.concat(attr_table) end -- Run cmd on a temporary file containing inp and return result. local function pipe(cmd, inp) local tmp = os.tmpname() local tmph = io.open(tmp, "w") tmph:write(inp) tmph:close() local outh = io.popen(cmd .. " " .. tmp,"r") local result = outh:read("*all")