melzneni

TURTI2_GIT_API

Jun 27th, 2025 (edited)
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.57 KB | None | 0 0
  1. local git = { token = nil }
  2.  
  3. local function indexBy(contents, key)
  4.     local indexedContents = {}
  5.     for _, entry in ipairs(contents) do
  6.         indexedContents[entry[key]] = entry
  7.     end
  8.     return indexedContents
  9. end
  10.  
  11. local BASE64_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  12.  
  13. local function decodeBase64(data)
  14.     --http://lua-users.org/wiki/BaseSixtyFour
  15.     data = string.gsub(data, '[^' .. BASE64_CHARACTERS .. '=]', '')
  16.     return (data:gsub('.', function(x)
  17.         if (x == '=') then
  18.             return ''
  19.         end
  20.         local r, f = '', (BASE64_CHARACTERS:find(x) - 1)
  21.         for i = 6, 1, -1 do
  22.             r = r .. (f % 2 ^ i - f % 2 ^ (i - 1) > 0 and '1' or '0')
  23.         end
  24.         return r;
  25.     end)        :gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
  26.         if (#x ~= 8) then
  27.             return ''
  28.         end
  29.         local c = 0
  30.         for i = 1, 8 do
  31.             c = c + (x:sub(i, i) == '1' and 2 ^ (8 - i) or 0)
  32.         end
  33.         return string.char(c)
  34.     end))
  35. end
  36.  
  37. local function fetchFile(url)
  38.     local response
  39.     if git.token == nil then
  40.         response = http.get(url)
  41.     else
  42.         response = http.get(url, { Authorization = "Bearer " .. git.token })
  43.     end
  44.     if response == nil then
  45.         error("failed to read from " .. url)
  46.     end
  47.     return response.readAll()
  48. end
  49.  
  50. local function fetchJson(url)
  51.     return textutils.unserializeJSON(fetchFile(url))
  52. end
  53.  
  54. function git.fetchContents(user, repo, path)
  55.     if path == nil then
  56.         path = ""
  57.     end
  58.     return fetchJson("https://api.github.com/repos/" .. user .. "/" .. repo .. "/contents/" .. path)
  59. end
  60.  
  61. function git.fetchTree(user, repo, branch)
  62.     return fetchJson("https://api.github.com/repos/" .. user .. "/" .. repo .. "/git/trees/" .. branch .. "?recursive=1")
  63. end
  64.  
  65. local function clearLines(targetY)
  66.     local _, cursorY = term.getCursorPos()
  67.     for y = targetY, cursorY do
  68.         term.setCursorPos(1, y)
  69.         term.clearLine()
  70.     end
  71.     term.setCursorPos(1, targetY)
  72. end
  73.  
  74. function git.checkoutLibrary(user, repo, library, targetDir)
  75.     local repoContents = indexBy(git.fetchContents(user, repo), "path")
  76.  
  77.     local contentsDefinitionFile = repoContents["contents.json"]
  78.     if contentsDefinitionFile == nil then
  79.         error("Repo " .. user .. "/" .. repo .. " doesn't define contents.json")
  80.     end
  81.  
  82.     local contentsDefinition = fetchJson(contentsDefinitionFile.download_url)
  83.  
  84.     local libraryDefinition = indexBy(contentsDefinition, "name")[library]
  85.  
  86.     if libraryDefinition == nil then
  87.         error("Library " .. library .. " doesn't exist in repo " .. user .. "/" .. repo)
  88.     end
  89.  
  90.     local version = libraryDefinition["version"]
  91.     local rootPackage = libraryDefinition["package"]
  92.     if rootPackage == nil then
  93.         rootPackage = ""
  94.     end
  95.     local libraryContentsDefinition = libraryDefinition["contents"]
  96.     if libraryContentsDefinition == nil then
  97.         error("Library " .. user .. "/" .. repo .. "/" .. library .. " doesn't define any contents")
  98.     end
  99.  
  100.     local contentUrls = git.fetchLibraryContentInfo(user, repo, library, libraryContentsDefinition, rootPackage)
  101.  
  102.     print("Pulling library " .. library .. " in version " .. version)
  103.     local entryCount = #contentUrls
  104.     local _, cursorY = term.getCursorPos()
  105.     for index, entry in pairs(contentUrls) do
  106.         clearLines(cursorY)
  107.         io.write(index .. "/" .. entryCount .. ": " .. entry.path)
  108.         local blobConfig = fetchJson(entry.url)
  109.  
  110.         if blobConfig["encoding"] ~= "base64" then
  111.             error("Unknown blob encoding at " .. entry.url)
  112.         end
  113.         local data = decodeBase64(blobConfig["content"])
  114.  
  115.         local path = targetDir .. "/" .. entry.path
  116.         local dir = fs.getDir(path)
  117.  
  118.         if not fs.exists(dir) then
  119.             fs.makeDir(dir)
  120.         end
  121.         local file = fs.open(path, "w")
  122.         file.write(data)
  123.         file.close()
  124.     end
  125.     clearLines(cursorY)
  126.     print("done")
  127. end
  128.  
  129. local PathTree = {}
  130. PathTree.__index = PathTree
  131.  
  132. function PathTree.new()
  133.     local tree = { children = {}, path = "" }
  134.     setmetatable(tree, PathTree)
  135.     return tree
  136. end
  137.  
  138. function PathTree:getOrCreateChild(name)
  139.     local child = self.children[name]
  140.     if child == nil then
  141.         child = PathTree.new()
  142.         if self.path == "" then
  143.             child.path = name
  144.         else
  145.             child.path = self.path .. "/" .. name
  146.         end
  147.         self.children[name] = child
  148.     end
  149.     return child
  150. end
  151.  
  152. function PathTree:insert(rawPath)
  153.     local node = self
  154.     for part in string.gmatch(rawPath, "[^/]+") do
  155.         node = node:getOrCreateChild(part)
  156.     end
  157.     return node
  158. end
  159.  
  160. function PathTree:remove(childName)
  161.     self.children[childName] = nil
  162. end
  163.  
  164. function PathTree:getChildrenDeep(children)
  165.     if children == nil then
  166.         children = {}
  167.     end
  168.     for _, child in pairs(self.children) do
  169.         table.insert(children, child)
  170.         child:getChildrenDeep(children)
  171.     end
  172.     return children
  173. end
  174.  
  175. local function removeExcludedTree(tree, excludedTree)
  176.     for name, subExcludedTree in pairs(excludedTree.children) do
  177.         if subExcludedTree.exclude then
  178.             tree:remove(name)
  179.         else
  180.             local subTree = tree.children[name]
  181.             if subTree ~= nil then
  182.                 removeExcludedTree(subTree, excludedTree)
  183.             end
  184.         end
  185.     end
  186. end
  187.  
  188. local function removeNonIncludedTree(tree, includedTree)
  189.     if includedTree.children["*"] ~= nil then
  190.         return
  191.     end
  192.  
  193.     for name, subTree in pairs(tree.children) do
  194.         local subIncludeTree = includedTree.children[name]
  195.         if subIncludeTree == nil then
  196.             tree:remove(name)
  197.         else
  198.             removeNonIncludedTree(subTree, subIncludeTree)
  199.         end
  200.     end
  201. end
  202.  
  203. local function removeBasePath(path, basePath)
  204.     local pathIterator = string.gmatch(path, "[^/]+")
  205.     for basePathPart in string.gmatch(basePath, "[^/]+") do
  206.         local pathPart = pathIterator()
  207.         if basePathPart ~= pathPart then
  208.             return nil
  209.         end
  210.     end
  211.     local remainingParts = {}
  212.     for remainingPart in pathIterator do
  213.         table.insert(remainingParts, remainingPart)
  214.     end
  215.     return table.concat(remainingParts, "/")
  216.  
  217. end
  218.  
  219. function git.fetchLibraryContentInfo(user, repo, library, contentsDefinition, basePath)
  220.  
  221.     local gitPaths = indexBy(git.fetchTree(user, repo, "master")["tree"], "path")
  222.  
  223.     local gitPathsTree = PathTree.new()
  224.     for path, entry in pairs(gitPaths) do
  225.         path = removeBasePath(path, basePath)
  226.         if path ~= nil and path ~= "" and entry.type == "blob" then
  227.             gitPathsTree:insert(path).url = entry.url
  228.         end
  229.     end
  230.  
  231.     if gitPathsTree == nil then
  232.         error("library " .. library .. " has invalid basePath: " .. basePath)
  233.     end
  234.  
  235.     local includedPathsTree = PathTree.new()
  236.     local excludedPathsTree = PathTree.new()
  237.  
  238.     for _, includedPath in pairs(contentsDefinition["include"]) do
  239.         includedPathsTree:insert(includedPath)
  240.     end
  241.     for _, excludedPath in pairs(contentsDefinition["exclude"]) do
  242.         excludedPathsTree:insert(excludedPath).exclude = true
  243.     end
  244.  
  245.     removeExcludedTree(gitPathsTree, excludedPathsTree)
  246.     removeNonIncludedTree(gitPathsTree, includedPathsTree)
  247.  
  248.     local children = {}
  249.     for _, child in pairs(gitPathsTree:getChildrenDeep()) do
  250.         if child.url ~= nil then
  251.             table.insert(children, child)
  252.         end
  253.     end
  254.  
  255.     return children
  256. end
  257.  
  258. return git
Add Comment
Please, Sign In to add comment