From 1788d0bb8c08e1ff7e671f20a023b3dcc4f80206 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 26 May 2014 19:40:58 -0300 Subject: Fix chmod argument order. --- src/luarocks/upload/api.lua | 182 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/luarocks/upload/api.lua diff --git a/src/luarocks/upload/api.lua b/src/luarocks/upload/api.lua new file mode 100644 index 00000000..818d293e --- /dev/null +++ b/src/luarocks/upload/api.lua @@ -0,0 +1,182 @@ + +local api = {} + +local cfg = require("luarocks.cfg") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local persist = require("luarocks.persist") + +local Api = {} + +local function upload_config_file() + local _, _, home_conf, home_ok = cfg.which_config() + if not home_conf then + return nil + end + return (home_conf:gsub("/[^/]+$", "/upload_config.lua")) +end + +function Api:load_config() + local upload_conf = upload_config_file() + print(upload_conf) + if not upload_conf then return nil end + local cfg, err = persist.load_into_table(upload_conf) + return cfg +end + +function Api:save_config() + -- Test configuration before saving it. + local res, err = self:raw_method("status") + if not res then + return nil, err + end + if res.errors then + util.printerr("Server says: " .. tostring(res.errors[1])) + return + end + local upload_conf = upload_config_file() + if not upload_conf then return nil end + persist.save_from_table(upload_conf, self.config) + fs.chmod(upload_conf, "0600") +end + +function Api:check_version() + if not self._server_tool_version then + local tool_version = cfg.upload.tool_version + local res, err = self:request("http://" .. tostring(self.config.server) .. "/api/tool_version", { + current = tool_version + }) + if not res then + return nil, err + end + if not res.version then + return nil, "failed to fetch tool version" + end + self._server_tool_version = res.version + if res.force_update then + return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks." + end + if res.version ~= tool_version then + util.printerr("Warning: Your LuaRocks is out of date, consider upgrading.") + end + end + return true +end + +function Api:method(...) + local res, err = self:raw_method(...) + if not res then + return nil, err + end + if res.errors then + if res.errors[1] == "Invalid key" then + return nil, res.errors[1] .. " (use the --api-key flag to change)" + end + local msg = table.concat(res.errors, ", ") + return nil, "API Failed: " .. msg + end + return res +end + +function Api:raw_method(path, ...) + self:check_version() + local url = "http://" .. tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. tostring(path) + return self:request(url, ...) +end + +local function encode_query_string(t, sep) + local url = require("socket.url") + if sep == nil then + sep = "&" + end + local i = 0 + local buf = { } + for k, v in pairs(t) do + if type(k) == "number" and type(v) == "table" then + k, v = v[1], v[2] + end + buf[i + 1] = url.escape(k) + buf[i + 2] = "=" + buf[i + 3] = url.escape(v) + buf[i + 4] = sep + i = i + 4 + end + buf[i] = nil + return table.concat(buf) +end + +-- An ode to the multitude of JSON libraries out there... +local function require_json() + for _, lib in ipairs({ "cjson", "dkjson", "json" }) do + local json_ok, json = pcall(require, lib) + if json_ok then + return json_ok, json + end + end + return nil +end + +function Api:request(url, params, post_params) + local http_ok, http = pcall(require, "socket.http") + local ltn12_ok, ltn12 = pcall(require, "ltn12") + local json_ok, json = require_json() + if not http_ok then return nil, "LuaSocket is required for this command." end + if not json_ok then return nil, "A JSON library is required for this command." end + + if not self.config.key then + return nil, "Must have API key before performing any actions." + end + local body + local headers = {} + if params and next(params) then + url = url .. ("?" .. encode_query_string(params)) + end + if post_params then + local multipart = require("luarocks.upload.multipart") + local boundary + body, boundary = multipart.encode(post_params) + headers["Content-length"] = #body + headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) + end + local method = post_params and "POST" or "GET" + if self.debug then + util.printout("[" .. tostring(method) .. "] " .. tostring(url) .. " ... ") + end + local out = {} + local _, status = http.request({ + url = url, + headers = headers, + method = method, + sink = ltn12.sink.table(out), + source = body and ltn12.source.string(body) + }) + if self.debug then + util.printout(tostring(status)) + end + if status ~= 200 then + return nil, "API returned " .. tostring(status) .. " - " .. tostring(url) + end + return json.decode(table.concat(out)) +end + +function api.new(flags, name) + local self = {} + setmetatable(self, { __index = Api }) + self.config = self:load_config() or {} + self.config.server = flags["server"] or self.config.server or cfg.upload.server + self.config.version = self.config.version or cfg.upload.version + self.config.key = flags["api-key"] or self.config.key + self.debug = flags["debug"] + if not self.config.key then + return nil, "You need an API key to upload rocks.\n" .. + "Navigate to http://"..self.config.server.."/settings to get a key\n" .. + "and then pass it through the --api-key= flag." + end + if flags["api-key"] then + self:save_config() + end + return self +end + +return api + -- cgit v1.2.3-55-g6feb From dc9d2daf9deaf867b6caaf88ab1bb088e2b4622d Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 26 May 2014 19:41:17 -0300 Subject: Transition to MoonRocks. Add `luarocks upload` command for uploading into MoonRocks. --- Makefile | 3 +- rockspec | 2 +- src/bin/luarocks | 1 + src/luarocks/cfg.lua | 15 +++--- src/luarocks/pack.lua | 4 +- src/luarocks/upload.lua | 88 +++++++++++++++++++++++++++++++ src/luarocks/upload/multipart.lua | 107 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 src/luarocks/upload.lua create mode 100644 src/luarocks/upload/multipart.lua diff --git a/Makefile b/Makefile index 46943312..1f9d0e2d 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,8 @@ remove.lua fs.lua manif.lua add.lua deps.lua build.lua search.lua show.lua \ manif_core.lua fetch.lua unpack.lua validate.lua cfg.lua download.lua \ help.lua util.lua index.lua cache.lua refresh_cache.lua loader.lua \ admin_remove.lua fetch/hg.lua fetch/git_file.lua new_version.lua lint.lua \ -purge.lua path.lua path_cmd.lua write_rockspec.lua doc.lua +purge.lua path.lua path_cmd.lua write_rockspec.lua doc.lua upload.lua \ +upload/api.lua upload/multipart.lua fetch/git_http.lua CONFIG_FILE = $(SYSCONFDIR)/config-$(LUA_VERSION).lua diff --git a/rockspec b/rockspec index 0320a3a2..16ac635c 100644 --- a/rockspec +++ b/rockspec @@ -1,5 +1,5 @@ package = "LuaRocks" -local VER = "2.1.2" +local VER = "2.2.0beta1" local REV = "1" version = VER.."-"..REV diff --git a/src/bin/luarocks b/src/bin/luarocks index b85fbc7c..3c9e1b74 100755 --- a/src/bin/luarocks +++ b/src/bin/luarocks @@ -23,6 +23,7 @@ commands = { write_rockspec = "luarocks.write_rockspec", purge = "luarocks.purge", doc = "luarocks.doc", + upload = "luarocks.upload", } command_line.run_command(...) diff --git a/src/luarocks/cfg.lua b/src/luarocks/cfg.lua index 59a17754..a96d610f 100644 --- a/src/luarocks/cfg.lua +++ b/src/luarocks/cfg.lua @@ -32,7 +32,7 @@ end cfg.site_config = site_config -cfg.program_version = "2.1.2" +cfg.program_version = "2.2.0beta1" cfg.major_version = cfg.program_version:match("([^.]%.[^.])") local persist = require("luarocks.persist") @@ -214,14 +214,17 @@ local defaults = { rocks_servers = { { - "http://www.luarocks.org/repositories/rocks", - "http://luarocks.giga.puc-rio.br/", - "http://luafr.org/luarocks/rocks", - "http://liblua.so/luarocks/repositories/rocks", - "http://luarocks.logiceditor.com/rocks", + "http://rocks.moonscript.org", + "https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/", } }, disabled_servers = {}, + + upload = { + server = "rocks.moonscript.org", + tool_version = "0.0.1", + api_version = "1", + }, lua_extension = "lua", lua_interpreter = site_config.LUA_INTERPRETER or "lua", diff --git a/src/luarocks/pack.lua b/src/luarocks/pack.lua index d8b3ad9b..c73d66ab 100644 --- a/src/luarocks/pack.lua +++ b/src/luarocks/pack.lua @@ -33,7 +33,7 @@ argument. -- @param rockspec_file string: An URL or pathname for a rockspec file. -- @return string or (nil, string): The filename of the resulting -- .src.rock file; or nil and an error message. -local function pack_source_rock(rockspec_file) +function pack.pack_source_rock(rockspec_file) assert(type(rockspec_file) == "string") local rockspec, err = fetch.load_rockspec(rockspec_file) @@ -201,7 +201,7 @@ function pack.run(...) local file, err if arg:match(".*%.rockspec") then - file, err = pack_source_rock(arg) + file, err = pack.pack_source_rock(arg) else file, err = do_pack_binary_rock(arg, version) end diff --git a/src/luarocks/upload.lua b/src/luarocks/upload.lua new file mode 100644 index 00000000..018e702d --- /dev/null +++ b/src/luarocks/upload.lua @@ -0,0 +1,88 @@ + +local upload = {} + +local util = require("luarocks.util") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local Api = require("luarocks.upload.api") + +upload.help_summary = "Upload a rockspec to the public rocks repository." +upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " +upload.help = [[ + Pack a source rock file (.src.rock extension), + upload rockspec and source rock to server. +--skip-pack Do not pack and send source rock. +--api-key= Give it an API key. It will be stored for subsequent uses. +--force Replace existing rockspec if the same revision of + a module already exists. This should be used only + in case of upload mistakes: when updating a rockspec, + increment the revision number instead. +]] + +function upload.run(...) + local flags, fname = util.parse_flags(...) + if not fname then + return nil, "Missing rockspec. "..util.see_help("upload") + end + + local api, err = Api.new(flags) + if not api then + return nil, err + end + + local rockspec, err, errcode = fetch.load_rockspec(fname) + if err then + return nil, err, errcode + end + + util.printout("Sending " .. tostring(fname) .. " ...") + local res, err = api:method("check_rockspec", { + package = rockspec.package, + version = rockspec.version + }) + if not res then return nil, err end + + if not res.module then + util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") + end + if res.version and not flags["force"] then + return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") + end + + local rock_fname + if not flags["skip-pack"] then + util.printout("Packing " .. tostring(rockspec.package)) + rock_fname, err = pack.pack_source_rock(fname) + if not rock_fname then + return nil, err + end + end + + local multipart = require("luarocks.upload.multipart") + + res, err = api:method("upload", nil, { + rockspec_file = multipart.new_file(fname) + }) + if not res then return nil, err end + + if res.is_new and #res.manifests == 0 then + util.printerr("Warning: module not added to root manifest due to name taken.") + end + + local module_url = res.module_url + + if rock_fname then + util.printout(("Sending " .. tostring(rock_fname) .. " ...")) + res, err = api:method("upload_rock/" .. tostring(res.version.id), nil, { + rock_file = multipart.new_file(rock_fname) + }) + if not res then return nil, err end + end + + util.printout() + util.printout("Done: " .. tostring(module_url)) + util.printout() + return true +end + +return upload diff --git a/src/luarocks/upload/multipart.lua b/src/luarocks/upload/multipart.lua new file mode 100644 index 00000000..95afe1b3 --- /dev/null +++ b/src/luarocks/upload/multipart.lua @@ -0,0 +1,107 @@ + +local multipart = {} + +local url = require("socket.url") + +local File = {} + +local unpack = unpack or table.unpack + +math.randomseed(os.time()) + +function File:mime() + if not self.mimetype then + local mimetypes_ok, mimetypes = pcall(require, "mimetypes") + if mimetypes_ok then + self.mimetype = mimetypes.guess(self.fname) + else + self.mimetype = "application/octet-stream" + end + end + return self.mimetype +end + +function File:content() + local fd = io.open(self.fname) + if not fd then + return nil, "Failed to open file: "..self.fname + end + local data = fd:read("*a") + fd:close() + return data +end + +local function rand_string(len) + local shuffled = {} + for i = 1, len do + local r = math.random(97, 122) + if math.random() >= 0.5 then + r = r - 32 + end + shuffled[i] = r + end + return string.char(unpack(shuffled)) +end + +-- multipart encodes params +-- returns encoded string,boundary +-- params is an a table of tuple tables: +-- params = { +-- {key1, value2}, +-- {key2, value2}, +-- key3: value3 +-- } +function multipart.encode(params) + local tuples = { } + for i = 1, #params do + tuples[i] = params[i] + end + for k,v in pairs(params) do + if type(k) == "string" then + table.insert(tuples, {k, v}) + end + end + local chunks = {} + for _, tuple in ipairs(tuples) do + local k,v = unpack(tuple) + k = url.escape(k) + local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } + local content + if type(v) == "table" and v.__class == File then + buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*/", "") .. '"') + table.insert(buffer, "Content-type: " .. v:mime()) + content = v:content() + else + content = v + end + table.insert(buffer, "") + table.insert(buffer, content) + table.insert(chunks, table.concat(buffer, "\r\n")) + end + local boundary + while not boundary do + boundary = "Boundary" .. rand_string(16) + for _, chunk in ipairs(chunks) do + if chunk:find(boundary) then + boundary = nil + break + end + end + end + local inner = "\r\n--" .. boundary .. "\r\n" + return table.concat({ "--", boundary, "\r\n", + table.concat(chunks, inner), + "\r\n", "--", boundary, "--", "\r\n" }), boundary +end + +function multipart.new_file(fname, mime) + local self = {} + setmetatable(self, { __index = File }) + self.__class = File + self.fname = fname + self.mimetype = mime + return self +end + +return multipart + -- cgit v1.2.3-55-g6feb