melzneni

TURTI2_GIT_API

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