1 line
No EOL
19 KiB
Text
1 line
No EOL
19 KiB
Text
{"protected":false,"mode":"f","metadata":{"name":"mrrp","author":"Luna Magdalena :3","version":[1,1],"dependencies":[],"paths":{"mrrp.js":"%B/mrrp.js"}},"content":"{\"mrrp.js\":{\"protected\":true,\"metadata\":{\"runner\":[]},\"mode\":\"f\",\"content\":\"let yarnball\\nlet repoNode = fs.getNode(\\\"/itty/repos.json\\\")\\nlet repoList = JSON.parse(repoNode.content)\\nlet pkgList\\nlet updateList = []\\nlet world = io.flines(fs.getNode(\\\"/itty/world\\\")).filter(Boolean)\\nlet unwantedPkgs\\n\\n// we use some env vars, parse them\\nconst dirs = {\\n bin: fs.resolve(itty.env.dir_bin).slice(0, -1) || \\\"/itty/bin\\\",\\n docs: fs.resolve(itty.env.dir_docs).slice(0, -1) || \\\"/itty/docs\\\",\\n conf: fs.resolve(itty.env.dir_conf) + \\\"examples\\\" || \\\"/conf/examples\\\",\\n lib: fs.resolve(itty.env.dir_lib).slice(0, -1) || \\\"/itty/lib\\\"\\n}\\n\\n// cache repo indexes, so querying and updating is faster\\nlet repoCache = fs.getNode(\\\"/.tmp/mrrp/repocache\\\")\\nif (!repoCache) {\\n fs.makeDir(\\\"/.tmp/mrrp/\\\")\\n fs.makeNode(\\\"/.tmp/mrrp/repocache\\\", \\\"f\\\")\\n await refreshCache()\\n} else {\\n repoCache = JSON.parse(repoCache.content) \\n}\\n\\n// refresh the repo cache!\\nasync function refreshCache() {\\n repoCache = fs.getNode(\\\"/.tmp/mrrp/repocache\\\")\\n const list = {}\\n for (let r of Object.entries(repoList)) {\\n let rj\\n try {\\n rj = await fetch(r[1].url + \\\"repo.json\\\")\\n rj = await rj.json()\\n } catch {\\n display.print(\\\"Cannot fetch repository info for \\\" + r[0], 0x9000)\\n continue\\n }\\n rj.url = r[1].url\\n list[r[0]] = rj\\n }\\n repoCache.content = JSON.stringify(list)\\n repoCache = list\\n}\\n\\n// confirmation as to whether to do things\\nasync function ask(message) {\\n const res = await io.read([[message + \\\" [y/N]: \\\"]])\\n\\n if (res.toLowerCase() !== \\\"y\\\" && res.toLowerCase() !== \\\"yes\\\") {\\n display.print(\\\"Aborting...\\\", 0x9000)\\n return false\\n }\\n\\n display.print(\\\"Proceeding...\\\", 0x3000)\\n return true\\n}\\n\\n// install a program\\nasync function install(yarnball, reponame, interactive = true, wanted) {\\n wanted = wanted || world.includes(yarnball.metadata.name)\\n\\n const listNode = fs.getNode(\\\"/itty/packages.json\\\")\\n let list = JSON.parse(listNode.content)\\n\\n // substitute %B, %C and %D in paths with env vars\\n for (let path of Object.keys(yarnball.metadata.paths)) {\\n let p = yarnball.metadata.paths[path]\\n p = p.replaceAll(\\\"%B\\\", dirs.bin)\\n p = p.replaceAll(\\\"%D\\\", dirs.docs)\\n p = p.replaceAll(\\\"%C\\\", dirs.conf)\\n p = p.replaceAll(\\\"%L\\\", dirs.lib)\\n yarnball.metadata.paths[path] = p\\n }\\n\\n // ask the user!\\n if (interactive) {\\n const v = yarnball.metadata.version\\n display.print(\\\"Installing \\\" + yarnball.metadata.name + \\\" version \\\" + v[0] + (v[1] ? \\\".\\\" + v[1] : \\\".0\\\") + (v[2] ? \\\".\\\" + v[2] : \\\"\\\") + \\\" from \\\" + reponame, 0x5000)\\n display.print(\\\"Warning: this will affect the following path(s):\\\", 0x2000)\\n for (let path in yarnball.metadata.paths) {\\n display.print(yarnball.metadata.paths[path], 0x2000)\\n }\\n if (yarnball.metadata.dependencies.length)\\n display.print(\\\"The following dependencies also need to be installed:\\\", 0x6000)\\n for (let d of yarnball.metadata.dependencies) {\\n if (!list[d])\\n display.print(d, 0x6000)\\n }\\n if (!await ask(\\\"Proceed?\\\"))\\n return\\n }\\n\\n // uninstall if already installed\\n if (list[yarnball.metadata.name])\\n await uninstall(yarnball.metadata.name, false)\\n\\n // unspool the yarnball\\n fs.removeNode(\\\"/.tmp/mrrp/x\\\")\\n fs.makeDir(\\\"/.tmp/mrrp/x\\\")\\n const node = fs.getNode(\\\"/.tmp/mrrp/x\\\")\\n let content\\n\\n try {\\n content = JSON.parse(yarnball.content)\\n } catch {\\n io.error([5, \\\"Cannot parse yarnball\\\"])\\n }\\n\\n for (let file of Object.keys(content)) {\\n if (node.content[file])\\n continue\\n const obj = content[file]\\n node.content[file] = obj\\n }\\n fs.refreshPathAttributes(node)\\n\\n // move files accordingly\\n for (let path of Object.entries(yarnball.metadata.paths)) {\\n const paths = fs.splitPath(path[1])\\n makeDir(paths[0])\\n const parent = fs.getNode(paths[0])\\n parent.content[paths[1]] = fs.getNode(\\\"/.tmp/mrrp/x/\\\" + path[0])\\n fs.refreshPathAttributes(parent)\\n }\\n\\n // keep track!\\n list[yarnball.metadata.name] = {\\n name: yarnball.metadata.name,\\n repo: reponame,\\n version: yarnball.metadata.version,\\n paths: [],\\n dependencies: yarnball.metadata.dependencies\\n }\\n\\n for (let path of Object.entries(yarnball.metadata.paths))\\n list[yarnball.metadata.name].paths.push(path[1])\\n\\n listNode.content = JSON.stringify(list)\\n\\n if (wanted && !world.includes[yarnball.metadata.name])\\n world.push(yarnball.metadata.name)\\n\\n if (interactive)\\n display.print(\\\"Package \\\" + yarnball.metadata.name + \\\" installed\\\", 0x6000)\\n\\n // handle dependencies but after install because we only keep one package in the cache hehe this is awful i'm sorry\\n for (let d of yarnball.metadata.dependencies) {\\n if (list[d])\\n continue\\n const reponame = await fetchPkg(d)\\n if (!reponame) {\\n display.print(\\\"Could not install dependency \\\" + d, 0x9000)\\n continue\\n }\\n const yarnball = fs.getNode(\\\"/.tmp/mrrp/dl/yarn\\\")\\n await install(yarnball, reponame, interactive)\\n }\\n}\\n\\nasync function uninstall(package, interactive = true) {\\n const listNode = fs.getNode(\\\"/itty/packages.json\\\")\\n let list = JSON.parse(listNode.content)\\n\\n const pkginfo = list[package]\\n if (!pkginfo) {\\n if (interactive)\\n display.print(\\\"Package is not installed\\\", 0x6000)\\n return\\n }\\n \\n // ask the user!\\n if (interactive) {\\n display.print(\\\"Removing \\\" + pkginfo.name, 0x5000)\\n display.print(\\\"Warning: this will affect the following path(s):\\\", 0x2000)\\n for (let path of pkginfo.paths)\\n display.print(path, 0x2000)\\n \\n if (!await ask(\\\"Proceed?\\\"))\\n return\\n }\\n\\n // actually remove\\n for (let path of pkginfo.paths) {\\n const paths = fs.splitPath(path)\\n delete fs.getNode(paths[0]).content[paths[1]]\\n }\\n\\n delete list[pkginfo.name]\\n list = JSON.stringify(list)\\n listNode.content = list\\n\\n const i = world.indexOf(pkginfo.name)\\n if (i > -1)\\n world.splice(i)\\n\\n if (interactive)\\n display.print(\\\"Package removed\\\", 0x6000)\\n}\\n\\nasync function fetchPkg(package, repo, reponame, interactive = true) {\\n let repojson\\n let rlist = []\\n if (repo) {\\n repojson = repo\\n \\n if (!repojson) {\\n display.print(\\\"Cannot find repository info for \\\" + reponame, 0x2000)\\n io.error([1, \\\"Cannot find repository info\\\"])\\n }\\n } else {\\n // look for package in repos\\n for (let r of Object.entries(repoCache)) {\\n let rj = r[1]\\n \\n if (rj.packages[package])\\n rlist.push([r[0], r[1], rj])\\n }\\n \\n switch (rlist.length) {\\n case 0:\\n display.print(\\\"Cannot find package \\\" + package, 0x9000)\\n return false\\n case 1:\\n repojson = rlist[0][2]\\n repo = rlist[0][1]\\n reponame = rlist[0][0]\\n break\\n case 2:\\n display.print(\\\"Package \\\" + package + \\\" is available in the following repositories:\\\", 0x5000)\\n for (let i=0; i < rlist.length; i++) {\\n const v = rlist[i][2].packages[package].version\\n display.buffer(\\\"[\\\" + (i + 1) + \\\"] \\\")\\n display.buffer(rlist[i][0], 0x2000)\\n display.print(\\\" v\\\" + v[0] + (v[1] ? \\\".\\\" + v[1] : \\\".0\\\") + (v[2] ? \\\".\\\" + v[2] : \\\"\\\"), 0x4000)\\n }\\n const sel = await io.read([[\\\"Selection: \\\"]])\\n if (!sel)\\n return false\\n else {\\n repojson = rlist[sel-1][2]\\n repo = rlist[sel-1][1]\\n reponame = rlist[sel-1][0]\\n }\\n }\\n }\\n\\n // download\\n\\n let pkg\\n try {\\n pkg = await fetch(repojson.packages[package].url.replace(/^~/, repo.url))\\n pkg = await pkg.json()\\n } catch {\\n display.print(\\\"Cannot fetch package \\\" + package + \\\" from repository \\\" + reponame, 0x9000)\\n throw [2, \\\"Cannot fetch package\\\"]\\n }\\n\\n fs.removeNode(\\\"/.tmp/mrrp/dl\\\")\\n fs.makeDir(\\\"/.tmp/mrrp/dl\\\")\\n const dl = fs.getNode(\\\"/.tmp/mrrp/dl\\\")\\n dl.content.yarn = pkg\\n fs.refreshPathAttributes(dl)\\n\\n return reponame\\n}\\n\\nfunction want(package) {\\n const index = unwantedPkgs.indexOf(package)\\n if (index > -1)\\n unwantedPkgs.splice(index)\\n\\n if (pkgList[pkg])\\n for (let dep of pkgList[package].dependencies)\\n want(dep)\\n}\\n\\nswitch (args[0]) {\\n case \\\"yarn\\\":\\n if (args.length < 2) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n try {\\n yarnball = fs.getNode(args[1])\\n } catch {\\n io.error([1, \\\"Cannot find yarnball\\\"])\\n }\\n\\n await install(yarnball, \\\"local yarnball\\\", true, true)\\n break\\n case \\\"uninstall\\\":\\n case \\\"remove\\\":\\n if (args.length < 2) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n await uninstall(args[1])\\n break\\n case \\\"list-repos\\\":\\n for (let repo of Object.entries(repoList)) {\\n display.write(repo[0] + \\\" \\\", 0x2000)\\n display.print(repo[1].url)\\n }\\n break\\n case \\\"remove-repo\\\":\\n if (args.length < 2) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n delete repoList[args[1]]\\n repoNode.content = JSON.stringify(repoList)\\n display.print(\\\"Removed repo \\\" + args[1], 0x6000)\\n await refreshCache()\\n break\\n case \\\"add-repo\\\":\\n if (args.length < 3) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n \\n args[2] = args[2].replace(/([^\\\\/])$/, \\\"$1/\\\")\\n\\n // fetch repo\\n try {\\n repo = await fetch(args[2] + \\\"repo.json\\\")\\n } catch {\\n display.print(\\\"Cannot fetch repository info\\\", 0x9000)\\n quit()\\n }\\n\\n if (repoList[args[1]]) {\\n display.print(\\\"Repository \\\" + args[1] + \\\" already exists.\\\", 0x2000)\\n if (!await ask(\\\"Overwrite?\\\"))\\n quit()\\n }\\n\\n repoList[args[1]] = {\\n url: args[2]\\n }\\n\\n repoNode.content = JSON.stringify(repoList)\\n display.print(\\\"Added repo \\\" + args[1] + \\\" with url \\\" + args[2], 0x6000)\\n await refreshCache()\\n break\\n case \\\"install\\\":\\n if (args.length < 2) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n const reponame = await fetchPkg(args[1])\\n if (!reponame)\\n quit()\\n yarnball = fs.getNode(\\\"/.tmp/mrrp/dl/yarn\\\")\\n await install(yarnball, reponame, true, true)\\n break\\n case \\\"refresh-repos\\\":\\n await refreshCache()\\n display.print(\\\"Refreshed repository cache\\\", 0x6000)\\n break\\n case \\\"update\\\":\\n try {\\n pkgList = JSON.parse(fs.getNode(\\\"/itty/packages.json\\\").content)\\n } catch {\\n display.print(\\\"Cannot parse package list\\\", 0x9000)\\n quit() \\n }\\n\\n for (let pkg of Object.entries(pkgList)) {\\n if (pkg[1].repo === \\\"local yarnball\\\" || !repoCache[pkg[1].repo])\\n continue\\n\\n const iv = pkg[1].version\\n const rv = repoCache[pkg[1].repo].packages[pkg[0]].version\\n if (rv[0] > iv[0] || rv[0] == iv[0] && rv[1] > iv[1] || rv[0] == iv[0] && rv[1] == iv[1] && rv[2] > iv[2])\\n updateList.push([pkg[0], pkg[1].repo, repoCache[pkg[1].repo]])\\n }\\n\\n if (!updateList.length) {\\n display.print(\\\"No packages need updating\\\", 0x3000)\\n quit()\\n }\\n\\n if (args[1] !== \\\"bg\\\") {\\n display.print(\\\"Packages to update:\\\", 0x6000)\\n for (let pkg of updateList)\\n display.print(pkg[0], 0x2000)\\n if (!await ask(\\\"Proceed?\\\"))\\n quit()\\n }\\n\\n for (let pkg of updateList) {\\n let f\\n try {\\n f = await fetchPkg(pkg[0], pkg[2], pkg[1], false)\\n } catch {\\n if (args[1] !== \\\"bg\\\")\\n display.print(\\\"Could not fetch \\\" + pkg[0] + \\\" from \\\" + pkg[1], 0x1000)\\n continue\\n }\\n if (f) {\\n yarnball = fs.getNode(\\\"/.tmp/mrrp/dl/yarn\\\")\\n await install(yarnball, pkg[1], false)\\n }\\n }\\n\\n if (args[1] !== \\\"bg\\\")\\n display.print(\\\"Updated packages\\\", 0x3000)\\n break\\n case \\\"list\\\":\\n try {\\n pkgList = JSON.parse(fs.getNode(\\\"/itty/packages.json\\\").content)\\n } catch {\\n display.print(\\\"Cannot parse package list\\\", 0x9000)\\n quit() \\n }\\n\\n for (let pkg of Object.entries(pkgList)) {\\n display.buffer(pkg[0], 0x6000)\\n const v = pkg[1].version\\n display.buffer(\\\" v\\\" + v[0] + (v[1] ? \\\".\\\" + v[1] : \\\".0\\\") + (v[2] ? \\\".\\\" + v[2] : \\\"\\\") + \\\" \\\", 0x4000)\\n display.buffer(\\\"(\\\")\\n display.buffer(pkg[1].repo, 0x2000)\\n display.print(\\\")\\\")\\n }\\n break\\n case \\\"query\\\":\\n if (args.length < 2) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n try {\\n pkgList = JSON.parse(fs.getNode(\\\"/itty/packages.json\\\").content)\\n } catch {\\n display.print(\\\"Cannot parse package list\\\", 0x9000)\\n quit() \\n }\\n \\n for (let repo of Object.entries(repoCache)) {\\n for (let pkg of Object.entries(repo[1].packages)) {\\n if (pkg[0].match(args[1])) {\\n display.buffer(\\\"[\\\")\\n display.buffer(repo[0], 0x2000)\\n display.buffer(\\\"]%t\\\")\\n display.buffer(pkg[0], 0x6000)\\n const v = pkg[1].version\\n display.buffer(\\\" v\\\" + v[0] + (v[1] ? \\\".\\\" + v[1] : \\\".0\\\") + (v[2] ? \\\".\\\" + v[2] : \\\"\\\"), 0x4000)\\n if (pkgList[pkg[0]] && pkgList[pkg[0]].repo == repo[0]) {\\n display.buffer(\\\"%t(\\\")\\n const v = pkgList[pkg[0]].version\\n if (pkg[1].version[0] != v[0] || pkg[1].version[1] != v[1] || pkg[1].version[2] != v[2])\\n display.buffer(\\\"v\\\" + v[0] + (v[1] ? \\\".\\\" + v[1] : \\\".0\\\") + (v[2] ? \\\".\\\" + v[2] : \\\"\\\") + \\\" \\\", 0x5000)\\n display.buffer(\\\"installed\\\", 0x5000)\\n display.buffer(\\\")\\\")\\n }\\n display.write(\\\"%n\\\")\\n }\\n }\\n }\\n break\\n case \\\"prune\\\":\\n try {\\n pkgList = JSON.parse(fs.getNode(\\\"/itty/packages.json\\\").content)\\n } catch {\\n display.print(\\\"Cannot parse package list\\\", 0x9000)\\n quit() \\n }\\n unwantedPkgs = Object.keys(pkgList)\\n\\n for (let pkg of world)\\n want(pkg)\\n\\n if (unwantedPkgs.length) {\\n display.print(\\\"The following packages will be removed:\\\", 0x6000)\\n for (let pkg of unwantedPkgs)\\n display.print(pkg, 0x2000)\\n\\n if (await ask(\\\"Proceed?\\\"))\\n for (let pkg of unwantedPkgs)\\n uninstall(pkg, false)\\n } else\\n display.print(\\\"No unwanted packages to remove\\\", 0x3000)\\n break\\n case \\\"batch\\\":\\n if (args.length < 2) {\\n display.print(\\\"Not enough arguments\\\", 0x1000)\\n quit()\\n }\\n const batchList = io.flines(fs.getNode(args[1])).filter(Boolean)\\n if (!batchList) {\\n display.print(\\\"Cannot find file\\\", 0x9000)\\n quit()\\n }\\n for (let pkg of batchList) {\\n const reponame = await fetchPkg(pkg)\\n if (!reponame)\\n quit()\\n yarnball = fs.getNode(\\\"/.tmp/mrrp/dl/yarn\\\")\\n await install(yarnball, reponame, true, true)\\n }\\n}\\n\\nconst worldfile = io.open(\\\"/itty/world\\\", \\\"w\\\")\\nfor (let w of world)\\n worldfile.print(w)\\nworldfile.writeOut()\\n\\nquit()\\n\\n// fs functions but copies that don't care about protectedness\\nfunction makeNode(path, mode) {\\n path = fs.resolve(path)\\n\\n // mode is bad?\\n if (mode != \\\"d\\\" && mode != \\\"f\\\")\\n throw [2, \\\"Invalid mode\\\"]\\n\\n // skip if it exists\\n const node = fs.getNode(path)\\n if (node && node.mode === \\\"d\\\")\\n return\\n else if (node)\\n throw [1, \\\"Node already exists\\\"]\\n\\n const paths = fs.splitPath(path)\\n const parentNode = fs.getNode(paths[0])\\n const pathEnd = paths[1]\\n\\n if (!parentNode)\\n throw [10, \\\"Parent node does not exist\\\"]\\n\\n // make the node\\n parentNode.content[pathEnd] = {\\n mode: mode,\\n protected: parentNode.protected ? true : false,\\n content: mode === \\\"d\\\" ? {} : \\\"\\\",\\n }\\n fs.setPathAttributes(parentNode.content[pathEnd], parentNode, pathEnd)\\n}\\n\\nfunction makeDir(path) {\\n path = fs.resolve(path)\\n const pathArray = path.split(\\\"/\\\").filter(Boolean)\\n let currentPath = \\\"\\\"\\n for (let part of pathArray) {\\n currentPath += \\\"/\\\" + part\\n makeNode(currentPath, \\\"d\\\")\\n }\\n}\"}}"} |