diff options
author | Daniel Silverstone <dsilvers@digital-scurf.org> | 2013-05-27 17:18:50 +0100 |
---|---|---|
committer | Daniel Silverstone <dsilvers@digital-scurf.org> | 2013-05-27 17:18:50 +0100 |
commit | a3fbdec53f490f83dbaacf5f62c7d4b9a05896e5 (patch) | |
tree | f45b35c363588e9fc6cb4d02aa30a3e6df823455 | |
parent | 50e2bf82ae69390d4517f040219f2bce991253b4 (diff) | |
parent | 9d28a255afc5b75090bf24303d4ef05b6220e621 (diff) | |
download | gitano-a3fbdec53f490f83dbaacf5f62c7d4b9a05896e5.tar.gz |
Merge branch 'dsilvers/add-commands'
Reviewd-By: Richard Maw <richard.maw@gmail.com>
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | lib/gitano/admincommand.lua | 119 | ||||
-rw-r--r-- | lib/gitano/command.lua | 112 | ||||
-rw-r--r-- | lib/gitano/repocommand.lua | 89 | ||||
-rw-r--r-- | lib/gitano/repository.lua | 24 | ||||
-rw-r--r-- | skel/gitano-admin/rules/defines.lace | 9 |
6 files changed, 268 insertions, 88 deletions
@@ -30,7 +30,8 @@ MODS := gitano \ gitano.util \ gitano.actions gitano.config gitano.lace gitano.log \ gitano.markdown gitano.repository gitano.supple \ - gitano.command gitano.admincommand gitano.usercommand + gitano.command gitano.admincommand gitano.usercommand \ + gitano.repocommand SKEL_FILES := gitano-admin/rules/selfchecks.lace \ gitano-admin/rules/aschecks.lace \ diff --git a/lib/gitano/admincommand.lua b/lib/gitano/admincommand.lua index 7d62b4b..f565e96 100644 --- a/lib/gitano/admincommand.lua +++ b/lib/gitano/admincommand.lua @@ -103,6 +103,7 @@ usage: user [list] user del <username> [confirm token] user email <username> <email> user name <username> <real name> + user rename <username> <newusername> [confirm token] With no subcommand, or the subcommand 'list' the user command will show a list of all the users, along with their email addresses and @@ -113,11 +114,12 @@ With the 'del' subcommand, you can delete a user from the system. With the 'email' subcommand, you can change a user's email address. With the 'name' subcommand, you can change a user's real name. -If you try and delete a user, you will need to paste a confirmation -token which will be supplied if you try and delete the user without -it. That token is reliant on the state of the admin repository. Any -admin operations performed between the two delete attempts will -invalidate the token and you will have to retry. +If you try and delete or rename a user, you will need to paste a +confirmation token which will be supplied if you try and delete or +rename the user without it. That token is reliant on the state of +the admin repository. Any admin operations performed between the +two delete or rename attempts will invalidate the token and you will +have to retry. ]] local function builtin_user_validate(conf, _, cmdline) @@ -127,9 +129,10 @@ local function builtin_user_validate(conf, _, cmdline) if cmdline[2] ~= "list" and cmdline[2] ~= "add" and cmdline[2] ~= "del" and + cmdline[2] ~= "rename" and cmdline[2] ~= "email" and cmdline[2] ~= "name" then - log.error("user takes one of list, add, del, email or name") + log.error("user takes one of list, add, del, rename, email or name") return false end if cmdline[2] == "list" and #cmdline ~= 2 then @@ -155,6 +158,10 @@ local function builtin_user_validate(conf, _, cmdline) log.error("user name takes a username and a real name") return false end + if cmdline[2] == "rename" and (#cmdline < 4 or #cmdline > 5) then + log.error("user rename takes a username, a new username [and a token]") + return false + end return true end @@ -229,7 +236,7 @@ local function builtin_user_run(conf, _, cmdline, env) if not utab then log.fatal("Could not find user:", username) end - local token = conf.repo:generate_confirmation() + local token = conf.repo:generate_confirmation("delete " .. username) if not cmdline[4] then log.state("In order to delete", username, "you must supply the following token:") log.state(token) @@ -242,6 +249,7 @@ local function builtin_user_run(conf, _, cmdline, env) if gtab.members[username] then table.remove(gtab.members, gtab.members[username]) gtab.members[username] = nil + gtab.changed_tables() log.state("Removed", username, "from membership of", g) end end @@ -250,6 +258,42 @@ local function builtin_user_run(conf, _, cmdline, env) -- And explain what reason = "Delete user " .. username end + elseif cmdline[2] == "rename" then + local oldusername = cmdline[3] + local newusername = cmdline[4] + local utab = conf.users[oldusername] + if not utab then + log.fatal("Could not find user:", oldusername) + end + if conf.users[newusername] then + log.fatal("New username already exists:", newusername) + end + local token = conf.repo:generate_confirmation("rename " .. oldusername .. + " to " .. newusername) + if not cmdline[5] then + log.state("In order to rename", oldusername, "you must supply the following token:") + log.state(token) + elseif cmdline[5] ~= token then + log.error("Tokens do not match. Did someone else do administrative actions?") + else + -- Iterate groups and rename the user in any group it is + -- a direct member of + for g, gtab in pairs(conf.groups) do + if gtab.members[oldusername] then + gtab.members[gtab.members[oldusername]] = newusername + gtab.members[newusername] = gtab.members[oldusername] + gtab.members[oldusername] = nil + gtab.changed_tables() + log.state("Renamed:", oldusername, "to", newusername, + "in membership of group:", g) + end + end + -- Now rename the user itself + conf.users[newusername] = utab + conf.users[oldusername] = nil + -- And explain what + reason = "Rename user " .. oldusername .. " to " .. newusername + end end if reason then -- Need to try and make a config commit @@ -268,6 +312,7 @@ usage: group [list] group show <groupname> group add <groupname> <description> group del <groupname [confirm token] + group rename <groupname> <newgroupname> [confirm token] group description <groupname> <description> group adduser <groupname> <username> group deluser <groupname> <username> [confirm token] @@ -287,16 +332,16 @@ If you add a group to a group, you are stating that everyone in the sub group is to be considered a member of this group also. Removing a group undoes this effect. -To delete a group, remove a user from a group or remove a group from a -group requires a confirmation token which will be supplied to you if -missing. +To delete or rename a group, remove a user from a group or remove a +group from a group requires a confirmation token which will be +supplied to you if missing. ]] local function builtin_group_validate(conf, _, cmdline) if not cmdline[2] then cmdline[2] = "list" end - local groupsubs = util.set {"list", "show", "add", "del", + local groupsubs = util.set {"list", "show", "add", "del", "rename", "adduser", "deluser", "addgroup", "delgroup", "description"} if not groupsubs[cmdline[2]] then log.error("Unknown sub command", cmdline[2], "for group") @@ -321,6 +366,10 @@ local function builtin_group_validate(conf, _, cmdline) log.error("Del takes a group name and a confirmation token") return false end + if cmdline[2] == "rename" and (#cmdline < 4 or #cmdline > 5) then + log.error("Rename takes a group name, a new group name [and a token]") + return false + end if cmdline[2] == "description" and #cmdline < 4 then log.error("Description takes a group name and a description") return false @@ -420,13 +469,53 @@ local function builtin_group_run(conf, _, cmdline, env) if not conf.groups[g] then log.fatal("Unknown group", g) end - local token = conf.repo:generate_confirmation() + local token = conf.repo:generate_confirmation("delete group " .. g) if not cmdline[4] then log.state("In order to delete group", g, "you must supply the following token:") log.state(token) elseif cmdline[4] ~= token then log.fatal("Token does not match. Has someone else done administrative actions?") else + for gg, gtab in pairs(conf.groups) do + if gtab.subgroups[g] then + table.remove(gtab.subgroups, gtab.subgroups[g]) + gtab.subgroups[g] = nil + gtab.changed_tables() + log.state("Removed:", g, "from subgroup membership of", gg) + end + end + conf.groups[g] = nil + reason = "Delete group " .. g + end + elseif cmdline[2] == "rename" then + local g = cmdline[3] + if not conf.groups[g] then + log.fatal("Unknown group", g) + end + local newg = cmdline[4] + if conf.groups[newg] then + log.fatal("New group", newg, "already exists.") + end + local token = conf.repo:generate_confirmation("rename group " .. g .. + " to " .. newg) + if not cmdline[5] then + log.state("In order to rename group", g, "to", newg, + "you must supply the following token:") + log.state(token) + elseif cmdline[5] ~= token then + log.fatal("Token does not match. Has someone else done administrative actions?") + else + for gg, gtab in pairs(conf.groups) do + if gtab.subgroups[g] then + gtab.subgroups[gtab.subgroups[g]] = newg + gtab.subgroups[newg] = gtab.subgroups[g] + gtab.subgroups[g] = nil + gtab.changed_tables() + log.state("Renamed:", g, "to", newg, + "in subgroup membership of", gg) + end + end + conf.groups[newg] = conf.groups[g] conf.groups[g] = nil reason = "Delete group " .. g end @@ -461,7 +550,8 @@ local function builtin_group_run(conf, _, cmdline, env) if not gtab.members[u] then log.fatal("User", u, "is not a member of", g) end - local token = conf.repo:generate_confirmation() + local token = conf.repo:generate_confirmation("delete user " .. u .. + " from group " .. g) if not cmdline[5] then log.state("To delete user", u, "from group", g, "you will need this token:") log.state(token) @@ -504,7 +594,8 @@ local function builtin_group_run(conf, _, cmdline, env) if not gtab.subgroups[g2] then log.fatal("Group", g2, "is not a subgroup of", g) end - local token = conf.repo:generate_confirmation() + local token = conf.repo:generate_confirmation("delete group " .. g .. + "from group " .. g2) if not cmdline[5] then log.state("To delete group", g2, "from group", g, "you will need this token:") log.state(token) diff --git a/lib/gitano/command.lua b/lib/gitano/command.lua index da20dea..80fa554 100644 --- a/lib/gitano/command.lua +++ b/lib/gitano/command.lua @@ -9,6 +9,7 @@ local util = require 'gitano.util' local repository = require 'gitano.repository' local sp = require "luxio.subprocess" +local sio = require "luxio.simple" local cmds = {} @@ -600,16 +601,22 @@ assert(register_cmd("config", builtin_config_short, builtin_config_validate, builtin_config_prep, builtin_config_run, true, false)) -local builtin_readme_short = "View readme for a repository (if present)" +local builtin_readme_short = "Access readme for a repository" local builtin_readme_helptext = [[ -usage: readme <reponame> +usage: readme <reponame> [set|show] -Shows you the readme for the given repository. You must have read -access to the repository in order to see it. +If you do not provide a sub-command, the readme command defaults to +showing you the readme for the given repository. To view the +readme you must have read access to the repository. + +If you provide the subcommand 'set' then the readme command reads +from the input stream and updates the readme in the repository. +You must have the right to alter the project readme in order to do +this. ]] local function builtin_readme_validate(config, repo, cmdline) - if #cmdline ~= 2 then + if #cmdline < 2 then log.error("Expected repository not provided") return false end @@ -617,25 +624,45 @@ local function builtin_readme_validate(config, repo, cmdline) log.error("Repository does not exist") return false end + if #cmdline > 3 then + log.error("usage: readme <reponame> [set|show]") + return false + end + if #cmdline == 2 then + cmdline[3] = "show" + end + if cmdline[3] ~= "set" and cmdline[3] ~= "show" then + log.error("usage: readme <reponame> [set|show]") + return false + end return true end local function builtin_readme_prep(config, repo, cmdline, context) - context.operation = "read" + if cmdline[3] == "show" then + context.operation = "read" + else + context.operation = "setreadme" + end return repo:run_lace(context) end local function builtin_readme_run(config, repo, cmdline, env) - local t = repo.readme_mdwn - if t then - if not t:match("\n$") then - t = t .. "\n" - end - for l in t:gmatch("([^\n]*)\n") do - log.state(l) + if cmdline[3] == "show" then + local t = repo.readme_mdwn + if t then + if not t:match("\n$") then + t = t .. "\n" + end + for l in t:gmatch("([^\n]*)\n") do + log.state(l) + end + else + log.state("# No README.mdwn found") end else - log.state("# No README.mdwn found") + local readme = sio.stdin:read("*a") + repo:set_readme(readme) end return "exit", 0 end @@ -673,16 +700,15 @@ local function builtin_destroy_prep(config, repo, cmdline, context) end local function builtin_destroy_run(config, repo, cmdline, env) + local token = repo:generate_confirmation("destroy repo " .. repo.name) if #cmdline == 2 then -- Generate the confirmation token - local token = repo:generate_confirmation() log.state("") log.state("If you are *certain* you wish to destroy this repository") log.state("Then re-run your command with the following confirmation token:") log.state("") log.state(" ", token) else - local token = repo:generate_confirmation() if cmdline[3] ~= token then log.error("Confirmation token does not match, refusing to destroy") return "exit", 1 @@ -937,63 +963,13 @@ assert(register_cmd("ls", builtin_ls_short, builtin_ls_helptext, builtin_ls_validate, builtin_ls_prep, builtin_ls_run, false, false)) -local builtin_gc_short = "Invoke git gc on your repository" -local builtin_gc_helptext = [[ -usage: gc repo [options] - -Invoke, git gc, passing the given options, on the given repository. -You must have basic write access to the repository in order to invoke a gc. -]] - -local function builtin_gc_prep(config, repo, cmdline, context) - context.operation = "write" - return repo:run_lace(context) -end - -local builtin_count_objects_short = "Count objects in your projects" -local builtin_count_objects_helptext = [[ -usage: count-objects repo [options] - -Counts objects in your repository. - -You must have read access to the repository in order -to run count-objects. -]] - -local function builtin_count_objects_prep(config, repo, cmdline, context) - context.operation = "read" - return repo:run_lace(context) -end - -local function builtin_simple_validate(config, repo, cmdline) - if not repo or repo.is_nascent then - log.error("Unable to proceed, repository does not exist") - return false - end - return true -end - -local function builtin_simple_run(config, repo, cmdline, env) - local cmdcopy = {env=util.deep_copy(env), "git", cmdline[1]} - cmdcopy.env.GIT_DIR=repo:fs_path() - for i = 3, #cmdline do cmdcopy[#cmdcopy+1] = cmdline[i] end - local proc = sp.spawn(cmdcopy) - return proc:wait() -end - -assert(register_cmd("gc", builtin_gc_short, builtin_gc_helptext, - builtin_simple_validate, builtin_gc_prep, - builtin_simple_run, true, false)) - -assert(register_cmd("count-objects", builtin_count_objects_short, - builtin_count_objects_helptext, builtin_simple_validate, - builtin_count_objects_prep, builtin_simple_run, - true, false)) local usercmds = require 'gitano.usercommand' usercmds.register(register_cmd) local admincmds = require 'gitano.admincommand' admincmds.register(register_cmd) +local repocmds = require 'gitano.repocommand' +repocmds.register(register_cmd) return { register = register_cmd, diff --git a/lib/gitano/repocommand.lua b/lib/gitano/repocommand.lua new file mode 100644 index 0000000..4d70a8b --- /dev/null +++ b/lib/gitano/repocommand.lua @@ -0,0 +1,89 @@ +-- gitano.repocommand +-- +-- Gitano repository related commands such as gc, count-objects and fsck +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> + +local log = require 'gitano.log' +local util = require 'gitano.util' +local repository = require 'gitano.repository' + +local sp = require "luxio.subprocess" + +local builtin_gc_short = "Invoke git gc on your repository" +local builtin_gc_helptext = [[ +usage: gc repo [options] + +Invoke, git gc, passing the given options, on the given repository. +You must have basic write access to the repository in order to invoke a gc. +]] + +local function builtin_gc_prep(config, repo, cmdline, context) + context.operation = "write" + return repo:run_lace(context) +end + +local builtin_count_objects_short = "Count objects in your projects" +local builtin_count_objects_helptext = [[ +usage: count-objects repo [options] + +Counts objects in your repository. + +You must have read access to the repository in order +to run count-objects. +]] + +local function builtin_count_objects_prep(config, repo, cmdline, context) + context.operation = "read" + return repo:run_lace(context) +end + +local builtin_fsck_short = "Perform a fsck operation on a repository" +local builtin_fsck_helptext = [[ +usage: fsck repo [options] + +Runs the git fsck process in your repository. + +You must have write access to the repository in order +to run fsck. +]] + +local function builtin_fsck_prep(config, repo, cmdline, context) + context.operation = "write" + return repo:run_lace(context) +end + +local function builtin_simple_validate(config, repo, cmdline) + if not repo or repo.is_nascent then + log.error("Unable to proceed, repository does not exist") + return false + end + return true +end + +local function builtin_simple_run(config, repo, cmdline, env) + local cmdcopy = {env=util.deep_copy(env), "git", cmdline[1]} + cmdcopy.env.GIT_DIR=repo:fs_path() + for i = 3, #cmdline do cmdcopy[#cmdcopy+1] = cmdline[i] end + local proc = sp.spawn(cmdcopy) + return proc:wait() +end + +local function register_repocommand(register_cmd) + assert(register_cmd("gc", builtin_gc_short, builtin_gc_helptext, + builtin_simple_validate, builtin_gc_prep, + builtin_simple_run, true, false)) + + assert(register_cmd("count-objects", builtin_count_objects_short, + builtin_count_objects_helptext, builtin_simple_validate, + builtin_count_objects_prep, builtin_simple_run, + true, false)) + + assert(register_cmd("fsck", builtin_fsck_short, builtin_fsck_helptext, + builtin_simple_validate, builtin_fsck_prep, + builtin_simple_run, true, false)) +end + +return { + register = register_repocommand +} diff --git a/lib/gitano/repository.lua b/lib/gitano/repository.lua index f512092..371fb51 100644 --- a/lib/gitano/repository.lua +++ b/lib/gitano/repository.lua @@ -428,6 +428,18 @@ function repo_method:set_description(newdesc) return true end +function repo_method:set_readme(newreadme) + local oldreadme = self.readme_mdwn + self.readme_mdwn = newreadme + local ok, msg = self:save_admin("Changing readme") + if not ok then + self.readme_mdwn = oldreadme + return nil, msg + end + log.state("<" .. self.name .. "> Changed readme") + return true +end + function repo_method:set_head(newhead) if not newhead:match("^refs/") then newhead = "refs/heads/" .. newhead @@ -443,7 +455,7 @@ function repo_method:set_head(newhead) return true end -function repo_method:generate_confirmation() +function repo_method:generate_confirmation(notes) -- Generate a confirmation token. -- To do this, we read *ALL* refs in the repository and their -- sha1 sums. We then hash all that to generate a token @@ -454,7 +466,7 @@ function repo_method:generate_confirmation() refnames[#refnames+1] = ref end table.sort(refnames) - local str = "" + local str = notes or "" log.debug("Calculating confirmation token for", self.name) for i = 1, #refnames do local ref, sha = refnames[i], refs[refnames[i]] @@ -571,6 +583,14 @@ function repo_method:save_admin(reason, username) flat_tree["project.conf"] = conf_blob + if self.readme_mdwn then + local readme_blob, msg = gall.object.create(self.git, "blob", self.readme_mdwn) + if not readme_blob then + return nil, msg + end + flat_tree["README.mdwn"] = readme_blob + end + local tree, msg = gall.tree.create(self.git, flat_tree) if not tree then return nil, msg diff --git a/skel/gitano-admin/rules/defines.lace b/skel/gitano-admin/rules/defines.lace index 0d7882a..e72f598 100644 --- a/skel/gitano-admin/rules/defines.lace +++ b/skel/gitano-admin/rules/defines.lace @@ -19,19 +19,21 @@ define op_userdel operation userdel define op_userlist operation userlist define op_useremail operation useremail define op_username operation username -define op_user anyof op_userlist op_useradd op_userdel op_useremail op_username +define op_userrename operation userrename +define op_user anyof op_userlist op_useradd op_userdel op_useremail op_username op_userrename ## Groups define op_grouplist operation grouplist define op_groupshow operation groupshow define op_groupadd operation groupadd define op_groupdel operation groupdel +define op_grouprename operation grouprename define op_groupadduser operation groupadduser define op_groupdeluser operation groupdeluser define op_groupaddgroup operation groupaddgroup define op_groupdelgroup operation groupdelgroup define op_groupdescription operation groupdescription -define op_group anyof op_grouplist op_groupshow op_groupadd op_groupdel op_groupadduser op_groupdeluser op_groupaddgroup op_groupdelgroup op_groupdescription +define op_group anyof op_grouplist op_groupshow op_groupadd op_groupdel op_groupadduser op_groupdeluser op_groupaddgroup op_groupdelgroup op_groupdescription op_grouprename ## Graveyard define op_graveyardlist operation graveyardlist @@ -53,7 +55,8 @@ define op_destroyrepo operation destroyrepo define op_config_show operation config_show define op_config_set operation config_set define op_config_del operation config_del -define op_is_config anyof op_config_show op_config_set op_config_del +define op_set_readme operation setreadme +define op_is_config anyof op_config_show op_config_set op_config_del op_set_readme # Reference update related operations define op_createref operation createref |