summaryrefslogtreecommitdiff
path: root/src/luarocks/admin
diff options
context:
space:
mode:
authorMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
committerMike Vink <mike@pionative.com>2025-02-03 21:29:42 +0100
commit5155816b7b925dec5d5feb1568b1d7ceb00938b9 (patch)
treedeca28ea15e79f6f804c3d90d2ba757881638af5 /src/luarocks/admin
fetch tarballHEADmaster
Diffstat (limited to 'src/luarocks/admin')
-rw-r--r--src/luarocks/admin/cache.lua88
-rw-r--r--src/luarocks/admin/cmd/add.lua134
-rw-r--r--src/luarocks/admin/cmd/make_manifest.lua50
-rw-r--r--src/luarocks/admin/cmd/refresh_cache.lua31
-rw-r--r--src/luarocks/admin/cmd/remove.lua95
-rw-r--r--src/luarocks/admin/index.lua185
6 files changed, 583 insertions, 0 deletions
diff --git a/src/luarocks/admin/cache.lua b/src/luarocks/admin/cache.lua
new file mode 100644
index 0000000..7a4e4af
--- /dev/null
+++ b/src/luarocks/admin/cache.lua
@@ -0,0 +1,88 @@
+
+--- Module handling the LuaRocks local cache.
+-- Adds a rock or rockspec to a rocks server.
+local cache = {}
+
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local util = require("luarocks.util")
+
+function cache.get_upload_server(server)
+ if not server then server = cfg.upload_server end
+ if not server then
+ return nil, "No server specified and no default configured with upload_server."
+ end
+ return server, cfg.upload_servers and cfg.upload_servers[server]
+end
+
+function cache.get_server_urls(server, upload_server)
+ local download_url = server
+ local login_url = nil
+ if upload_server then
+ if upload_server.rsync then download_url = "rsync://"..upload_server.rsync
+ elseif upload_server.http then download_url = "http://"..upload_server.http
+ elseif upload_server.ftp then download_url = "ftp://"..upload_server.ftp
+ end
+
+ if upload_server.ftp then login_url = "ftp://"..upload_server.ftp
+ elseif upload_server.sftp then login_url = "sftp://"..upload_server.sftp
+ end
+ end
+ return download_url, login_url
+end
+
+function cache.split_server_url(url, user, password)
+ local protocol, server_path = dir.split_url(url)
+ if protocol == "file" then
+ server_path = fs.absolute_name(server_path)
+ elseif server_path:match("@") then
+ local credentials
+ credentials, server_path = server_path:match("([^@]*)@(.*)")
+ if credentials:match(":") then
+ user, password = credentials:match("([^:]*):(.*)")
+ else
+ user = credentials
+ end
+ end
+ local local_cache = dir.path(cfg.local_cache, (server_path:gsub("[\\/]", "_")))
+ return local_cache, protocol, server_path, user, password
+end
+
+local function download_cache(protocol, server_path, user, password)
+ os.remove("index.html")
+ -- TODO abstract away explicit 'wget' call
+ if protocol == "rsync" then
+ local srv, path = server_path:match("([^/]+)(/.+)")
+ return fs.execute(cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..user.."@"..srv..":"..path.."/ ./")
+ elseif protocol == "file" then
+ return fs.copy_contents(server_path, ".")
+ else
+ local login_info = ""
+ if user then login_info = " --user="..user end
+ if password then login_info = login_info .. " --password="..password end
+ return fs.execute(cfg.variables.WGET.." --no-cache -q -m -np -nd "..protocol.."://"..server_path..login_info)
+ end
+end
+
+function cache.refresh_local_cache(url, given_user, given_password)
+ local local_cache, protocol, server_path, user, password = cache.split_server_url(url, given_user, given_password)
+
+ local ok, err = fs.make_dir(local_cache)
+ if not ok then
+ return nil, "Failed creating local cache dir: "..err
+ end
+
+ fs.change_dir(local_cache)
+
+ util.printout("Refreshing cache "..local_cache.."...")
+
+ ok = download_cache(protocol, server_path, user, password)
+ if not ok then
+ return nil, "Failed downloading cache."
+ end
+
+ return local_cache, protocol, server_path, user, password
+end
+
+return cache
diff --git a/src/luarocks/admin/cmd/add.lua b/src/luarocks/admin/cmd/add.lua
new file mode 100644
index 0000000..aa444c5
--- /dev/null
+++ b/src/luarocks/admin/cmd/add.lua
@@ -0,0 +1,134 @@
+
+--- Module implementing the luarocks-admin "add" command.
+-- Adds a rock or rockspec to a rocks server.
+local add = {}
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local writer = require("luarocks.manif.writer")
+local fs = require("luarocks.fs")
+local cache = require("luarocks.admin.cache")
+local index = require("luarocks.admin.index")
+
+function add.add_to_parser(parser)
+ local cmd = parser:command("add", "Add a rock or rockspec to a rocks server.", util.see_also())
+
+ cmd:argument("rock", "A local rockspec or rock file.")
+ :args("+")
+
+ cmd:option("--server", "The server to use. If not given, the default server "..
+ "set in the upload_server variable from the configuration file is used instead.")
+ :target("add_server")
+ cmd:flag("--no-refresh", "Do not refresh the local cache prior to "..
+ "generation of the updated manifest.")
+ cmd:flag("--index", "Produce an index.html file for the manifest. This "..
+ "flag is automatically set if an index.html file already exists.")
+end
+
+local function zip_manifests()
+ for ver in util.lua_versions() do
+ local file = "manifest-"..ver
+ local zip = file..".zip"
+ fs.delete(dir.path(fs.current_dir(), zip))
+ fs.zip(zip, file)
+ end
+end
+
+local function add_files_to_server(refresh, rockfiles, server, upload_server, do_index)
+ assert(type(refresh) == "boolean" or not refresh)
+ assert(type(rockfiles) == "table")
+ assert(type(server) == "string")
+ assert(type(upload_server) == "table" or not upload_server)
+
+ local download_url, login_url = cache.get_server_urls(server, upload_server)
+ local at = fs.current_dir()
+ local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url
+
+ local local_cache, protocol, server_path, user, password = refresh_fn(download_url, cfg.upload_user, cfg.upload_password)
+ if not local_cache then
+ return nil, protocol
+ end
+
+ if not login_url then
+ login_url = protocol.."://"..server_path
+ end
+
+ local ok, err = fs.change_dir(at)
+ if not ok then return nil, err end
+
+ local files = {}
+ for _, rockfile in ipairs(rockfiles) do
+ if fs.exists(rockfile) then
+ util.printout("Copying file "..rockfile.." to "..local_cache.."...")
+ local absolute = fs.absolute_name(rockfile)
+ fs.copy(absolute, local_cache, "read")
+ table.insert(files, dir.base_name(absolute))
+ else
+ util.printerr("File "..rockfile.." not found")
+ end
+ end
+ if #files == 0 then
+ return nil, "No files found"
+ end
+
+ local ok, err = fs.change_dir(local_cache)
+ if not ok then return nil, err end
+
+ util.printout("Updating manifest...")
+ writer.make_manifest(local_cache, "one", true)
+
+ zip_manifests()
+
+ if fs.exists("index.html") then
+ do_index = true
+ end
+
+ if do_index then
+ util.printout("Updating index.html...")
+ index.make_index(local_cache)
+ end
+
+ local login_info = ""
+ if user then login_info = " -u "..user end
+ if password then login_info = login_info..":"..password end
+ if not login_url:match("/$") then
+ login_url = login_url .. "/"
+ end
+
+ if do_index then
+ table.insert(files, "index.html")
+ end
+ table.insert(files, "manifest")
+ for ver in util.lua_versions() do
+ table.insert(files, "manifest-"..ver)
+ table.insert(files, "manifest-"..ver..".zip")
+ end
+
+ -- TODO abstract away explicit 'curl' call
+
+ local cmd
+ if protocol == "rsync" then
+ local srv, path = server_path:match("([^/]+)(/.+)")
+ cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/"
+ elseif protocol == "file" then
+ return fs.copy_contents(local_cache, server_path)
+ elseif upload_server and upload_server.sftp then
+ local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$")
+ cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2
+ else
+ cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url
+ end
+
+ util.printout(cmd)
+ return fs.execute(cmd)
+end
+
+function add.command(args)
+ local server, server_table = cache.get_upload_server(args.add_server or args.server)
+ if not server then return nil, server_table end
+ return add_files_to_server(not args.no_refresh, args.rock, server, server_table, args.index)
+end
+
+
+return add
diff --git a/src/luarocks/admin/cmd/make_manifest.lua b/src/luarocks/admin/cmd/make_manifest.lua
new file mode 100644
index 0000000..18f74b5
--- /dev/null
+++ b/src/luarocks/admin/cmd/make_manifest.lua
@@ -0,0 +1,50 @@
+
+--- Module implementing the luarocks-admin "make_manifest" command.
+-- Compile a manifest file for a repository.
+local make_manifest = {}
+
+local writer = require("luarocks.manif.writer")
+local index = require("luarocks.admin.index")
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local deps = require("luarocks.deps")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+
+function make_manifest.add_to_parser(parser)
+ local cmd = parser:command("make_manifest", "Compile a manifest file for a repository.", util.see_also())
+
+ cmd:argument("repository", "Local repository pathname.")
+ :args("?")
+
+ cmd:flag("--local-tree", "If given, do not write versioned versions of the manifest file.\n"..
+ "Use this when rebuilding the manifest of a local rocks tree.")
+ util.deps_mode_option(cmd)
+end
+
+--- Driver function for "make_manifest" command.
+-- @return boolean or (nil, string): True if manifest was generated,
+-- or nil and an error message.
+function make_manifest.command(args)
+ local repo = args.repository or cfg.rocks_dir
+
+ util.printout("Making manifest for "..repo)
+
+ if repo:match("/lib/luarocks") and not args.local_tree then
+ util.warning("This looks like a local rocks tree, but you did not pass --local-tree.")
+ end
+
+ local ok, err = writer.make_manifest(repo, deps.get_deps_mode(args), not args.local_tree)
+ if ok and not args.local_tree then
+ util.printout("Generating index.html for "..repo)
+ index.make_index(repo)
+ end
+ if args.local_tree then
+ for luaver in util.lua_versions() do
+ fs.delete(dir.path(repo, "manifest-"..luaver))
+ end
+ end
+ return ok, err
+end
+
+return make_manifest
diff --git a/src/luarocks/admin/cmd/refresh_cache.lua b/src/luarocks/admin/cmd/refresh_cache.lua
new file mode 100644
index 0000000..f8d5189
--- /dev/null
+++ b/src/luarocks/admin/cmd/refresh_cache.lua
@@ -0,0 +1,31 @@
+
+--- Module implementing the luarocks-admin "refresh_cache" command.
+local refresh_cache = {}
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local cache = require("luarocks.admin.cache")
+
+function refresh_cache.add_to_parser(parser)
+ local cmd = parser:command("refresh_cache", "Refresh local cache of a remote rocks server.", util.see_also())
+
+ cmd:option("--from", "The server to use. If not given, the default server "..
+ "set in the upload_server variable from the configuration file is used instead.")
+ :argname("<server>")
+end
+
+function refresh_cache.command(args)
+ local server, upload_server = cache.get_upload_server(args.server)
+ if not server then return nil, upload_server end
+ local download_url = cache.get_server_urls(server, upload_server)
+
+ local ok, err = cache.refresh_local_cache(download_url, cfg.upload_user, cfg.upload_password)
+ if not ok then
+ return nil, err
+ else
+ return true
+ end
+end
+
+
+return refresh_cache
diff --git a/src/luarocks/admin/cmd/remove.lua b/src/luarocks/admin/cmd/remove.lua
new file mode 100644
index 0000000..ed7644e
--- /dev/null
+++ b/src/luarocks/admin/cmd/remove.lua
@@ -0,0 +1,95 @@
+
+--- Module implementing the luarocks-admin "remove" command.
+-- Removes a rock or rockspec from a rocks server.
+local admin_remove = {}
+
+local cfg = require("luarocks.core.cfg")
+local util = require("luarocks.util")
+local dir = require("luarocks.dir")
+local writer = require("luarocks.manif.writer")
+local fs = require("luarocks.fs")
+local cache = require("luarocks.admin.cache")
+local index = require("luarocks.admin.index")
+
+function admin_remove.add_to_parser(parser)
+ local cmd = parser:command("remove", "Remove a rock or rockspec from a rocks server.", util.see_also())
+
+ cmd:argument("rock", "A local rockspec or rock file.")
+ :args("+")
+
+ cmd:option("--server", "The server to use. If not given, the default server "..
+ "set in the upload_server variable from the configuration file is used instead.")
+ cmd:flag("--no-refresh", "Do not refresh the local cache prior to "..
+ "generation of the updated manifest.")
+end
+
+local function remove_files_from_server(refresh, rockfiles, server, upload_server)
+ assert(type(refresh) == "boolean" or not refresh)
+ assert(type(rockfiles) == "table")
+ assert(type(server) == "string")
+ assert(type(upload_server) == "table" or not upload_server)
+
+ local download_url, login_url = cache.get_server_urls(server, upload_server)
+ local at = fs.current_dir()
+ local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url
+
+ local local_cache, protocol, server_path, user, password = refresh_fn(download_url, cfg.upload_user, cfg.upload_password)
+ if not local_cache then
+ return nil, protocol
+ end
+
+ local ok, err = fs.change_dir(at)
+ if not ok then return nil, err end
+
+ local nr_files = 0
+ for _, rockfile in ipairs(rockfiles) do
+ local basename = dir.base_name(rockfile)
+ local file = dir.path(local_cache, basename)
+ util.printout("Removing file "..file.."...")
+ fs.delete(file)
+ if not fs.exists(file) then
+ nr_files = nr_files + 1
+ else
+ util.printerr("Failed removing "..file)
+ end
+ end
+ if nr_files == 0 then
+ return nil, "No files removed."
+ end
+
+ local ok, err = fs.change_dir(local_cache)
+ if not ok then return nil, err end
+
+ util.printout("Updating manifest...")
+ writer.make_manifest(local_cache, "one", true)
+ util.printout("Updating index.html...")
+ index.make_index(local_cache)
+
+ if protocol == "file" then
+ local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete "..local_cache.."/ ".. server_path.."/"
+ util.printout(cmd)
+ fs.execute(cmd)
+ return true
+ end
+
+ if protocol ~= "rsync" then
+ return nil, "This command requires 'rsync', check your configuration."
+ end
+
+ local srv, path = server_path:match("([^/]+)(/.+)")
+ local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/"
+
+ util.printout(cmd)
+ fs.execute(cmd)
+
+ return true
+end
+
+function admin_remove.command(args)
+ local server, server_table = cache.get_upload_server(args.server)
+ if not server then return nil, server_table end
+ return remove_files_from_server(not args.no_refresh, args.rock, server, server_table)
+end
+
+
+return admin_remove
diff --git a/src/luarocks/admin/index.lua b/src/luarocks/admin/index.lua
new file mode 100644
index 0000000..64c8c1e
--- /dev/null
+++ b/src/luarocks/admin/index.lua
@@ -0,0 +1,185 @@
+
+--- Module which builds the index.html page to be used in rocks servers.
+local index = {}
+
+local util = require("luarocks.util")
+local fs = require("luarocks.fs")
+local vers = require("luarocks.core.vers")
+local persist = require("luarocks.persist")
+local dir = require("luarocks.dir")
+local manif = require("luarocks.manif")
+
+local ext_url_target = ' target="_blank"'
+
+local index_header = [[
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Available rocks</title>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
+<style>
+body {
+ background-color: white;
+ font-family: "bitstream vera sans", "verdana", "sans";
+ font-size: 14px;
+}
+a {
+ color: #0000c0;
+ text-decoration: none;
+}
+a.pkg {
+ color: black;
+}
+a:hover {
+ text-decoration: underline;
+}
+td.main {
+ border-style: none;
+}
+blockquote {
+ font-size: 12px;
+}
+td.package {
+ background-color: #f0f0f0;
+ vertical-align: top;
+}
+td.spacer {
+ height: 5px;
+}
+td.version {
+ background-color: #d0d0d0;
+ vertical-align: top;
+ text-align: left;
+ padding: 5px;
+ width: 100px;
+}
+p.manifest {
+ font-size: 8px;
+}
+</style>
+</head>
+<body>
+<h1>Available rocks</h1>
+<p>
+Lua modules available from this location for use with <a href="http://www.luarocks.org">LuaRocks</a>:
+</p>
+<table class="main">
+]]
+
+local index_package_begin = [[
+<td class="package">
+<p><a name="$anchor"></a><a href="#$anchor" class="pkg"><b>$package</b></a> - $summary<br/>
+</p><blockquote><p>$detailed<br/>
+$externaldependencies
+<font size="-1"><a href="$original">latest sources</a> $homepage | License: $license</font></p>
+</blockquote></a></td>
+<td class="version">
+]]
+
+local index_package_end = [[
+</td></tr>
+<tr><td colspan="2" class="spacer"></td></tr>
+]]
+
+local index_footer_begin = [[
+</table>
+<p class="manifest">
+<a href="manifest">manifest file</a>
+]]
+local index_manifest_ver = [[
+&bull; <a href="manifest-$VER">Lua $VER manifest file</a> (<a href="manifest-$VER.zip">zip</a>)
+]]
+local index_footer_end = [[
+</p>
+</body>
+</html>
+]]
+
+function index.format_external_dependencies(rockspec)
+ if rockspec.external_dependencies then
+ local deplist = {}
+ local listed_set = {}
+ local plats = nil
+ for name, desc in util.sortedpairs(rockspec.external_dependencies) do
+ if name ~= "platforms" then
+ table.insert(deplist, name:lower())
+ listed_set[name] = true
+ else
+ plats = desc
+ end
+ end
+ if plats then
+ for plat, entries in util.sortedpairs(plats) do
+ for name, desc in util.sortedpairs(entries) do
+ if not listed_set[name] then
+ table.insert(deplist, name:lower() .. " (on "..plat..")")
+ end
+ end
+ end
+ end
+ return '<p><b>External dependencies:</b> ' .. table.concat(deplist, ',&nbsp;').. '</p>'
+ else
+ return ""
+ end
+end
+
+function index.make_index(repo)
+ if not fs.is_dir(repo) then
+ return nil, "Cannot access repository at "..repo
+ end
+ local manifest = manif.load_manifest(repo)
+ local out = io.open(dir.path(repo, "index.html"), "w")
+
+ out:write(index_header)
+ for package, version_list in util.sortedpairs(manifest.repository) do
+ local latest_rockspec = nil
+ local output = index_package_begin
+ for version, data in util.sortedpairs(version_list, vers.compare_versions) do
+ local versions = {}
+ output = output..version..':&nbsp;'
+ table.sort(data, function(a,b) return a.arch < b.arch end)
+ for _, item in ipairs(data) do
+ local file
+ if item.arch == 'rockspec' then
+ file = ("%s-%s.rockspec"):format(package, version)
+ if not latest_rockspec then latest_rockspec = file end
+ else
+ file = ("%s-%s.%s.rock"):format(package, version, item.arch)
+ end
+ table.insert(versions, '<a href="'..file..'">'..item.arch..'</a>')
+ end
+ output = output .. table.concat(versions, ',&nbsp;') .. '<br/>'
+ end
+ output = output .. index_package_end
+ if latest_rockspec then
+ local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec))
+ local descript = rockspec.description or {}
+ local vars = {
+ anchor = package,
+ package = rockspec.package,
+ original = rockspec.source.url,
+ summary = descript.summary or "",
+ detailed = descript.detailed or "",
+ license = descript.license or "N/A",
+ homepage = descript.homepage and ('| <a href="'..descript.homepage..'"'..ext_url_target..'>project homepage</a>') or "",
+ externaldependencies = index.format_external_dependencies(rockspec)
+ }
+ vars.detailed = vars.detailed:gsub("\n\n", "</p><p>"):gsub("%s+", " ")
+ vars.detailed = vars.detailed:gsub("(https?://[a-zA-Z0-9%.%%-_%+%[%]=%?&/$@;:]+)", '<a href="%1"'..ext_url_target..'>%1</a>')
+ output = output:gsub("$(%w+)", vars)
+ else
+ output = output:gsub("$anchor", package)
+ output = output:gsub("$package", package)
+ output = output:gsub("$(%w+)", "")
+ end
+ out:write(output)
+ end
+ out:write(index_footer_begin)
+ for ver in util.lua_versions() do
+ out:write((index_manifest_ver:gsub("$VER", ver)))
+ end
+ out:write(index_footer_end)
+ out:close()
+end
+
+return index