summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2013-05-27 17:18:50 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2013-05-27 17:18:50 +0100
commita3fbdec53f490f83dbaacf5f62c7d4b9a05896e5 (patch)
treef45b35c363588e9fc6cb4d02aa30a3e6df823455
parent50e2bf82ae69390d4517f040219f2bce991253b4 (diff)
parent9d28a255afc5b75090bf24303d4ef05b6220e621 (diff)
downloadgitano-a3fbdec53f490f83dbaacf5f62c7d4b9a05896e5.tar.gz
Merge branch 'dsilvers/add-commands'
Reviewd-By: Richard Maw <richard.maw@gmail.com>
-rw-r--r--Makefile3
-rw-r--r--lib/gitano/admincommand.lua119
-rw-r--r--lib/gitano/command.lua112
-rw-r--r--lib/gitano/repocommand.lua89
-rw-r--r--lib/gitano/repository.lua24
-rw-r--r--skel/gitano-admin/rules/defines.lace9
6 files changed, 268 insertions, 88 deletions
diff --git a/Makefile b/Makefile
index 5e0249c..0bb007c 100644
--- a/Makefile
+++ b/Makefile
@@ -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