Compare commits
No commits in common. "master" and "v1.5.6" have entirely different histories.
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
conf/* crlf=input
|
||||
src/* crlf=input
|
||||
hooks/common/* crlf=input
|
||||
hooks/gitolite-admin/* crlf=input
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*.tar
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
conf/VERSION
|
86
CHANGELOG
86
CHANGELOG
|
@ -1,86 +0,0 @@
|
|||
2012-12-29 v3.3 bug fix: gl-perms propagation to slaves broke sometime
|
||||
after v3.2 (so if you're only picking up tagged releases
|
||||
you're OK)
|
||||
|
||||
the "D" command now allows rm/unlock to be totally
|
||||
disabled
|
||||
|
||||
new trigger: update-gitweb-daemon-from-options; another
|
||||
way to update gitweb and daemon access lists
|
||||
|
||||
new 'create' command for explicit wild repo creation, and
|
||||
new AutoCreate trigger to control auto-creation
|
||||
|
||||
allow simple macros in conf file
|
||||
|
||||
2012-11-14 v3.2 major efficiency boost for large setups
|
||||
|
||||
optional support for multi-line pubkeys; see
|
||||
src/triggers/post-compile/ssh-authkeys-split
|
||||
|
||||
bug fix for not creating gl-conf when repo para has only
|
||||
config lines and no access rules
|
||||
|
||||
new 'bg' trigger command to put long jobs started from a
|
||||
trigger into background
|
||||
|
||||
%GL_REPO and %GL_CREATOR now work for 'option's also
|
||||
|
||||
test suite now much more BSD friendly
|
||||
|
||||
2012-10-05 v3.1 (security) fix path traversal on wild repos
|
||||
|
||||
new %GL_CREATOR variable for git-config lines
|
||||
|
||||
rsync command to create and send bundles automagically
|
||||
|
||||
migrated 'who-pushed'
|
||||
|
||||
logical expressions on refexes!!!
|
||||
|
||||
2012-06-27 v3.04 documentation graduated and moved out of parents house :)
|
||||
|
||||
new trigger for 'repo specific umask'
|
||||
|
||||
new 'list-dangling-repos' command
|
||||
|
||||
new LOCAL_CODE rc var; allow admin specified programs to
|
||||
override system-installed ones
|
||||
|
||||
new 'upstream' trigger-cum-command to maintain local
|
||||
copies of external repos
|
||||
|
||||
new 'sudo' command
|
||||
|
||||
minor backward compat breakage in 'gitolite query-rc'
|
||||
|
||||
'perms' command can now create repo if needed
|
||||
|
||||
migrated 'symbolic-ref' command
|
||||
|
||||
'gitolite setup --hooks-only'
|
||||
|
||||
2012-05-23 v3.03 fix major bug that allowed an admin to get a shell
|
||||
|
||||
2012-05-20 v3.02 packaging instructions fixed up and smoke tested
|
||||
|
||||
make it easier to give some users a full shell
|
||||
|
||||
allow aliasing a repo to another name
|
||||
|
||||
simulate POST_CREATE for new normal (non-wild) repos
|
||||
|
||||
(just for kicks) a VREF that allows for voting on changes
|
||||
to a branch
|
||||
|
||||
bug fix: smart http was not running PRE_ and POST_GIT
|
||||
triggers
|
||||
|
||||
htpasswd migrated
|
||||
|
||||
2012-04-29 v3.01 mostly BSD and Solaris compat
|
||||
also fork command added
|
||||
|
||||
2012-04-18 v3.0 first release to "master"
|
||||
This is a compete rewrite of gitolite; please see
|
||||
documentation before upgrading.
|
21
Makefile
Normal file
21
Makefile
Normal file
|
@ -0,0 +1,21 @@
|
|||
# this is a simple wrapper around "git archive" using make
|
||||
|
||||
# "make [refname].tar" produces a tar of refname, then adds a file containing
|
||||
# the "git describe" output for that refname to the tar. This lets you say
|
||||
# "cat .GITOLITE-VERSION" to find out which ref produced this tar
|
||||
|
||||
# Note: I'm not sure if that "-r" is a GNU tar extension...
|
||||
|
||||
branch := $(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
$(branch): $(branch).tar
|
||||
|
||||
.GITOLITE-VERSION:
|
||||
@touch conf/VERSION
|
||||
|
||||
%.tar: .GITOLITE-VERSION
|
||||
git describe --tags --long $* > conf/VERSION
|
||||
git archive $* > $@
|
||||
tar -r -f $@ conf/VERSION
|
||||
rm conf/VERSION
|
||||
cp -v $@ /tmp
|
359
README.md
359
README.md
|
@ -1,359 +0,0 @@
|
|||
Github-users: click the 'wiki' link before sending me anything via github.
|
||||
|
||||
Existing users: this is gitolite v3.x. If you are upgrading from v2.x this
|
||||
file will not suffice; you *must* check the online docs (see below for URL).
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
This file contains BASIC DOCUMENTATION ONLY.
|
||||
|
||||
* It is suitable for a fresh, ssh-based, installation of gitolite and basic
|
||||
usage of its most important features.
|
||||
* It is NOT meant to be exhaustive or detailed.
|
||||
|
||||
The COMPLETE DOCUMENTATION is at:
|
||||
|
||||
http://sitaramc.github.com/gitolite/master-toc.html
|
||||
|
||||
Please go there for what/why/how, concepts, background, troubleshooting, more
|
||||
details on what is covered here, or advanced features not covered here.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
BASIC DOCUMENTATION FOR GITOLITE
|
||||
================================
|
||||
|
||||
This file contains the following sections:
|
||||
|
||||
* INSTALLATION AND SETUP
|
||||
* ADDING USERS AND REPOS
|
||||
* HELP FOR YOUR USERS
|
||||
* BASIC SYNTAX
|
||||
* ACCESS RULES
|
||||
* GROUPS
|
||||
* COMMANDS
|
||||
* THE 'rc' FILE
|
||||
* GIT-CONFIG
|
||||
* GIT-DAEMON
|
||||
* GITWEB
|
||||
* CONTACT AND SUPPORT
|
||||
* LICENSE
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
INSTALLATION AND SETUP
|
||||
----------------------
|
||||
|
||||
Server requirements:
|
||||
|
||||
* any unix system
|
||||
* sh
|
||||
* git 1.6.6+
|
||||
* perl 5.8.8+
|
||||
* openssh 5.0+
|
||||
* a dedicated userid to host the repos (in this document, we assume it
|
||||
is 'git'), with shell access ONLY by 'su - git' from some other userid
|
||||
on the same server.
|
||||
|
||||
Steps to install:
|
||||
|
||||
* login as 'git' as described above
|
||||
* make sure ~/.ssh/authorized_keys is empty or non-existent
|
||||
* make sure your ssh public key from your workstation is available at $HOME/YourName.pub
|
||||
* run the following commands:
|
||||
|
||||
git clone git://github.com/sitaramc/gitolite
|
||||
mkdir -p $HOME/bin
|
||||
gitolite/install -to $HOME/bin
|
||||
gitolite setup -pk YourName.pub
|
||||
|
||||
If the last command doesn't run perhaps 'bin' in not in your 'PATH'.
|
||||
You can either add it, or just run:
|
||||
|
||||
$HOME/bin/gitolite setup -pk YourName.pub
|
||||
|
||||
|
||||
ADDING USERS AND REPOS
|
||||
----------------------
|
||||
|
||||
Do NOT add new repos or users manually on the server. Gitolite users,
|
||||
repos, and access rules are maintained by making changes to a special repo
|
||||
called 'gitolite-admin' and pushing those changes to the server.
|
||||
|
||||
----
|
||||
|
||||
To administer your gitolite installation, start by doing this on your
|
||||
workstation (if you have not already done so):
|
||||
|
||||
git clone git@host:gitolite-admin
|
||||
|
||||
**NOTE**: if you are asked for a password, something has gone wrong.
|
||||
|
||||
Now if you 'cd gitolite-admin', you will see two subdirectories in it:
|
||||
'conf' and 'keydir'.
|
||||
|
||||
To add new users alice, bob, and carol, obtain their public keys and add
|
||||
them to 'keydir' as alice.pub, bob.pub, and carol.pub respectively.
|
||||
|
||||
To add a new repo 'foo' and give different levels of access to these
|
||||
users, edit the file 'conf/gitolite.conf' and add lines like this:
|
||||
|
||||
repo foo
|
||||
RW+ = alice
|
||||
RW = bob
|
||||
R = carol
|
||||
|
||||
See the 'ACCESS RULES' section later for more details.
|
||||
|
||||
Once you have made these changes, do something like this:
|
||||
|
||||
git add conf
|
||||
git add keydir
|
||||
git commit -m 'added foo, gave access to alice, bob, carol'
|
||||
git push
|
||||
|
||||
When the push completes, gitolite will add the new users to
|
||||
~/.ssh/authorized_keys on the server, as well as create a new, empty, repo
|
||||
called 'foo'.
|
||||
|
||||
|
||||
HELP FOR YOUR USERS
|
||||
-------------------
|
||||
|
||||
Once a user has sent you their public key and you have added them as
|
||||
specified above and given them access, you have to tell them what URL to
|
||||
access their repos at. This is usually 'git clone git@host:reponame'; see
|
||||
man git-clone for other forms.
|
||||
|
||||
**NOTE**: again, if they are asked for a password, something is wrong.
|
||||
|
||||
If they need to know what repos they have access to, they just have to run
|
||||
'ssh git@host info'; see 'COMMANDS' section later for more on this.
|
||||
|
||||
|
||||
BASIC SYNTAX
|
||||
------------
|
||||
|
||||
The basic syntax of the conf file is very simple.
|
||||
|
||||
* Everything is space separated; there are no commas, semicolons, etc.,
|
||||
in the syntax.
|
||||
* Comments are in the usual perl/shell style.
|
||||
* User and repo names are as simple as possible; they must start with an
|
||||
alphanumeric, but after that they can also contain '.', '_', or '-'.
|
||||
|
||||
Usernames can optionally be followed by an '@' and a domainname
|
||||
containing at least one '.'; this allows you to use an email address
|
||||
as someone's username.
|
||||
|
||||
Reponames can contain '/' characters; this allows you to put your
|
||||
repos in a tree-structure for convenience.
|
||||
* There are no continuation lines.
|
||||
|
||||
|
||||
ACCESS RULES
|
||||
------------
|
||||
|
||||
This section is mostly 'by example'.
|
||||
|
||||
Gitolite's access rules are very powerful. The simplest use was already
|
||||
shown above. Here is a slightly more detailed example:
|
||||
|
||||
repo foo
|
||||
RW+ = alice
|
||||
- master = bob
|
||||
- refs/tags/v[0-9] = bob
|
||||
RW = bob
|
||||
RW refs/tags/v[0-9] = carol
|
||||
R = dave
|
||||
|
||||
For clones and fetches, as long as the user is listed with an R, RW
|
||||
or RW+ in at least one rule, he is allowed to read the repo.
|
||||
|
||||
For pushes, rules are processed in sequence until a rule is found
|
||||
where the user, the permission (see note 1), and the refex (note 2)
|
||||
*all* match. At that point, if the permission on the matched rule
|
||||
was '-', the push is denied, otherwise it is allowed. If no rule
|
||||
matches, the push is denied.
|
||||
|
||||
Note 1: permission matching:
|
||||
|
||||
* a permission of RW matches only a fast-forward push or create
|
||||
* a permission of RW+ matches any type of push
|
||||
* a permission of '-' matches any type of push
|
||||
|
||||
Note 2: refex matching:
|
||||
(refex = optional regex to match the ref being pushed)
|
||||
|
||||
* an empty refex is treated as 'refs/.*'
|
||||
* a refex that does not start with 'refs/' is prefixed with 'refs/heads/'
|
||||
* finally, a '^' is prefixed
|
||||
* the ref being pushed is matched against this resulting refex
|
||||
|
||||
With all that background, here's what the example rules say:
|
||||
|
||||
* alice can do anything to any branch or tag -- create, push, delete, rewind/overwrite etc.
|
||||
* bob can create or fast-forward push any branch whose name does
|
||||
not start with 'master' and create any tag whose name does not
|
||||
start with 'v'+digit.
|
||||
* carol can create tags whose names start with 'v'+digit.
|
||||
* dave can clone/fetch.
|
||||
|
||||
|
||||
GROUPS
|
||||
------
|
||||
|
||||
Gitolite allows you to group users or repos for convenience. Here's an
|
||||
example that creates two groups of users:
|
||||
|
||||
@staff = alice bob carol
|
||||
@interns = ashok
|
||||
|
||||
repo secret
|
||||
RW = @staff
|
||||
|
||||
repo foss
|
||||
RW+ = @staff
|
||||
RW = @interns
|
||||
|
||||
Group lists accumulate. The following two lines have the same effect as
|
||||
the earlier definition of @staff above:
|
||||
|
||||
@staff = alice bob
|
||||
@staff = carol
|
||||
|
||||
You can also use group names in other group names:
|
||||
|
||||
@all-devs = @staff @interns
|
||||
|
||||
Finally, @all is a special group name that is often convenient to use if
|
||||
you really mean 'all repos' or 'all users'.
|
||||
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|
||||
Users can run certain commands remotely, using ssh. For example:
|
||||
|
||||
ssh git@host help
|
||||
|
||||
prints a list of available commands.
|
||||
|
||||
The most commonly used command is 'info'. All commands respond to a
|
||||
single argument of '-h' with suitable information.
|
||||
|
||||
If you have shell on the server, you have a lot more commands available to
|
||||
you; try running 'gitolite help'.
|
||||
|
||||
|
||||
THE 'rc' FILE
|
||||
--------------
|
||||
|
||||
Some of the instructions below may require you to edit the rc file
|
||||
(~/.gitolite.rc on the server).
|
||||
|
||||
The rc file is perl code, but you do NOT need to know perl to edit it.
|
||||
Just mind the commas, use single quotes unless you know what you're doing,
|
||||
and make sure the brackets and braces stay matched up.
|
||||
|
||||
|
||||
GIT-CONFIG
|
||||
----------
|
||||
|
||||
Gitolite lets you set git-config values for individual repos without
|
||||
having to log on to the server and run 'git config' commands:
|
||||
|
||||
repo foo
|
||||
config hooks.mailinglist = foo-commits@example.tld
|
||||
config hooks.emailprefix = '[foo] '
|
||||
config foo.bar = ''
|
||||
config foo.baz =
|
||||
|
||||
**WARNING**
|
||||
|
||||
The last syntax shown above is the *only* way to *delete* a config
|
||||
variable once you have added it. Merely removing it from the conf
|
||||
file will *not* delete it from the repo.git/config file.
|
||||
|
||||
**SECURITY NOTE**
|
||||
|
||||
Some git-config keys allow arbitrary code to be run on the server.
|
||||
|
||||
If all of your gitolite admins already have shell access to the server
|
||||
account hosting it, you can edit the rc file (~/.gitolite.rc) on the
|
||||
server, and change the GIT_CONFIG_KEYS line to look like this:
|
||||
|
||||
GIT_CONFIG_KEYS => '.*',
|
||||
|
||||
Otherwise, give it a space-separated list of regular expressions that
|
||||
define what git-config keys are allowed. For example, this one allows
|
||||
only variables whose names start with 'gitweb' or with 'gc' to be
|
||||
defined:
|
||||
|
||||
GIT_CONFIG_KEYS => 'gitweb\..* gc\..*',
|
||||
|
||||
|
||||
GIT-DAEMON
|
||||
----------
|
||||
|
||||
Gitolite creates the 'git-daemon-export-ok' file for any repo that is
|
||||
readable by a special user called 'daemon', like so:
|
||||
|
||||
repo foo
|
||||
R = daemon
|
||||
|
||||
|
||||
GITWEB
|
||||
------
|
||||
|
||||
Any repo that is readable by a special user called 'gitweb' will be added
|
||||
to the projects.list file.
|
||||
|
||||
repo foo
|
||||
R = gitweb
|
||||
|
||||
Or you can set one or more of the following config variables instead:
|
||||
|
||||
repo foo
|
||||
config gitweb.owner = some person's name
|
||||
config gitweb.description = some description
|
||||
config gitweb.category = some category
|
||||
|
||||
**NOTE**
|
||||
|
||||
You will probably need to change the UMASK in the rc file from the
|
||||
default (0077) to 0027 and add whatever user your gitweb is running as
|
||||
to the 'git' group. After that, you need to run a one-time 'chmod -R'
|
||||
on the already created files and directories.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
CONTACT AND SUPPORT
|
||||
-------------------
|
||||
|
||||
Mailing list for support and general discussion:
|
||||
gitolite@googlegroups.com
|
||||
subscribe address: gitolite+subscribe@googlegroups.com
|
||||
|
||||
Mailing list for announcements and notices:
|
||||
subscribe address: gitolite-announce+subscribe@googlegroups.com
|
||||
|
||||
IRC: #git and #gitolite on freenode. Note that I live in India (UTC+0530
|
||||
time zone).
|
||||
|
||||
Author: sitaramc@gmail.com, but please DO NOT use this for general support
|
||||
questions. Subscribe to the list and ask there instead.
|
||||
|
||||
|
||||
LICENSE
|
||||
-------
|
||||
|
||||
The gitolite *code* is released under GPL v2. See COPYING for details.
|
||||
|
||||
This documentation, which is part of the source code repository, is
|
||||
provided under a Creative Commons Attribution-ShareAlike 3.0 Unported
|
||||
License -- see http://creativecommons.org/licenses/by-sa/3.0/
|
197
README.mkd
Normal file
197
README.mkd
Normal file
|
@ -0,0 +1,197 @@
|
|||
<a name="start"></a>
|
||||
|
||||
# gitolite
|
||||
|
||||
Gitolite is an access control layer on top of git, which allows access control
|
||||
down to the branch level, including specifying who can and cannot *rewind* a
|
||||
given branch.
|
||||
|
||||
Gitolite comes with a **huge** amount of documentation. If you're absolutely
|
||||
new, the suggested reading order is this:
|
||||
|
||||
* the README (this document) for a quick intro
|
||||
* the [INSTALL][install] document
|
||||
* the [ADMIN][admin] document
|
||||
|
||||
If you run into trouble start [here](#support). If you're migrating from
|
||||
gitosis, read [this][migr].
|
||||
|
||||
And [here][who]'s some information on some of the projects and people using
|
||||
gitolite (and who, in turn, have helped shape its features).
|
||||
|
||||
Once you've installed it and started using it, you'll want to explore some of
|
||||
the more powerful features. All the documentation is available in the source
|
||||
repo as well as [online][docs]. All the longer documents have tables of
|
||||
contents, so you can quickly get a feel for what is covered right at the top.
|
||||
|
||||
----
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_what">what</a>
|
||||
* <a href="#_why">why</a>
|
||||
* <a href="#_main_features">main features</a>
|
||||
* <a href="#_support">support</a>
|
||||
* <a href="#_security">security</a>
|
||||
* <a href="#_contact_and_license">contact and license</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_what"></a>
|
||||
|
||||
### what
|
||||
|
||||
Gitolite lets you use a single user on a server to host many git repositories
|
||||
and provide access to many developers, without having to give them real
|
||||
userids on or shell access to the server. The essential magic in doing this
|
||||
is ssh's pubkey access and the `authorized_keys` file, and the inspiration was
|
||||
an older program called gitosis.
|
||||
|
||||
Gitolite can restrict who can read from (clone/fetch) or write to (push) a
|
||||
repository. It can also restrict who can push to what branch or tag, which is
|
||||
very important in a corporate environment. Gitolite can be installed without
|
||||
requiring root permissions, and with no additional software than git itself
|
||||
and perl. It also has several other neat features described below and
|
||||
elsewhere in the [doc/][docs] directory.
|
||||
|
||||
<a name="_why"></a>
|
||||
|
||||
### why
|
||||
|
||||
Gitolite is separate from git, and needs to be installed and configured. So...
|
||||
why do we bother?
|
||||
|
||||
Gitolite is useful in any server that is going to host multiple git
|
||||
repositories, each with many developers, where some sort of access control is
|
||||
required.
|
||||
|
||||
In theory, this can be done with plain old Unix permissions: each user is a
|
||||
member of one or more groups, each group "owns" one or more repositories, and
|
||||
using unix permissions (especially the setgid bit -- `chmod g+s`) you can
|
||||
allow/disallow users access to repos.
|
||||
|
||||
But there are several disadvantages here:
|
||||
|
||||
* every user needs a userid and password on the server. This is usually a
|
||||
killer, especially in tightly controlled environments
|
||||
* adding/removing access rights involves complex `usermod -G ...` mumblings
|
||||
which most admins would rather not deal with
|
||||
* *viewing* (aka auditing) the current set of permissions requires running
|
||||
multiple commands to list directories and their permissions/ownerships,
|
||||
users and their group memberships, and then correlating all these manually
|
||||
* auditing historical permissions or permission changes is pretty much
|
||||
impossible without extraneous tools
|
||||
* errors or omissions in setting the permissions exactly can cause problems
|
||||
of either kind: false accepts or false rejects
|
||||
* without going into ACLs it is not possible to give someone read-only
|
||||
access to a repo; they either get read-write access or no access
|
||||
* it is absolutely impossible to restrict pushing by branch name or tag
|
||||
name.
|
||||
|
||||
Gitolite does away with all this:
|
||||
|
||||
* it uses ssh magic to remove the need to give actual unix userids to
|
||||
developers
|
||||
* it uses a simple but powerful config file format to specify access rights
|
||||
* access control changes are affected by modifying this file, adding or
|
||||
removing user's public keys, and "compiling" the configuration
|
||||
* this also makes auditing trivial -- all the data is in one place, and
|
||||
changes to the configuration are also logged, so you can audit them.
|
||||
* finally, the config file allows distinguishing between read-only and
|
||||
read-write access, not only at the repository level, but at the branch
|
||||
level within repositories.
|
||||
|
||||
<a name="_main_features"></a>
|
||||
|
||||
### main features
|
||||
|
||||
The most important feature I needed was **per-branch permissions**. This is
|
||||
pretty much mandatory in a corporate environment, and is almost the single
|
||||
reason I started *thinking* about writing gitolite.
|
||||
|
||||
It's not just "read-only" versus "read-write". Rewinding a branch (aka "non
|
||||
fast forward push") is potentially dangerous, but sometimes needed. So is
|
||||
deleting a branch (which is really just an extreme form of rewind). I needed
|
||||
something in between allowing anyone to do it (the default) and disabling it
|
||||
completely (`receive.denyNonFastForwards` or `receive.denyDeletes`).
|
||||
|
||||
Here're **some more features**. All of them, and more, are documented in
|
||||
detail somewhere in gitolite's [doc/][docs] subdirectory.
|
||||
|
||||
* simple, yet powerful, config file syntax, including specifying
|
||||
gitweb/daemon access. You'll need this power if you manage lots of
|
||||
users+repos+combinations of access
|
||||
* apart from branch-name based restrictions, you can also restrict by
|
||||
file/dir name changed (i.e., output of `git diff --name-only`)
|
||||
* if your requirements are still too complex, you can split up the config
|
||||
file and delegate authority over parts of it
|
||||
* easy to specify gitweb owner, description and gitweb/daemon access
|
||||
* easy to sync gitweb (http) authorisation with gitolite's access config
|
||||
* comprehensive logging [aka: management does not think "blame" is just a
|
||||
synonym for "annotate" :-)]
|
||||
* "personal namespace" prefix for each dev
|
||||
* migration guide and simple converter for gitosis conf file
|
||||
* "exclude" (or "deny") rights at the branch/tag level
|
||||
* specify repos using patterns (patterns may include creator's name)
|
||||
* define powerful operations on the server side, even github-like forking
|
||||
|
||||
<a name="support"></a>
|
||||
|
||||
<a name="_support"></a>
|
||||
|
||||
### support
|
||||
|
||||
Most installation problems are caused by not knowing ssh. Take a look at this
|
||||
[transcript][] to see how simple it actually is, if your server's ssh daemon
|
||||
is behaving itself.
|
||||
|
||||
If I suspect your problem is an ssh issue, I will probably ignore it. Please
|
||||
learn how [gitolite uses ssh][doc9gas] and then methodically go through the
|
||||
[ssh trouble shooting][doc6sts] document. These two documents contain
|
||||
everything I could possibly tell you. I have nothing to add.
|
||||
|
||||
Even for other topics, please look through at least the table of contents of
|
||||
at least the numbered documents to see if your question is already answered,
|
||||
before asking.
|
||||
|
||||
<a name="_security"></a>
|
||||
|
||||
### security
|
||||
|
||||
Due to the environment in which this was created and the need it fills, I
|
||||
consider this a "security" program, albeit a very modest one.
|
||||
|
||||
For the first person to find a security hole in it, defined as allowing a
|
||||
normal user (not the gitolite admin) to read a repo, or write/rewind a ref,
|
||||
that the config file says he shouldn't, and caused by a bug in *code* that is
|
||||
in the "master" branch, (not in the other branches, or the configuration file
|
||||
or in Unix, perl, shell, etc.)... well I can't afford 1000 USD rewards like
|
||||
djb, so you'll have to settle for 5000 INR (Indian Rupees) as a "token" prize
|
||||
:-)
|
||||
|
||||
However, there are a few optional features (which must be explicitly enabled
|
||||
in the RC file) where I just haven't had the time to reason about security
|
||||
thoroughly enough. Please read the comments in `conf/example.gitolite.rc` for
|
||||
details, looking for the word "security".
|
||||
|
||||
----
|
||||
|
||||
<a name="_contact_and_license"></a>
|
||||
|
||||
### contact and license
|
||||
|
||||
Gitolite is released under GPL v2. See COPYING for details.
|
||||
|
||||
* author: sitaramc@gmail.com, sitaram@atc.tcs.com
|
||||
* mailing list: gitolite@googlegroups.com
|
||||
* list subscribe address : gitolite+subscribe@googlegroups.com
|
||||
|
||||
[transcript]: http://github.com/sitaramc/gitolite/blob/pu/doc/install-transcript.mkd
|
||||
[install]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd
|
||||
[admin]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd
|
||||
[migr]: http://github.com/sitaramc/gitolite/blob/pu/doc/migrate.mkd
|
||||
[docs]: http://github.com/sitaramc/gitolite/blob/pu/doc
|
||||
[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite-and-ssh.mkd
|
||||
[doc6sts]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd
|
||||
[who]: http://github.com/sitaramc/gitolite/blob/pu/doc/who-uses-it.mkd
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use Cwd;
|
||||
|
||||
my $h = $ENV{HOME};
|
||||
my $rc = "$h/.gitolite.rc";
|
||||
my %count;
|
||||
|
||||
intro();
|
||||
|
||||
msg( FATAL => "no rc file found; do you even *have* g2 running?" ) if not -f $rc;
|
||||
do $rc;
|
||||
unless ( $return = do $rc ) {
|
||||
msg( FATAL => "couldn't parse $rc: $@" ) if $@;
|
||||
msg( FATAL => "couldn't do $rc: $!" ) unless defined $return;
|
||||
msg( WARNING => "couldn't run $rc" ) unless $return;
|
||||
}
|
||||
|
||||
print "checking rc file...\n";
|
||||
rc_basic();
|
||||
rest_of_rc();
|
||||
print "\n";
|
||||
|
||||
print "checking conf file(s)...\n";
|
||||
conf();
|
||||
print "\n";
|
||||
|
||||
print "checking repos...\n";
|
||||
repo();
|
||||
print "\n";
|
||||
|
||||
print "...all done...\n";
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub intro {
|
||||
msg( INFO => "This program only checks for uses that make the new g3 completely unusable" );
|
||||
msg( '' => "or that might end up giving *more* access to someone if migrated as-is." );
|
||||
msg( '' => "It does NOT attempt to catch all the differences described in the docs." );
|
||||
msg( '', '' );
|
||||
msg( INFO => "'see docs' usually means the pre-migration checklist in" );
|
||||
msg( '', => "'g2migr.html'; to get there, start from the main migration" );
|
||||
msg( '', => "page at http://sitaramc.github.com/gitolite/install.html#migr" );
|
||||
msg( '', '' );
|
||||
}
|
||||
|
||||
sub rc_basic {
|
||||
msg( FATAL => "GL_ADMINDIR in the wrong place -- aborting; see docs" ) if $GL_ADMINDIR ne "$h/.gitolite";
|
||||
msg( NOTE => "GL_ADMINDIR is in the right place; assuming you did not mess with" );
|
||||
msg( '', "GL_CONF, GL_LOGT, GL_KEYDIR, and GL_CONF_COMPILED" );
|
||||
msg( FATAL => "REPO_BASE in the wrong place -- aborting; see docs" ) if $REPO_BASE ne "$h/repositories" and $REPO_BASE ne "repositories";
|
||||
# ( abs or rel both ok)
|
||||
}
|
||||
|
||||
sub rest_of_rc {
|
||||
msg( SEVERE => "GIT_PATH found; see docs" ) if $GIT_PATH;
|
||||
msg( SEVERE => "GL_ALL_INCLUDES_SPECIAL found; see docs" ) if $GL_ALL_INCLUDES_SPECIAL;
|
||||
msg( SEVERE => "GL_NO_CREATE_REPOS not yet implemented" ) if $GL_NO_CREATE_REPOS;
|
||||
msg( SEVERE => "rsync not yet implemented" ) if $RSYNC_BASE;
|
||||
msg( WARNING => "ADMIN_POST_UPDATE_CHAINS_TO found; see docs" ) if $ADMIN_POST_UPDATE_CHAINS_TO;
|
||||
msg( WARNING => "GL_NO_DAEMON_NO_GITWEB found; see docs" ) if $GL_NO_DAEMON_NO_GITWEB;
|
||||
msg( WARNING => "GL_NO_SETUP_AUTHKEYS found; see docs" ) if $GL_NO_SETUP_AUTHKEYS;
|
||||
msg( WARNING => "UPDATE_CHAINS_TO found; see docs" ) if $UPDATE_CHAINS_TO;
|
||||
msg( WARNING => "GL_ADC_PATH found; see docs" ) if $GL_ADC_PATH;
|
||||
msg( WARNING => "non-default GL_WILDREPOS_PERM_CATS found" ) if $GL_WILDREPOS_PERM_CATS ne 'READERS WRITERS';
|
||||
}
|
||||
|
||||
sub conf {
|
||||
chdir($h);
|
||||
chdir($GL_ADMINDIR);
|
||||
|
||||
my $conf = `find . -name "*.conf" | xargs cat`;
|
||||
msg( "SEVERE", "NAME rules; see docs" ) if $conf =~ m(NAME/);
|
||||
msg( "SEVERE", "subconf command in admin repo; see docs" ) if $conf =~ m(NAME/conf/fragments);
|
||||
msg( "SEVERE", "mirroring used; see docs" ) if $conf =~ m(config +gitolite\.mirror\.);
|
||||
}
|
||||
|
||||
sub repo {
|
||||
chdir($h);
|
||||
chdir($REPO_BASE);
|
||||
my @creater = `find . -name gl-creater`;
|
||||
if (@creater) {
|
||||
msg( WARNING => "found " . scalar(@creater) . " gl-creater files; see docs" );
|
||||
}
|
||||
|
||||
my @perms = `find . -name gl-perms | xargs egrep -l -w R\\|RW`;
|
||||
if (@perms) {
|
||||
msg( WARNING => "found " . scalar(@perms) . " gl-perms files with R or RW; see docs" );
|
||||
}
|
||||
}
|
||||
|
||||
sub msg {
|
||||
my ( $type, $text ) = @_;
|
||||
print "$type" if $type;
|
||||
print "\t$text\n";
|
||||
exit 1 if $type eq 'FATAL';
|
||||
|
||||
$count{$type}++ if $type;
|
||||
}
|
300
conf/example.conf
Normal file
300
conf/example.conf
Normal file
|
@ -0,0 +1,300 @@
|
|||
# example conf file for gitolite
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# overall syntax:
|
||||
# - everything is space-separated; no commas, semicolons, etc (except in
|
||||
# the description string for gitweb)
|
||||
# - comments in the normal shell-ish style; no surprises there
|
||||
# - there are NO continuation lines of any kind
|
||||
# - user/repo names as simple as possible; they must start with an
|
||||
# alphanumeric, but after that they can also contain ".", "_", "-".
|
||||
# - usernames can optionally be followed by an "@" and a domainname
|
||||
# containing at least one "." (this allows you to use an email
|
||||
# address as someone's username)
|
||||
# - reponames can contain "/" characters (this allows you to
|
||||
# put your repos in a tree-structure for convenience)
|
||||
|
||||
# objectives, over and above gitosis:
|
||||
# - simpler syntax
|
||||
# - easier gitweb/daemon control
|
||||
# - specify who can push a branch/tag
|
||||
# - specify who can rewind a branch/rewrite a tag
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# GROUPS
|
||||
# ------
|
||||
|
||||
# syntax:
|
||||
# @groupname = [one or more names]
|
||||
|
||||
# groups let you club (user or group) names together for convenience
|
||||
|
||||
# * a group is like a #define in C except that it can *accumulate* values
|
||||
# * the config file is parsed in a single-pass, so later *additions* to a
|
||||
# group name cannot affect earlier *uses* of it
|
||||
|
||||
# The following examples should illustrate all this:
|
||||
|
||||
# you can have a group of people...
|
||||
@staff = sitaram some_dev another-dev
|
||||
|
||||
# ...or a group of repos
|
||||
@oss_repos = gitolite linux git perl rakudo entrans vkc
|
||||
|
||||
# ...or even a group of refexes
|
||||
@important = master$ QA_done refs/tags/v[0-9]
|
||||
# (see later for what "refex"s are; I'm only mentioning it
|
||||
# here to emphasise that you can group them too)
|
||||
|
||||
# even sliced and diced differently
|
||||
@admins = sitaram admin2
|
||||
# notice that sitaram is in 2 groups (staff and admins)
|
||||
|
||||
# if you repeat a group name in another definition line, the
|
||||
# new ones get added to the old ones (they accumulate)
|
||||
@staff = au.thor
|
||||
# so now "@staff" expands to all 4 names
|
||||
|
||||
# groups can include other groups, and the included group will
|
||||
# be expanded to whatever value it currently has
|
||||
@interns = indy james
|
||||
@staff = bob @interns
|
||||
# "@staff" expands to 7 names now
|
||||
@interns = han
|
||||
# "@interns" now has 3 names in it, but note that this does
|
||||
# not change @staff
|
||||
|
||||
# REPO AND BRANCH PERMISSIONS
|
||||
# ---------------------------
|
||||
|
||||
# syntax:
|
||||
# start line:
|
||||
# repo [one or more repos and/or repo groups]
|
||||
# followed by one or more permissions lines:
|
||||
# (C|R|RW|RW+|RWC|RW+C|RWD|RW+D|RWCD|RW+CD) [zero or more refexes] = [one or more users]
|
||||
|
||||
# there are 6 types of permissions: R, RW, and RW+ are simple (the "+" means
|
||||
# permission to "rewind" -- force push a non-fast forward to -- a branch).
|
||||
# The *standalone* C permission pertains to creating a REPO and is described
|
||||
# in doc/4-wildcard-repositories.mkd. The C and D *suffixes* to the RW/RW+
|
||||
# permissions pertain to creating or deleting a BRANCH, and are described in
|
||||
# doc/3-faq-tips-etc.mkd, in the sections on "separating push and create
|
||||
# rights" and "separating delete and rewind rights" respectively.
|
||||
|
||||
# how permissions are matched:
|
||||
# - user, repo, and access (W or +) are known. For that combination, if
|
||||
# any of the refexes match the refname being updated, the push succeeds.
|
||||
# If none of them match, it fails
|
||||
|
||||
# what's a refex? a regex to match against the ref being updated (get it?)
|
||||
# See next section for more on refexes
|
||||
|
||||
# BASIC PERMISSIONS (repo level only; apply to all branches/tags in repo)
|
||||
|
||||
# most important rule of all -- specify who can make changes
|
||||
# to *this* file take effect
|
||||
repo gitolite-admin
|
||||
RW+ = @admins
|
||||
|
||||
# "@all" is a special, predefined, group name of all users
|
||||
# (everyone who has a pubkey in keydir)
|
||||
repo testing
|
||||
RW+ = @all
|
||||
|
||||
# this repo is visible to staff but only sitaram can write to it
|
||||
repo gitolite
|
||||
R = @staff
|
||||
RW+ = sitaram
|
||||
|
||||
# you can split up access rules for a repo for convenience
|
||||
# (notice that @oss_repos contains gitolite also)
|
||||
repo @oss_repos
|
||||
R = @all
|
||||
|
||||
# set permissions to all repos. *Please* do see
|
||||
# doc/3-faq-tips-etc.mkd for notes on this feature
|
||||
repo @all
|
||||
RW+ = @admins
|
||||
|
||||
# SPECIFYING AND USING A REFEX
|
||||
|
||||
# - refexes are specified in perl regex syntax
|
||||
# - refexes are prefix-matched (they are internally anchored with "^"
|
||||
# before being used), which means a refex like "refs/tags/v[0-9]"
|
||||
# matches anything *starting with* that pattern. There may be text
|
||||
# after it (example: refs/tags/v4-r3/p7), and it will still match
|
||||
|
||||
# ADVANCED PERMISSIONS USING REFEXES
|
||||
|
||||
# - if no refex appears, the rule applies to all refs in that repo
|
||||
# - a refex is automatically prefixed by "refs/heads/" if it doesn't start
|
||||
# with "refs/" (so tags have to be explicitly named as
|
||||
# refs/tags/pattern)
|
||||
|
||||
# here's the example from
|
||||
# Documentation/howto/update-hook-example.txt:
|
||||
|
||||
# refs/heads/master junio
|
||||
# +refs/heads/pu junio
|
||||
# refs/heads/cogito$ pasky
|
||||
# refs/heads/bw/.* linus
|
||||
# refs/heads/tmp/.* .*
|
||||
# refs/tags/v[0-9].* junio
|
||||
|
||||
# and here're the equivalent gitolite refexes
|
||||
repo git
|
||||
RW master = junio
|
||||
RW+ pu = junio
|
||||
RW cogito$ = pasky
|
||||
RW bw/ = linus
|
||||
RW tmp/ = @all
|
||||
RW refs/tags/v[0-9] = junio
|
||||
|
||||
# DENY/EXCLUDE RULES
|
||||
|
||||
# ***IMPORTANT NOTES ABOUT "DENY" RULES***:
|
||||
|
||||
# - deny rules do NOT affect read access. They only apply to `W` and `+`.
|
||||
#
|
||||
# - when using deny rules, the order of your rules starts to matter, where
|
||||
# earlier it did not. The first matching rule applies, where "matching" is
|
||||
# defined as either permitting the operation you're attempting (`W` or `+`),
|
||||
# which results in success, or a "deny" (`-`), which results in failure.
|
||||
# (As before, a fallthrough also results in failure).
|
||||
#
|
||||
# - do not use `@all` when your config has any deny rules; it won't work as
|
||||
# you probably expect it to!
|
||||
|
||||
# in the example above, you cannot easily say "anyone can write any tag,
|
||||
# except version tags can only be written by junio". The following might look
|
||||
# like it works but it doesn't:
|
||||
|
||||
# RW refs/tags/v[0-9] = junio
|
||||
# RW refs/tags/ = junio linus pasky @others
|
||||
|
||||
# if you use "deny" rules, however, you can do this (a "deny" rule just uses
|
||||
# "-" instead of "R" or "RW" or "RW+" in the permission field)
|
||||
|
||||
RW refs/tags/v[0-9] = junio
|
||||
- refs/tags/v[0-9] = linus pasky @others
|
||||
RW refs/tags/ = junio linus pasky @others
|
||||
|
||||
# FILE/DIR NAME BASED RESTRICTIONS
|
||||
# --------------------------------
|
||||
|
||||
# Here's a hopefully self-explanatory example. Assume the project has the
|
||||
# following contents at the top level: a README, a "doc/" directory, and an
|
||||
# "src/" directory.
|
||||
|
||||
repo foo
|
||||
RW+ = lead_dev # rule 1
|
||||
RW = dev1 dev2 dev3 dev4 # rule 2
|
||||
|
||||
RW NAME/ = lead_dev # rule 3
|
||||
RW NAME/doc/ = dev1 dev2 # rule 4
|
||||
RW NAME/src/ = dev1 dev2 dev3 dev4 # rule 5
|
||||
|
||||
# Notes
|
||||
|
||||
# - the "NAME/" is part of the syntax; think of it as a keyword if you like.
|
||||
# The rest of it is treated as a refex to match against each file being
|
||||
# touched (see "SPECIFYING AND USING A REFEX" above for details)
|
||||
|
||||
# - file/dir NAME-based restrictions are *in addition* to normal (branch-name
|
||||
# based) restrictions; they are not a *replacement* for them. This is why
|
||||
# rule #2 (or something like it, maybe with a more specific branch-name) is
|
||||
# needed; without it, dev1/2/3/4 cannot push any branches.
|
||||
|
||||
# - if a repo has *any* NAME/ rules, then NAME-based restrictions are checked
|
||||
# for *all* users. This is why rule 3 is needed, even though we don't
|
||||
# actually have any NAME-based restrictions on lead_dev. Notice the pattern
|
||||
# on rule 3.
|
||||
|
||||
# - *each* file touched by the commits being pushed is checked against those
|
||||
# rules. So, lead_dev can push changes to any files, dev1/2 can push
|
||||
# changes to files in "doc/" and "src/" (but not the top level README), and
|
||||
# dev3/4 can only push changes to files in "src/".
|
||||
|
||||
# GITWEB AND DAEMON STUFF
|
||||
# -----------------------
|
||||
|
||||
# No specific syntax for gitweb and daemon access; just make the repo readable
|
||||
# ("R" access) to the special users "gitweb" and "daemon"
|
||||
|
||||
# make "@oss_repos" (all 7 of them!) accessible via git daemon
|
||||
repo @oss_repos
|
||||
R = daemon
|
||||
|
||||
# make the two *large* repos accessible via gitweb
|
||||
repo linux perl
|
||||
R = gitweb
|
||||
|
||||
# REPO OWNER/DESCRIPTION LINE FOR GITWEB
|
||||
|
||||
# syntax, one of:
|
||||
# reponame = "some description string in double quotes"
|
||||
# reponame "owner name" = "some description string in double quotes"
|
||||
|
||||
# note: setting a description also gives gitweb access; you do not have to
|
||||
# give gitweb access as described above if you're specifying a description
|
||||
|
||||
gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment"
|
||||
|
||||
# REPO SPECIFIC GITCONFIG
|
||||
# -----------------------
|
||||
|
||||
# update 2010-02-06; this won't work unless the rc file has the right
|
||||
# settings; please see comments around the variable $GL_GITCONFIG_KEYS in
|
||||
# conf/example.gitolite.rc for details and security information.
|
||||
|
||||
# (Thanks to teemu dot matilainen at iki dot fi)
|
||||
|
||||
# this should be specified within a "repo" stanza
|
||||
|
||||
# syntax:
|
||||
# config sectionname.keyname = [optional value_string]
|
||||
|
||||
# example usage: if you placed a hook in hooks/common that requires
|
||||
# configuration information that is specific to each repo, you could do this:
|
||||
|
||||
repo gitolite
|
||||
config hooks.mailinglist = gitolite-commits@example.tld
|
||||
config hooks.emailprefix = "[gitolite] "
|
||||
config foo.bar = ""
|
||||
config foo.baz =
|
||||
|
||||
# This does either a plain "git config section.key value" (for the first 3
|
||||
# examples above) or "git config --unset-all section.key" (for the last
|
||||
# example). Other forms (--add, the value_regex, etc) are not supported.
|
||||
|
||||
# INCLUDE SOME OTHER FILE
|
||||
# -----------------------
|
||||
|
||||
include "foo.conf"
|
||||
|
||||
# this includes the contents of $GL_ADMINDIR/conf/foo.conf here
|
||||
|
||||
# Notes:
|
||||
# - the include statement is not allowed inside delegated fragments for
|
||||
# security reasons.
|
||||
# - you can also use an absolute path if you like, although in the interests
|
||||
# of cloning the admin-repo sanely you should avoid doing this!
|
||||
|
||||
# EXTERNAL COMMAND HELPERS -- RSYNC
|
||||
# ---------------------------------
|
||||
|
||||
# If $RSYNC_BASE is non-empty, the following config entries come into play
|
||||
# (otherwise they are ignored):
|
||||
|
||||
# a "fake" git repository to collect rsync rules. Gitolite does not
|
||||
# auto-create any repo whose name starts with EXTCMD/
|
||||
repo EXTCMD/rsync
|
||||
# grant permissions to files/dirs within the $RSYNC_BASE tree. A leading
|
||||
# NAME/ is required as a prefix; the actual path starts after that. Matching
|
||||
# follows the same rules as given in "FILE/DIR NAME BASED RESTRICTIONS" above
|
||||
RW NAME/ = sitaram
|
||||
RW NAME/foo/ = user1
|
||||
R NAME/bar/ = user2
|
||||
# just to remind you that these are perl regexes, not shell globs
|
||||
RW NAME/baz/.*/*.c = user3
|
295
conf/example.gitolite.rc
Normal file
295
conf/example.gitolite.rc
Normal file
|
@ -0,0 +1,295 @@
|
|||
# paths and configuration variables for gitolite
|
||||
|
||||
# please read comments before editing
|
||||
|
||||
# this file is meant to be pulled into a perl program using "do" or "require".
|
||||
|
||||
# You do NOT need to know perl to edit the paths; it should be fairly
|
||||
# self-explanatory and easy to maintain perl syntax :-)
|
||||
|
||||
# --------------------------------------
|
||||
# Do not uncomment these values unless you know what you're doing
|
||||
# $GL_PACKAGE_CONF = "";
|
||||
# $GL_PACKAGE_HOOKS = "";
|
||||
|
||||
# --------------------------------------
|
||||
# MIRRORING SUPPORT
|
||||
|
||||
# $GL_SLAVE_MODE = 0;
|
||||
# $ENV{GL_SLAVES} = 'gitolite@server2 gitolite@server3';
|
||||
# PLEASE USE SINGLE QUOTES ABOVE, NOT DOUBLE QUOTES
|
||||
|
||||
# see doc/mirroring.mkd for details
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# this is where the repos go. If you provide a relative path (not starting
|
||||
# with "/"), it's relative to your $HOME. You may want to put in something
|
||||
# like "/bigdisk" or whatever if your $HOME is too small for the repos, for
|
||||
# example
|
||||
|
||||
$REPO_BASE="repositories";
|
||||
|
||||
# the default umask for repositories is 0077; change this if you run stuff
|
||||
# like gitweb and find it can't read the repos. Please note the syntax; the
|
||||
# leading 0 is required
|
||||
|
||||
$REPO_UMASK = 0077; # gets you 'rwx------'
|
||||
# $REPO_UMASK = 0027; # gets you 'rwxr-x---'
|
||||
# $REPO_UMASK = 0022; # gets you 'rwxr-xr-x'
|
||||
|
||||
# part of the setup of gitweb is a variable called $projects_list (please see
|
||||
# gitweb documentation for more on this). Set this to the same value:
|
||||
|
||||
$PROJECTS_LIST = $ENV{HOME} . "/projects.list";
|
||||
|
||||
# giving access to @all users (as in "R = @all") in the config normally does
|
||||
# *not* include the special users "gitweb" and "daemon". If you want @all to
|
||||
# include these two users, set this variable:
|
||||
|
||||
# $GL_ALL_INCLUDES_SPECIAL = 0;
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# I see no reason anyone may want to change the gitolite admin directory, but
|
||||
# feel free to do so. However, please note that it *must* be an *absolute*
|
||||
# path (i.e., starting with a "/" character)
|
||||
|
||||
# gitolite admin directory, files, etc
|
||||
|
||||
$GL_ADMINDIR=$ENV{HOME} . "/.gitolite";
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# templates for location of the log files and format of their names
|
||||
|
||||
# I prefer this template (note the %y and %m placeholders)
|
||||
# it produces files like `~/.gitolite/logs/gitolite-2009-09.log`
|
||||
|
||||
$GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m.log";
|
||||
|
||||
# other choices are below, or you can make your own -- but PLEASE MAKE SURE
|
||||
# the directory exists and is writable; gitolite won't do that for you (unless
|
||||
# it is the default, which is "$GL_ADMINDIR/logs")
|
||||
|
||||
# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y-%m-%d.log";
|
||||
# $GL_LOGT="$GL_ADMINDIR/logs/gitolite-%y.log";
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# location of the performance log files
|
||||
|
||||
# uncomment and set this variable if you want performance logging
|
||||
#
|
||||
# perf log files are different from access log files; they store different
|
||||
# information, are not meant to be as long-lived, and so on
|
||||
|
||||
# $GL_PERFLOGT="$GL_ADMINDIR/logs/perf-gitolite-%y-%m.log";
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# Please DO NOT change these three paths
|
||||
|
||||
$GL_CONF="$GL_ADMINDIR/conf/gitolite.conf";
|
||||
$GL_KEYDIR="$GL_ADMINDIR/keydir";
|
||||
$GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm";
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# if git on your server is on a standard path (that is
|
||||
# ssh git@server git --version
|
||||
# works), leave this setting as is. Otherwise, choose one of the
|
||||
# alternatives, or write your own
|
||||
|
||||
$GIT_PATH="";
|
||||
# $GIT_PATH="/opt/bin/";
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# BIG CONFIG SETTINGS
|
||||
|
||||
# Please read doc/big-config.mkd for details
|
||||
|
||||
$GL_BIG_CONFIG = 0;
|
||||
$GL_NO_DAEMON_NO_GITWEB = 0;
|
||||
$GL_NO_CREATE_REPOS = 0;
|
||||
$GL_NO_SETUP_AUTHKEYS = 0;
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# SECURITY SENSITIVE SETTINGS
|
||||
#
|
||||
# Settings below this point may have security implications. That
|
||||
# usually means that I have not thought hard enough about all the
|
||||
# possible ways to crack security if these settings are enabled.
|
||||
|
||||
# Please see details on each setting for specifics, if any.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
# --------------------------------------
|
||||
# ALLOW REPO ADMIN TO SET GITCONFIG KEYS
|
||||
#
|
||||
# Gitolite allows you to set git repo options using the "config" keyword; see
|
||||
# conf/example.conf for details and syntax.
|
||||
#
|
||||
# However, if you are in an installation where the repo admin does not (and
|
||||
# should not) have shell access to the server, then allowing him to set
|
||||
# arbitrary repo config options *may* be a security risk -- some config
|
||||
# settings may allow executing arbitrary commands.
|
||||
#
|
||||
# You have 3 choices. By default $GL_GITCONFIG_KEYS is left empty, which
|
||||
# completely disables this feature (meaning you cannot set git configs from
|
||||
# the repo config).
|
||||
|
||||
$GL_GITCONFIG_KEYS = "";
|
||||
|
||||
# The second choice is to give it a space separated list of settings you
|
||||
# consider safe. (These are actually treated as a set of regular expression
|
||||
# patterns, and any one of them must match). For example:
|
||||
# $GL_GITCONFIG_KEYS = "core\.logAllRefUpdates core\..*compression";
|
||||
# allows repo admins to set one of those 3 config keys (yes, that second
|
||||
# pattern matches two settings from "man git-config", if you look)
|
||||
#
|
||||
# The third choice (which you may have guessed already if you're familiar with
|
||||
# regular expressions) is to allow anything and everything:
|
||||
# $GL_GITCONFIG_KEYS = ".*";
|
||||
|
||||
# NOTE that due to some quoting and interpolation issues I have not been able
|
||||
# to look at, a literal "." needs to be specified in this string as \\. (two
|
||||
# backslashes and a dot). So this is how you'd allow any keys in the "foo"
|
||||
# category:
|
||||
# $GL_GITCONFIG_KEYS = "foo\\..*";
|
||||
|
||||
# --------------------------------------
|
||||
# ALLOW GITCONFIG KEYS EVEN FOR WILD REPOS
|
||||
#
|
||||
# This is an efficiency issue more than a security issue, since this requires
|
||||
# trawling through all of $REPO_BASE looking for stuff :)
|
||||
|
||||
# $GL_GITCONFIG_WILD = 0;
|
||||
|
||||
# --------------------------------------
|
||||
# EXTERNAL COMMAND HELPER -- HTPASSWD
|
||||
|
||||
# security note: runs an external command (htpasswd) with specific arguments,
|
||||
# including a user-chosen "password".
|
||||
|
||||
# if you want to enable the "htpasswd" command, give this the absolute path to
|
||||
# whatever file apache (etc) expect to find the passwords in.
|
||||
|
||||
$HTPASSWD_FILE = "";
|
||||
|
||||
# Look in doc/3 ("easier to link gitweb authorisation with gitolite" section)
|
||||
# for more details on using this feature.
|
||||
|
||||
# --------------------------------------
|
||||
# EXTERNAL COMMAND HELPER -- RSYNC
|
||||
|
||||
# security note: runs an external command (rsync) with specific arguments, all
|
||||
# presumably filled in correctly by the client-side rsync.
|
||||
|
||||
# base path of all the files that are accessible via rsync. Must be an
|
||||
# absolute path. Leave it undefined or set to the empty string to disable the
|
||||
# rsync helper.
|
||||
|
||||
$RSYNC_BASE = "";
|
||||
|
||||
# $RSYNC_BASE = "/home/git/up-down";
|
||||
# $RSYNC_BASE = "/tmp/up-down";
|
||||
|
||||
# --------------------------------------
|
||||
# EXTERNAL COMMAND HELPER -- SVNSERVE
|
||||
|
||||
# security note: runs an external command (svnserve) with specific arguments,
|
||||
# as specified below. %u is substituted with the username.
|
||||
|
||||
# This setting allows launching svnserve when requested by the ssh client.
|
||||
# This allows using the same SSH setup (hostname/username/public key) for both
|
||||
# SVN and git access. Leave it undefined or set to the empty string to disable
|
||||
# svnserve access.
|
||||
|
||||
$SVNSERVE = "";
|
||||
# $SVNSERVE = "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u";
|
||||
|
||||
# --------------------------------------
|
||||
# ALLOW REPO CONFIG TO USE WILDCARDS
|
||||
|
||||
# security note: this used to in a separate "wildrepos" branch. You can
|
||||
# create repositories based on wild cards, give "ownership" to the specific
|
||||
# user who created it, allow him/her to hand out R and RW permissions to other
|
||||
# users to collaborate, etc. This is powerful stuff, and I've made it as
|
||||
# secure as I can, but it hasn't had the kind of rigorous line-by-line
|
||||
# analysis that the old "master" branch had.
|
||||
|
||||
# This has now been rolled into master, with all the functionality gated by
|
||||
# this variable. Set this to 1 if you want to enable the wildrepos features.
|
||||
# Please see doc/4-wildcard-repositories.mkd for details.
|
||||
|
||||
$GL_WILDREPOS = 0;
|
||||
|
||||
# --------------------------------------
|
||||
# DEFAULT WILDCARD PERMISSIONS
|
||||
|
||||
# If set, this value will be used as the default user-level permission rule of
|
||||
# new wildcard repositories. The user can change this value with the setperms command
|
||||
# as desired after repository creation; it is only a default. Note that @all can be
|
||||
# used here but is special; no other groups can be used in user-level permissions.
|
||||
|
||||
# $GL_WILDREPOS_DEFPERMS = 'R @all';
|
||||
|
||||
# --------------------------------------
|
||||
# HOOK CHAINING
|
||||
|
||||
# by default, the update hook in every repo chains to "update.secondary".
|
||||
# Similarly, the post-update hook in the admin repo chains to
|
||||
# "post-update.secondary". If you're fine with the defaults, there's no need
|
||||
# to do anything here. However, if you want to use different names or paths,
|
||||
# change these variables
|
||||
|
||||
# $UPDATE_CHAINS_TO = "hooks/update.secondary";
|
||||
# $ADMIN_POST_UPDATE_CHAINS_TO = "hooks/post-update.secondary";
|
||||
|
||||
# --------------------------------------
|
||||
# ADMIN DEFINED COMMANDS
|
||||
|
||||
# WARNING: Use this feature only if (a) you really really know what you're
|
||||
# doing or (b) you really don't care too much about security. Please read
|
||||
# doc/admin-defined-commands.mkd for details.
|
||||
|
||||
# $GL_ADC_PATH = "";
|
||||
|
||||
# --------------------------------------
|
||||
# SITE-SPECIFIC INFORMATION
|
||||
|
||||
# Some installations would like to give their users customised information
|
||||
# (like a link to their own websites, for example) so that each end user does
|
||||
# not have to grok all the gitolite documentation.
|
||||
|
||||
# If this variable is defined, the "info" command will print it at the end of
|
||||
# the listing.
|
||||
|
||||
# $GL_SITE_INFO = "";
|
||||
# $GL_SITE_INFO = "XYZ.COM DEVELOPERS: PLEASE SEE http://xyz.com/gitolite/help first";
|
||||
|
||||
# --------------------------------------
|
||||
# USERGROUP HANDLING
|
||||
|
||||
# Some sites would like to store group membership outside gitolite, because
|
||||
# they already have it in (usually) their LDAP server, and it doesn't make
|
||||
# sense to be forced to duplicate this information.
|
||||
|
||||
# Set the following variable to the name of a script that, given a username as
|
||||
# argument, will return a list of groups that she is a member of.
|
||||
|
||||
# $GL_GET_MEMBERSHIPS_PGM = "/usr/local/bin/expand-ldap-user-to-groups"
|
||||
|
||||
# --------------------------------------
|
||||
# per perl rules, this should be the last line in such a file:
|
||||
1;
|
||||
|
||||
# Local variables:
|
||||
# mode: perl
|
||||
# End:
|
||||
# vim: set syn=perl:
|
46
contrib/adc/able
Executable file
46
contrib/adc/able
Executable file
|
@ -0,0 +1,46 @@
|
|||
#!/bin/bash
|
||||
|
||||
# WARNING: USES BASH FEATURES TO AVOID A TEMP FILE; CAN BE FIXED IF NEEDED
|
||||
|
||||
. $(dirname $0)/adc.common-functions
|
||||
|
||||
get_rights_and_owner gitolite-admin
|
||||
[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
|
||||
|
||||
op=$1
|
||||
shift
|
||||
|
||||
locs=
|
||||
while [ -n "$1" ]
|
||||
do
|
||||
case $1 in
|
||||
'@all' )
|
||||
locs="$locs $HOME"
|
||||
;;
|
||||
* )
|
||||
[ -d $loc ] && locs="$locs $GL_REPO_BASE_ABS/$1.git"
|
||||
[ -d $loc ] || echo "ignoring $1..."
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case $op in
|
||||
en|enable )
|
||||
for l in $locs
|
||||
do
|
||||
rm -fv $l/.gitolite.down
|
||||
done
|
||||
;;
|
||||
dis|disable )
|
||||
# bashism
|
||||
read msg <<<$(cat)
|
||||
for l in $locs
|
||||
do
|
||||
echo $msg > $l/.gitolite.down
|
||||
done
|
||||
;;
|
||||
* )
|
||||
die "argument 1 must be 'en' or 'dis'"
|
||||
;;
|
||||
esac
|
17
contrib/adc/adc.common-functions
Normal file
17
contrib/adc/adc.common-functions
Normal file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
# please make sure this file is NOT chmod +x
|
||||
|
||||
die() { echo "$@"; exit 1; }
|
||||
|
||||
get_rights_and_owner() {
|
||||
local ans
|
||||
ans=$(perl -I$GL_BINDIR -Mgitolite -e 'cli_repo_rights("'$1'")')
|
||||
|
||||
# set shell variables as needed
|
||||
owner=${ans#* }
|
||||
rights=${ans% *}
|
||||
echo $rights | grep C >/dev/null 2>&1 && perm_create=yes || perm_create=
|
||||
echo $rights | grep R >/dev/null 2>&1 && perm_read=yes || perm_read=
|
||||
echo $rights | grep W >/dev/null 2>&1 && perm_write=yes || perm_write=
|
||||
}
|
32
contrib/adc/fork
Executable file
32
contrib/adc/fork
Executable file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/sh
|
||||
|
||||
from=$1
|
||||
to=$2
|
||||
|
||||
. $(dirname $0)/adc.common-functions
|
||||
|
||||
get_rights_and_owner $from
|
||||
[ -z "$perm_read" ] && die "no read permissions on $from"
|
||||
|
||||
get_rights_and_owner $to
|
||||
[ -z "$perm_create" ] && die "no create permissions on $to"
|
||||
|
||||
# clone $from to $to
|
||||
git clone --bare -l $GL_REPO_BASE_ABS/$from.git $GL_REPO_BASE_ABS/$to.git
|
||||
[ $? -ne 0 ] && exit 1
|
||||
|
||||
# fix up creator, gitweb owner, and hooks
|
||||
cd $GL_REPO_BASE_ABS/$to.git
|
||||
echo $GL_USER > gl-creater
|
||||
git config gitweb.owner "$GL_USER"
|
||||
( cd $HOME;perl -le 'do ".gitolite.rc"; print $GL_WILDREPOS_DEFPERMS' ) |
|
||||
SSH_ORIGINAL_COMMAND="setperms $to" $GL_BINDIR/gl-auth-command $GL_USER
|
||||
cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks
|
||||
|
||||
if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then
|
||||
echo "$GL_WILDREPOS_DEFPERMS" > gl-perms
|
||||
fi
|
||||
|
||||
# run gitolite's post-init hook if you can (hook code expects GL_REPO to be set)
|
||||
export GL_REPO; GL_REPO="$to"
|
||||
[ -x hooks/gl-post-init ] && hooks/gl-post-init
|
88
contrib/adc/gl-reflog
Executable file
88
contrib/adc/gl-reflog
Executable file
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# - show fake "reflog" from gitolite server
|
||||
# - recover deleted branches
|
||||
# - recover from bad force pushes
|
||||
|
||||
# --------------------
|
||||
|
||||
# WARNING
|
||||
# - heavily dependent on the gitolite log file format (duh!)
|
||||
# - cannot recover if some other commits were made after the force push
|
||||
|
||||
# USAGE
|
||||
# ssh git@server gl-reflog show r1 refs/heads/b1
|
||||
# # shows last 10 updates to branch b1 in repo r1
|
||||
# ssh git@server gl-reflog show r1 refs/heads/b1 20
|
||||
# # shows last 20 entries...
|
||||
# ssh git@server gl-reflog recover r1 refs/heads/b1
|
||||
# # recovers the last update to b1 in r1 if it was a "+"
|
||||
|
||||
# NOTES
|
||||
# - the verb "recover" is used because this is expected to be used most often
|
||||
# to recover deleted branches. Plus there's enough confusion in git land
|
||||
# caused by "reset" and "revert" I thought I should add my bit to it ;-)
|
||||
# - git's internal reflog is NOT recovered, even if you recover the branch.
|
||||
# I'm good but not *that* good ;-)
|
||||
# - since this program produces a log entry that satisfies it's own criteria,
|
||||
# it acts as a "toggle" for its own action for rewinds (but not for deletes)
|
||||
|
||||
my($cmd, $repo, $ref, $limit) = @ARGV;
|
||||
$limit ||= 10;
|
||||
|
||||
require "$ENV{GL_BINDIR}/gitolite.pm" or die "parse gitolite.pm failed\n";
|
||||
my ($perm, $creator, $wild) = &repo_rights($repo);
|
||||
die "you don't have read access to $repo\n" unless $perm =~ /R/;
|
||||
|
||||
my @logfiles = sort glob("$ENV{GL_ADMINDIR}/logs/*");
|
||||
|
||||
# TODO figure out how to avoid reading *all* the log files when you really
|
||||
# only need the last few
|
||||
|
||||
our @loglines;
|
||||
{
|
||||
my @f;
|
||||
local(@ARGV) = @logfiles;
|
||||
while (<>) {
|
||||
chomp;
|
||||
@f = split /\t/;
|
||||
# field 2 is the userid, 5 is W or +, 6/7 are old/new SHAs
|
||||
# 8 is reponame, 9 is refname (but all those are 1-based)
|
||||
next unless $f[3] =~ /^(git-receive-pack|gl-reflog recover) /;
|
||||
next unless $f[8];
|
||||
next unless $f[7] eq $repo;
|
||||
next unless $f[8] eq $ref;
|
||||
push @loglines, $_;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $cmd eq 'show' ) {
|
||||
my $start = @loglines - $limit;
|
||||
$start = 0 if $start < 0;
|
||||
map { print "$loglines[$_]\n" } $start .. $#loglines;
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
if ( $cmd eq 'recover' ) {
|
||||
my @f = split /\t/, $loglines[$#loglines];
|
||||
die "the last push was not yours\n" unless $f[1] eq $ENV{GL_USER};
|
||||
die "the last push was not a rewind or delete\n" unless $f[4] eq '+';
|
||||
|
||||
my($oldsha, $newsha) = @f[5,6];
|
||||
if ($newsha =~ /^0+$/) {
|
||||
print "recovering $repo $ref at $oldsha (was deleted)\n";
|
||||
} else {
|
||||
print "recovering $repo $ref at $oldsha (was forced to $newsha)\n";
|
||||
}
|
||||
chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
|
||||
|
||||
my $newsha2 = $newsha;
|
||||
$newsha2 = '' if $newsha =~ /^0+$/;
|
||||
system("git", "update-ref", $ref, $oldsha, $newsha2) and
|
||||
die "repo $repo, update-ref $ref $oldsha $newsha failed...\n";
|
||||
&log_it("", "+\t$newsha\t$oldsha\t$repo\t$ref");
|
||||
}
|
11
contrib/adc/restrict-admin
Executable file
11
contrib/adc/restrict-admin
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
. $(dirname $0)/adc.common-functions
|
||||
|
||||
get_rights_and_owner gitolite-admin
|
||||
[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
|
||||
|
||||
# and here you let them do the dangerous stuff
|
||||
echo "+rm -rf $GL_REPO_BASE_ABS"
|
||||
sleep 2
|
||||
echo ...just kidding!
|
17
contrib/adc/rmrepo
Executable file
17
contrib/adc/rmrepo
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
. $(dirname $0)/adc.common-functions
|
||||
|
||||
delete=$1
|
||||
|
||||
get_rights_and_owner $delete
|
||||
|
||||
[ "$owner" = "$GL_USER" ] || die "$delete is not yours to delete!"
|
||||
|
||||
cd $GL_REPO_BASE_ABS
|
||||
rm -rf $delete.git
|
||||
|
||||
cd $HOME
|
||||
PROJECTS_LIST=$(perl -e 'do ".gitolite.rc"; print $PROJECTS_LIST')
|
||||
export delete
|
||||
perl -ni -e 'print unless /^\Q$ENV{delete}.git\E$/' $PROJECTS_LIST
|
56
contrib/adc/su-expand
Executable file
56
contrib/adc/su-expand
Executable file
|
@ -0,0 +1,56 @@
|
|||
#!/bin/sh
|
||||
|
||||
# adc for someone with admin privs to invoke "expand" on other users.
|
||||
|
||||
# This doc block as "WHY", "HOW", and "CAVEATS" sections; I mention that only
|
||||
# for those people with very small xterms who may miss the CAVEATS section
|
||||
# otherwise...!
|
||||
|
||||
# WHY
|
||||
# ===
|
||||
|
||||
# ...because info has it, and expand doesn't :-)
|
||||
|
||||
# (Possible question: Why not add that capability to expand instead to be
|
||||
# consistent? Probable answer: how about we go the other way and make
|
||||
# "info" also work like this, and remove that code from core? I like
|
||||
# having less less code in core!)
|
||||
|
||||
# HOW
|
||||
# ===
|
||||
|
||||
# ssh gitolite su-expand . user1 user2 user3
|
||||
|
||||
# note that the first argument is still treated as a "pattern", so at
|
||||
# least put in a "." for a catch-all pattern. Also see caveats for
|
||||
# patterns below.
|
||||
|
||||
# This will output the same thing as "ssh git@server expand ." performed
|
||||
# by the individual users
|
||||
|
||||
# CAVEATS
|
||||
# =======
|
||||
|
||||
# (1) LIMITS TO THE PATTERN
|
||||
|
||||
# Due to this being an ADC, and ADC arguments being very, Very, VERY,
|
||||
# stricly limited, you won't be able to use any regex meta characters
|
||||
# expect ".". I'll worry about changing it if enough people complain.
|
||||
|
||||
# (2) NAME OF THIS SCRIPT
|
||||
|
||||
# please name this script something other than "expand", because we can't
|
||||
# have name clashes between internal commands (info, expand,
|
||||
# (get|set)(perms|desc), etc) and admin-defined (external) commands. I'm
|
||||
# calling it su-expand; feel free to change it.
|
||||
|
||||
. $(dirname $0)/adc.common-functions
|
||||
|
||||
get_rights_and_owner gitolite-admin
|
||||
[ -z "$perm_write" ] && die "just *what* are you trying to pull here, $GL_USER?"
|
||||
pat="$1"; shift
|
||||
|
||||
for user
|
||||
do
|
||||
SSH_ORIGINAL_COMMAND="expand $pat" $GL_BINDIR/gl-auth-command $user
|
||||
done
|
24
contrib/adc/sudo
Executable file
24
contrib/adc/sudo
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
|
||||
# this command is pretty cool, even if I may say so myself :)
|
||||
|
||||
# for any ADC that a normal user can run, like
|
||||
|
||||
# ssh git@server adc arguments
|
||||
|
||||
# this adc lets a "super user" (defined as "have write access to the
|
||||
# gitolite-admin repo"), do this
|
||||
|
||||
# ssh git@server sudo normal_user adc arguments
|
||||
|
||||
. $(dirname $0)/adc.common-functions
|
||||
|
||||
get_rights_and_owner gitolite-admin
|
||||
[ -z "$perm_write" ] && die "just *what* are you trying to pull, young man?"
|
||||
|
||||
user="$1"; shift
|
||||
cmd="$1"; shift
|
||||
|
||||
GL_USER=$user; export GL_USER
|
||||
[ -x $(dirname $0)/$cmd ] || die "no adc called $cmd"
|
||||
exec $(dirname $0)/$cmd "$@"
|
35
contrib/autotoc
Executable file
35
contrib/autotoc
Executable file
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
|
||||
# filter gitolite's mkd files through this; it's designed to be idempotent of
|
||||
# course
|
||||
|
||||
sub make_anchor {
|
||||
# make an anchor out of a section heading
|
||||
my $sh = shift;
|
||||
$sh =~ s/\W+/_/g;
|
||||
$sh =~ s/^_//;
|
||||
return $sh;
|
||||
}
|
||||
|
||||
undef $/;
|
||||
my $doc = <>;
|
||||
|
||||
$doc =~ s/^<a name="_.*"><\/a>\n\n//mg;
|
||||
$doc =~ s/^<a name="AUTO_.*"><\/a>\n\n//mg;
|
||||
|
||||
my @toc = $doc =~ /^###+ .*/mg;
|
||||
$doc =~ s/^(###+) (.*)/"<a name=\"_" . &make_anchor($2) . "\"><\/a>\n\n$1 $2"/mge;
|
||||
|
||||
for (@toc) {
|
||||
s/^(###+) (.*)/' ' x (length($1)-3) . ' * <a href="#_' . &make_anchor($2) . "\">$2<\/a>"/e;
|
||||
}
|
||||
|
||||
my $toc = "In this document:\n\n";
|
||||
$toc .= join("\n", @toc);
|
||||
$toc .= "\n\n";
|
||||
|
||||
$doc =~ s/^In this document:\n\n.*?\n\n/$toc/sm;
|
||||
|
||||
print $doc;
|
113
contrib/gerrit.mkd
Normal file
113
contrib/gerrit.mkd
Normal file
|
@ -0,0 +1,113 @@
|
|||
# comparing gerrit and gitolite
|
||||
|
||||
Gerrit and gitolite have too many high level differences. Size is most
|
||||
visible of course: 56000 lines of Java versus 1300 lines of perl+shell,
|
||||
according to David A. Wheeler's 'SLOCCount' tool. Gerrit needs a database (it
|
||||
comes with a perfectly usable one, or I believe you can use any of the usual
|
||||
suspects),
|
||||
and even comes with its own ssh server and git server, and since the git
|
||||
engine is internal it probably has to include a lot of things that normal git
|
||||
already has; I wouldn't know for sure.
|
||||
|
||||
Gerrit allows a lot more de-centralisation in managing the system, and of
|
||||
course excels at code review, and it seems geared to really large, open
|
||||
source-ish projects with lots of contributors.
|
||||
|
||||
Gitolite works on a pure command-line install and a plain text file config,
|
||||
and is designed to run unobtrusively and quite transparently to all developers
|
||||
-- other than sending the admin their pubkey, nothing really changes for them
|
||||
in their workflow, toolset, etc. The "lite" in the name still holds, despite
|
||||
all the extra features being pumped in!
|
||||
|
||||
Gitolite was mainly written for a corporate environment, where we really,
|
||||
really, need branch-level ACLs. While they would certainly love the code
|
||||
review part, things like *voting* on a change, and so on seem a bit alien, and
|
||||
it seems to me that code review itself is more likely to be a hierarchical
|
||||
thing, not a peer-to-peer, "anyone can comment" thing. I could be wrong.
|
||||
|
||||
----
|
||||
|
||||
In short, gitolite doesn't do the main thing that gerrit does, and gerrit is
|
||||
so much bigger than gitolite in so many ways, it seems really odd to compare
|
||||
them at all.
|
||||
|
||||
However, it seems gerrit comes closest to gitolite in terms of flexibility of
|
||||
access control, which is gitolite's main strength, so I thought it would be
|
||||
useful to compare gitolite with just what is in the "access-control.html" in
|
||||
the gerrit war file. Or see [this][gdac]. [...and stop sniggering at the
|
||||
"svn" in the link dammit!]
|
||||
|
||||
[gdac]: http://gerrit.googlecode.com/svn/documentation/2.1.2/access-control.html
|
||||
[jwzq]: http://regex.info/blog/2006-09-15/247
|
||||
|
||||
**Administrators**: anyone who has gitolite-admin push privs
|
||||
|
||||
**Anonymous Users**: gitolite doesn't do that, though the "ssh-plus" branch,
|
||||
combined with git-daemon2 (Ilari) will allow that in future. When git-daemon2
|
||||
becomes mainstream, the supporting code in this branch will also be merged
|
||||
into "master".
|
||||
|
||||
**Registered Users**: @all
|
||||
|
||||
**Account Groups**: @groups in gitolite. We do allow them to be nested,
|
||||
although the parsing is single-pass. We also don't have group `foo-admin`
|
||||
managing membership to group `foo` though; all groups are managed by the eqvt
|
||||
of "Administrators".
|
||||
|
||||
**Project ACLs**: first, let's remember (again) that we don't have any of the code
|
||||
review stuff :)
|
||||
|
||||
* **Reference-level access controls** make no mention of regexes. I'm not
|
||||
[JWZ][jwzq], and I strongly consider regexes a plus point :)
|
||||
|
||||
* They also make no mention of giving permissions to individual users, only
|
||||
groups. If this is true, it would be a little cumbersome when managing
|
||||
many small projects -- projects where you have only one QA, one
|
||||
integrator, etc., would end up making many 1-man groups. [If anyone who's
|
||||
used gerrit can correct me on this I'd appreciate it].
|
||||
|
||||
* **Evaluation** order and priority of access control rules are different.
|
||||
Gerrit goes by specificity, gitolite goes by sequence. It shouldn't
|
||||
matter; they're probably equivalent except perhaps in some far-fetched
|
||||
scenarios.
|
||||
|
||||
* One big difference is that gitolite does not process "deny" rules ("-1 no
|
||||
Access" in gerrit terms) for *read* access -- we only support those for
|
||||
write access. Gerrit uses this to "hide a handful of projects on an
|
||||
otherwise public server"; in gitolite you'd better avoid giving `R = @all`
|
||||
in the first place :)
|
||||
|
||||
* [Update 2010-04-14: it appears that Gerrit is also in the process of
|
||||
implementing *read* access control at the branch level -- they can afford
|
||||
to even think of that because they have a full jgit stack to play with.
|
||||
Gitolite is dependent on git itself to provide that -- it just cannot be
|
||||
done without support from git core. I can see some corporates drooling at
|
||||
this possibility (makes no sense for open source projects IMO) ;-)]
|
||||
|
||||
**Categories**:
|
||||
|
||||
* gitolite doesnt have an "owner" for each project in any administrative
|
||||
sense. Perhaps you could consider whoever has `RW+` perms to be an owner
|
||||
but it doesn't go beyond what that implies.
|
||||
|
||||
* gitolite doesnt do anything special to signed or annotated tags
|
||||
|
||||
* gitolite always allows creating a branch. The only way to prevent that is
|
||||
to list out allowed branches explicitly (make sure you end the refex with
|
||||
a `$`!).
|
||||
|
||||
* Force push is the same as delete: historically (and by default, even now)
|
||||
gitolite does the same . However, I've only recently (and somewhat
|
||||
reluctantly) changed gitolite to allow treating these two separately.
|
||||
|
||||
Of course, direct pushing clashes with code review, and gerrit recommends
|
||||
that if you want code review you should not use this feature. [Normal
|
||||
pushes in gerrit go through a temp branch that is moved to the correct one
|
||||
after a review is done; direct pushes are all that gitolite has].
|
||||
|
||||
* author/committer identity: checking these fields in pushed commits is
|
||||
likely to be important in some projects, but gitolite doesn't have any
|
||||
notion of this. Hmm... I smell another feature in the future :)
|
||||
|
||||
The rest of it is in areas that the two tools have no overlap on (again, code
|
||||
review being the main thing).
|
15
contrib/gitolite-tools.mkd
Normal file
15
contrib/gitolite-tools.mkd
Normal file
|
@ -0,0 +1,15 @@
|
|||
# gitolite-tools
|
||||
|
||||
gitolite-tools is a collection of external git commands to work with
|
||||
gitolite server and repositories:
|
||||
|
||||
* git gl-info - Display gitolite server information
|
||||
* git gl-ls - List accessible gitolite repositories
|
||||
* git gl-desc - Display or edit description of gitolite wildcard repositories
|
||||
* git gl-perms - Display or edit permissions of gitolite wildcard repositories
|
||||
* git gl-htpasswd - Set password for gitweb/apache
|
||||
|
||||
## Homepage
|
||||
|
||||
The project in GitHub:
|
||||
[http://github.com/tmatilai/gitolite-tools](http://github.com/tmatilai/gitolite-tools)
|
40
contrib/gitweb/gitweb.conf
Normal file
40
contrib/gitweb/gitweb.conf
Normal file
|
@ -0,0 +1,40 @@
|
|||
# --------------------------------------------
|
||||
# Per-repo authorization based on gitolite ACL
|
||||
# Include this in gitweb.conf
|
||||
# See doc/3-faq-tips-etc.mkd for more info
|
||||
|
||||
# HOME of the gitolite user
|
||||
my $gl_home = "/home/git";
|
||||
|
||||
# environment variables needed by gitolite.pm
|
||||
$ENV{GL_RC} = "$gl_home/.gitolite.rc";
|
||||
$ENV{GL_USER} = $cgi->remote_user || "gitweb";
|
||||
|
||||
# variables from the RC file
|
||||
our ($REPO_BASE, $GL_ADMINDIR);
|
||||
|
||||
# set HOME temporarily for RC parsing
|
||||
my $orig_home = $ENV{HOME};
|
||||
$ENV{HOME} = $gl_home;
|
||||
do $ENV{GL_RC}
|
||||
or die_error(500, "Failed to parse $ENV{GL_RC}: " . ($! or $@));
|
||||
$ENV{HOME} = $orig_home;
|
||||
|
||||
# set project root etc. absolute paths
|
||||
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$gl_home/$REPO_BASE" );
|
||||
$projects_list = $projectroot = $ENV{GL_REPO_BASE_ABS};
|
||||
|
||||
# load gitolite helper routines
|
||||
require "$GL_ADMINDIR/src/gitolite.pm"
|
||||
or die_error(500, "Failed to parse gitolite.pm: " . ($! or $@));
|
||||
|
||||
$export_auth_hook = sub {
|
||||
my $repo = shift;
|
||||
# gitweb passes us the full repo path; so we strip the beginning
|
||||
# and the end, to get the repo name as it is specified in gitolite conf
|
||||
return unless $repo =~ s/^\Q$projectroot\E\/?(.+)\.git$/$1/;
|
||||
|
||||
# check for (at least) "R" permission
|
||||
my ($perm, $creator) = &repo_rights($repo);
|
||||
return ($perm =~ /R/);
|
||||
};
|
11
contrib/vim/README.mkd
Normal file
11
contrib/vim/README.mkd
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Vim Syntax Highlight
|
||||
|
||||
[Vim][] Syntax highlight for `gitolite.conf` can be found from:
|
||||
|
||||
- [vim.org script page][vim.org] (Releases)
|
||||
- [GitHub][] (Sources)
|
||||
|
||||
|
||||
[Vim]: http://www.vim.org/
|
||||
[vim.org]: http://www.vim.org/scripts/script.php?script_id=2900
|
||||
[GitHub]: http://github.com/tmatilai/gitolite.vim
|
|
@ -1,127 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
#
|
||||
# migrate gitosis.conf to gitolite.conf format
|
||||
#
|
||||
# Based on gl-conf-convert by: Sitaram Chamarty
|
||||
# Rewritten by: Behan Webster <behanw@websterwood.com>
|
||||
#
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
if (not @ARGV and -t or @ARGV and $ARGV[0] eq '-h') {
|
||||
print "Usage:\n gl-conf-convert < gitosis.conf > gitolite.conf\n(please see the documentation for details)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my @comments = ();
|
||||
my $groupname;
|
||||
my %groups;
|
||||
my $reponame;
|
||||
my %repos;
|
||||
|
||||
while (<>)
|
||||
{
|
||||
# not supported
|
||||
if (/^repositories *=/ or /^map /) {
|
||||
print STDERR "not supported: $_";
|
||||
s/^/NOT SUPPORTED: /;
|
||||
print;
|
||||
next;
|
||||
}
|
||||
|
||||
# normalise whitespace to help later regexes
|
||||
chomp;
|
||||
s/\s+/ /g;
|
||||
s/ ?= ?/ = /;
|
||||
s/^ //;
|
||||
s/ $//;
|
||||
|
||||
if (/^\s*$/ and @comments > 1) {
|
||||
@{$repos{$reponame}{comments}} = @comments if $reponame;
|
||||
@{$groups{$groupname}{comments}} = @comments if $groupname;
|
||||
@comments = ();
|
||||
} elsif (/^\s*#/) {
|
||||
push @comments, $_;
|
||||
} elsif (/^\[repo\s+(.*?)\]$/) {
|
||||
$groupname = '';
|
||||
$reponame = $1;
|
||||
$reponame =~ s/\.git$//;
|
||||
} elsif (/^\[gitosis\]$/) {
|
||||
$groupname = '';
|
||||
$reponame = '@all';
|
||||
} elsif (/^gitweb\s*=\s*yes/i) {
|
||||
push @{$repos{$reponame}{R}}, 'gitweb';
|
||||
} elsif (/^daemon\s*=\s*yes/i) {
|
||||
push @{$repos{$reponame}{R}}, 'daemon';
|
||||
} elsif (/^description\s*=\s*(.+?)$/) {
|
||||
$repos{$reponame}{desc} = $1;
|
||||
} elsif (/^owner\s*=\s*(.+?)$/) {
|
||||
$repos{$reponame}{owner} = $1;
|
||||
} elsif (/^\[group\s+(.*)\]$/) {
|
||||
$reponame = '';
|
||||
$groupname = $1;
|
||||
} elsif (/^members\s*=\s*(.*)/) {
|
||||
push @{$groups{$groupname}{users}}, map {s/\@([^.]+)$/_$1/g; $_} split(' ', $1);
|
||||
} elsif (/^write?able\s*=\s*(.*)/) {
|
||||
foreach my $repo (split(' ', $1)) {
|
||||
$repo =~ s/\.git$//;
|
||||
push @{$repos{$repo}{RW}}, "\@$groupname";
|
||||
}
|
||||
} elsif (/^readonly\s*=\s*(.*)/) {
|
||||
foreach my $repo (split(' ', $1)) {
|
||||
$repo =~ s/\.git$//;
|
||||
push @{$repos{$repo}{R}}, "\@$groupname";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#use Data::Dumper;
|
||||
#print Dumper(\%repos);
|
||||
#print Dumper(\%groups);
|
||||
|
||||
# Groups
|
||||
print "#\n# Groups\n#\n\n";
|
||||
foreach my $grp (sort keys %groups) {
|
||||
next unless @{$groups{$grp}{users}};
|
||||
printf join("\n", @{$groups{$grp}{comments}})."\n" if $groups{$grp}{comments};
|
||||
printf "\@%-19s = %s\n", $grp, join(' ', @{$groups{$grp}{users}});
|
||||
}
|
||||
|
||||
# Gitweb
|
||||
print "\n#\n# Gitweb\n#\n\n";
|
||||
foreach my $repo (sort keys %repos) {
|
||||
if ($repos{$repo}{desc}) {
|
||||
@{$repos{$repo}{R}} = grep(!/^gitweb$/, @{$repos{$repo}{R}});
|
||||
print $repo;
|
||||
print " \"$repos{$repo}{owner}\"" if $repos{$repo}{owner};
|
||||
print " = \"$repos{$repo}{desc}\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Repos
|
||||
print "\n#\n# Repos\n#\n";
|
||||
foreach my $repo (sort keys %repos) {
|
||||
print "\n";
|
||||
printf join("\n", @{$repos{$repo}{comments}})."\n" if $repos{$repo}{comments};
|
||||
#if ($repos{$repo}{desc}) {
|
||||
# @{$repos{$repo}{R}} = grep(!/^gitweb$/, @{$repos{$repo}{R}});
|
||||
#}
|
||||
print "repo\t$repo\n";
|
||||
foreach my $access (qw(RW+ RW R)) {
|
||||
next unless $repos{$repo}{$access};
|
||||
my @keys;
|
||||
foreach my $key (@{$repos{$repo}{$access}}) {
|
||||
if ($key =~ /^\@(.*)/) {
|
||||
next unless defined $groups{$1} and @{$groups{$1}{users}};
|
||||
}
|
||||
push @keys, $key;
|
||||
}
|
||||
printf "\t$access\t= %s\n", join(' ', @keys) if @keys;
|
||||
}
|
||||
#if ($repos{$repo}{desc}) {
|
||||
# print $repo;
|
||||
# print " \"$repos{$repo}{owner}\"" if $repos{$repo}{owner};
|
||||
# print " = \"$repos{$repo}{desc}\"\n";
|
||||
#}
|
||||
}
|
345
doc/1-INSTALL.mkd
Normal file
345
doc/1-INSTALL.mkd
Normal file
|
@ -0,0 +1,345 @@
|
|||
# gitolite installatation
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_please_read_this_first">please read this first</a>
|
||||
* <a href="#_important_notes">important notes</a>
|
||||
* <a href="#_conventions_used">conventions used</a>
|
||||
* <a href="#_requirements">requirements</a>
|
||||
* <a href="#_client_workstation">client/workstation</a>
|
||||
* <a href="#_server">server</a>
|
||||
* <a href="#_technical_skills">technical skills</a>
|
||||
* <a href="#_installation_and_setup">installation and setup</a>
|
||||
* <a href="#_install_methods_and_deciding_which_one_to_use">install methods and deciding which one to use</a>
|
||||
* <a href="#_package_method_directly_on_the_server_using_RPM_DEB">(package method) directly on the server, using RPM/DEB</a>
|
||||
* <a href="#_root_method_directly_on_the_server_manually_with_root_access">(root method) directly on the server, manually, with root access</a>
|
||||
* <a href="#_non_root_method_directly_on_the_server_manually_without_root_access">(non-root method) directly on the server, manually, without root access</a>
|
||||
* <a href="#_from_client_method_install_from_the_client_to_the_server">(from-client method) install from the client to the server</a>
|
||||
* <a href="#_URLs_for_gitolite_managed_repos">URLs for gitolite-managed repos</a>
|
||||
* <a href="#_special_cases_multiple_gitolite_servers">special cases -- multiple gitolite servers</a>
|
||||
* <a href="#_package_method_and_root_method">package method and root method</a>
|
||||
* <a href="#_from_client_method">from-client method</a>
|
||||
* <a href="#_upgrading">upgrading</a>
|
||||
* <a href="#_uninstalling">uninstalling</a>
|
||||
* <a href="#_cleaning_out_a_botched_install">cleaning out a botched install</a>
|
||||
* <a href="#_uninstalling_gitolite_completely">uninstalling gitolite completely</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_please_read_this_first"></a>
|
||||
|
||||
### please read this first
|
||||
|
||||
<a name="_important_notes"></a>
|
||||
|
||||
#### important notes
|
||||
|
||||
Please make sure you understand the following points first.
|
||||
|
||||
* gitolite runs as a single user on a server, and is invoked via ssh. Thus,
|
||||
every user on the server is a potential "gitolite host".
|
||||
|
||||
* gitolite depends **heavily** on ssh pubkey (passwordless) access. Do not
|
||||
assume you know all about ssh -- most people **don't**. If in doubt, use
|
||||
a dedicated userid on both client and server for installation and
|
||||
administration of gitolite.
|
||||
|
||||
To make matters worse, ssh problems in gitolite don't always look like ssh
|
||||
problems. See [doc/ssh-troubleshooting.mkd][doc6] for help.
|
||||
|
||||
A gitolite setup has:
|
||||
|
||||
* a server
|
||||
* a "hosting user" on the server -- the userid under which gitolite runs.
|
||||
You can have any number of "hosting users" on one server; in fact every
|
||||
user can host their own gitolite instance
|
||||
* an "admin user" -- the user who sets up gitolite and configures it
|
||||
* the admin user's client or workstation, from which he does all his work
|
||||
|
||||
It is possible to have the server and the client be the same machine, and even
|
||||
the admin user be also the hosting user, (i.e., `sitaram@server` can install
|
||||
and administer a gitolite setup running under `sitaram@server`, a situation
|
||||
that is common with some hosting services). It's actually fairly easy and
|
||||
**safe** to do, **as long as you have password access to the server** for
|
||||
emergency use. However, I will not be documenting it because (a) if you know
|
||||
ssh you'll know how to extrapolate my instructions to do this and (b) if you
|
||||
don't know ssh it'll be a nightmare to support you.
|
||||
|
||||
<a name="_conventions_used"></a>
|
||||
|
||||
#### conventions used
|
||||
|
||||
Throughout the documentation, we use "sitaram" as the admin user, and his
|
||||
workstation is called "client". The hosting user is "git", and the server is
|
||||
called "server". **Please substitute your values as needed**.
|
||||
|
||||
Also, we often say "the rc file". This means `~/.gitolite.rc` on the server.
|
||||
And when we say the "access control rules", or "conf file", or "config file",
|
||||
we mean `conf/gitolite.conf` on your gitolite-admin clone.
|
||||
|
||||
<a name="_requirements"></a>
|
||||
|
||||
#### requirements
|
||||
|
||||
<a name="_client_workstation"></a>
|
||||
|
||||
##### client/workstation
|
||||
|
||||
* git version 1.6.2 or greater
|
||||
* even msysgit on Windows is fine; please don't ask me for help if
|
||||
you're using putty, plink, puttygen, etc., for ssh; I recommend
|
||||
msysgit for Windows and the openssh that comes with it
|
||||
* if you're using the "from-client" method of install (see below), the bash
|
||||
shell is needed
|
||||
* again, msysgit on Windows is fine
|
||||
|
||||
<a name="_server"></a>
|
||||
|
||||
##### server
|
||||
|
||||
* any Unix system with a posix compatible "sh".
|
||||
* people using "csh" or derivatives please don't ask me for help -- tell
|
||||
your admin csh is not posix compatible
|
||||
* git version 1.6.2 or greater
|
||||
* can be in a non-PATH location if you are unable to install it
|
||||
normally; see the `$GIT_PATH` variable in the "rc" file
|
||||
* perl (but since git requires it anyway, you probably have it)
|
||||
* openssh or any ssh that can understand the `authorized_keys` file format
|
||||
|
||||
<a name="_technical_skills"></a>
|
||||
|
||||
##### technical skills
|
||||
|
||||
* if you're installing gitolite, you're a "system admin", like it or not.
|
||||
Ssh is therefore a necessary skill. Please take the time to learn at
|
||||
least enough to get passwordless access working.
|
||||
|
||||
* you also need to be somewhat familiar with git itself. You cannot
|
||||
administer a whole bunch of git repositories if you don't know the basics
|
||||
of git.
|
||||
|
||||
* some familiarity with Unix and shells is probably required
|
||||
|
||||
* regular expressions are a big part of gitolite in many places but
|
||||
familiarity is not necessary to do basic access control.
|
||||
|
||||
<a name="_installation_and_setup"></a>
|
||||
|
||||
### installation and setup
|
||||
|
||||
<a name="methods"></a>
|
||||
|
||||
<a name="_install_methods_and_deciding_which_one_to_use"></a>
|
||||
|
||||
#### install methods and deciding which one to use
|
||||
|
||||
Gitolite has 4 install methods:
|
||||
|
||||
* **package method** if you have a gitolite RPM or a DEB available
|
||||
* **root method** if you have root access to the server, and you plan to
|
||||
have multiple "hosting users" on it
|
||||
* **non-root method** if you don't have root access to the server, but you
|
||||
do have at least one account with a password
|
||||
* **from-client method** if you are not comfortable with public keys and
|
||||
server side commands
|
||||
|
||||
Here's how you install using these 3 methods. Future upgrades are equally
|
||||
easy -- the steps required for upgrading are marked "(U)".
|
||||
|
||||
<a name="_package_method_directly_on_the_server_using_RPM_DEB"></a>
|
||||
|
||||
#### (package method) directly on the server, using RPM/DEB
|
||||
|
||||
* from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server.
|
||||
Put it in `/tmp/sitaram.pub`.
|
||||
|
||||
* (U) on the server, as root, do the install (urpmi, yum, apt-get, etc.).
|
||||
|
||||
* on the server, "su - git", then as "git" user, run `gl-setup
|
||||
/tmp/sitaram.pub`.
|
||||
|
||||
* on the client, run `cd; git clone git@server:gitolite-admin`
|
||||
|
||||
<a name="_root_method_directly_on_the_server_manually_with_root_access"></a>
|
||||
|
||||
#### (root method) directly on the server, manually, with root access
|
||||
|
||||
* from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server.
|
||||
Put it in `/tmp/sitaram.pub`.
|
||||
|
||||
* (U) on the server, as root, do the following:
|
||||
|
||||
cd $HOME
|
||||
git clone git://github.com/sitaramc/gitolite gitolite-source
|
||||
cd gitolite-source
|
||||
# now checkout whatever branch you want; for early adopters I suggest
|
||||
# "pu", as in "git checkout -t origin/pu" for recent gits
|
||||
mkdir -p /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks
|
||||
src/gl-system-install /usr/local/bin /usr/local/share/gitolite/conf /usr/local/share/gitolite/hooks
|
||||
|
||||
* on the server, "su - git", then as "git" user, run `gl-setup
|
||||
/tmp/sitaram.pub`.
|
||||
|
||||
* on the client, run `cd; git clone git@server:gitolite-admin`
|
||||
|
||||
<a name="_non_root_method_directly_on_the_server_manually_without_root_access"></a>
|
||||
|
||||
#### (non-root method) directly on the server, manually, without root access
|
||||
|
||||
WARNING: if you use this method you'd better know enough about ssh to be able
|
||||
to keep your keys straight, and you'd also better have password access to the
|
||||
server so that if you screw up the keys you can still get on, or be able to
|
||||
"su - git" from some other user on the server.
|
||||
|
||||
* from your workstation, copy your `~/.ssh/id_rsa.pub` file to the server.
|
||||
Put it in `/tmp/sitaram.pub`.
|
||||
|
||||
* if `$HOME/bin` is not on the default PATH, fiddle with your `.bashrc` or
|
||||
`.bash_profile` or similar files and add it somehow.
|
||||
|
||||
* (U) on the server, as "git", do the following:
|
||||
|
||||
cd $HOME
|
||||
git clone git://github.com/sitaramc/gitolite gitolite-source
|
||||
# now checkout whatever branch you want; for early adopters I suggest
|
||||
# "pu", as in "git checkout -t origin/pu" for recent gits
|
||||
cd gitolite-source
|
||||
mkdir -p $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks
|
||||
src/gl-system-install $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks
|
||||
|
||||
* on the server, still as "git", run `gl-setup /tmp/sitaram.pub`.
|
||||
|
||||
* on the client, run `cd; git clone git@server:gitolite-admin`
|
||||
|
||||
<a name="fc"></a>
|
||||
|
||||
<a name="_from_client_method_install_from_the_client_to_the_server"></a>
|
||||
|
||||
#### (from-client method) install from the client to the server
|
||||
|
||||
The advantage of this method is that it forces you to solve the ssh pubkey
|
||||
problem **before** attempting to install. It works best if you have dedicated
|
||||
userids, one on the server for installing gitolite, and one the client for
|
||||
administering it.
|
||||
|
||||
Sadly, it also forces the admin to use a different URL to access gitolite
|
||||
repos than normal users, which seems to confuse a heck of a lot of people who
|
||||
don't read the prominently displayed messages and/or the documentation.
|
||||
|
||||
This method is verbosely documented in this [transcript][], including
|
||||
*outputs* of the commands concerned.
|
||||
|
||||
<a name="_URLs_for_gitolite_managed_repos"></a>
|
||||
|
||||
### URLs for gitolite-managed repos
|
||||
|
||||
The URL for normal users (i.e., users other than the admin) is always of the
|
||||
form "git@server:reponame". So, for instance, `git clone git@server:testing`
|
||||
gets any valid user a copy of the "testing" repo.
|
||||
|
||||
In the first 3 install methods, the admin user will also use the same URL
|
||||
format, like `git clone git@server:gitolite-admin`.
|
||||
|
||||
However, in the fourth ("from-client") method, the admin user needs a
|
||||
different URL (`gitolite:reponame`) to gain access to the gitolite
|
||||
repositories. Check [here][twokeys] for why.
|
||||
|
||||
<a name="_special_cases_multiple_gitolite_servers"></a>
|
||||
|
||||
### special cases -- multiple gitolite servers
|
||||
|
||||
<a name="_package_method_and_root_method"></a>
|
||||
|
||||
#### package method and root method
|
||||
|
||||
With the first two methods of installation, it's trivial to create multiple
|
||||
gitolite instances (say one for each department, on some mega company-wide
|
||||
server). You can even do this without giving shell access to the admins.
|
||||
Here's an example with just two "departments", and their admins Alice and Bob:
|
||||
|
||||
* create userids `webbrowser_repos` and `webserver_repos`
|
||||
* ask Alice and Bob for their pubkeys; copy them to the respective home
|
||||
directories for convenience
|
||||
* run `su - webbrowser_repos`, then `gl-setup alice.pub`
|
||||
* (similarly with `webserver_repos` and `bob.pub`, and so on for others)
|
||||
|
||||
That's it. The URL for all web browser projects is now something like
|
||||
`webbrowser_repos@server:reponame`, and similarly for the others.
|
||||
|
||||
Notice that you only have to do this once for each "department", and it's
|
||||
really just one command after creating the userid. None of these admins need
|
||||
to have a command line on the server, so don't give them the passwords if you
|
||||
don't need to -- the pubkey will allow them to be gitolite admins on their
|
||||
domain, and that's quite enough for normal operations.
|
||||
|
||||
<a name="_from_client_method"></a>
|
||||
|
||||
#### from-client method
|
||||
|
||||
Thanks to Matt Perzel, the easy-install command now takes an optional 4th
|
||||
parameter, which is the "nickname" of the gitolite server. It gets defined in
|
||||
`~/.ssh/config`, and if not used it defaults to "gitolite".
|
||||
|
||||
So if you used the following command to install gitolite to 2 different
|
||||
servers:
|
||||
|
||||
./src/gl-easy-install -q git my.1st.git.server admin_user1 gitolite_server_1
|
||||
./src/gl-easy-install -q git my.2nd.git.server admin_user1 gitolite_server_2
|
||||
|
||||
you will find that `~/gitolite_server_1-admin` and `~/gitolite_server_2-admin`
|
||||
have been created as respective clones. Or you can re-clone elsewhere:
|
||||
|
||||
cd ~/admin1; git clone gitolite_server_1:gitolite-admin.git
|
||||
cd ~/admin2; git clone gitolite_server_2:gitolite-admin.git
|
||||
|
||||
<a name="_upgrading"></a>
|
||||
|
||||
### upgrading
|
||||
|
||||
Upgrading gitolite is easy. In each method above, just re-do the step that is
|
||||
marked "(U)". Also, if you're using either of the two methods that use the
|
||||
`src/gl-system-install` command, please make sure you give it the same
|
||||
arguments!
|
||||
|
||||
If you've added any new hooks, please also run the next step (`gl-setup`)
|
||||
also.
|
||||
|
||||
Also, remember that some new features may require additional settings in your
|
||||
`~/.gitolite.rc` file.
|
||||
|
||||
<a name="_uninstalling"></a>
|
||||
|
||||
### uninstalling
|
||||
|
||||
<a name="_cleaning_out_a_botched_install"></a>
|
||||
|
||||
#### cleaning out a botched install
|
||||
|
||||
When people have trouble installing gitolite, they often try to change a bunch
|
||||
of things manually on the server. This usually makes things worse ;-) so
|
||||
here's how to clean the slate.
|
||||
|
||||
* client-side
|
||||
* edit `~/.ssh/config` and delete the paragraph starting with `host
|
||||
gitolite`, if present.
|
||||
* remove `~/gitolite-admin`
|
||||
* server-side
|
||||
* edit `~/.ssh/authorized_keys` and delete all lines between `# gitolite
|
||||
start` and `# gitolite end` inclusive.
|
||||
* remove `~/.gitolite`, `~/.gitolite.rc` and
|
||||
`~/repositories/gitolite-admin.git`
|
||||
|
||||
<a name="_uninstalling_gitolite_completely"></a>
|
||||
|
||||
#### uninstalling gitolite completely
|
||||
|
||||
There's some duplication between this and the previous section, but
|
||||
uninstalling gitolite is described in great detail in
|
||||
[doc/uninstall.mkd][doc9unin]
|
||||
|
||||
----
|
||||
|
||||
[doc6]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd
|
||||
[doc9unin]: http://github.com/sitaramc/gitolite/blob/pu/doc/uninstall.mkd
|
||||
[twokeys]: http://github.com/sitaramc/gitolite/blob/pu/doc/ssh-troubleshooting.mkd#twokeys
|
||||
[transcript]: http://github.com/sitaramc/gitolite/blob/pu/doc/install-transcript.mkd
|
257
doc/2-admin.mkd
Normal file
257
doc/2-admin.mkd
Normal file
|
@ -0,0 +1,257 @@
|
|||
# administering and running gitolite
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_please_read_this_first">please read this first</a>
|
||||
* <a href="#_adding_users_and_repos">adding users and repos</a>
|
||||
* <a href="#_using_hooks">using hooks</a>
|
||||
* <a href="#_custom_hooks">custom hooks</a>
|
||||
* <a href="#_gl_post_init_hook">"gl-post-init" hook</a>
|
||||
* <a href="#_hook_chaining">hook chaining</a>
|
||||
* <a href="#_environment_variables_available_to_hooks">environment variables available to hooks</a>
|
||||
* <a href="#_other_features">other features</a>
|
||||
* <a href="#_moving_pre_existing_repos_into_gitolite">moving pre-existing repos into gitolite</a>
|
||||
* <a href="#_specifying_gitweb_and_daemon_access">specifying gitweb and daemon access</a>
|
||||
* <a href="#_custom_git_config">custom git config</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_please_read_this_first"></a>
|
||||
|
||||
### please read this first
|
||||
|
||||
Unless you know what you're doing, do not do **anything** manually on the
|
||||
server, like adding new repositories or users or changing the access control
|
||||
rules. Things will break. For example, if you manually create a repo on the
|
||||
server, it will not have the required "update" hook, without which there is no
|
||||
access control for pushes.
|
||||
|
||||
Most normal (day-to-day) gitolite admin work is done by cloning the
|
||||
gitolite-admin repo from the server to your workstation, making changes to the
|
||||
clone, and pushing those changes back.
|
||||
|
||||
The installation steps in the previous section include the steps to do this
|
||||
clone, so you should already have one on your workstation, in
|
||||
`~/gitolite-admin`. You can of course clone it anywhere else you want and use
|
||||
that clone.
|
||||
|
||||
Either way, make sure you `cd` into this clone first.
|
||||
|
||||
*Note*: some of the paths in this document use variable names. Just refer to
|
||||
`~/.gitolite.rc` for the correct values for *your* installation.
|
||||
|
||||
Once you've cloned it, you're ready to add users and repos.
|
||||
|
||||
<a name="_adding_users_and_repos"></a>
|
||||
|
||||
### adding users and repos
|
||||
|
||||
* ask each user who will get access to send you a public key. See other
|
||||
sources (for example [here][genpub]) for how to do this
|
||||
|
||||
* rename each public key according to the user's name, with a `.pub`
|
||||
extension, like `sitaram.pub` or `john-smith.pub`. You can also use
|
||||
periods and underscores
|
||||
|
||||
* copy all these `*.pub` files to `keydir` in your gitolite-admin repo
|
||||
clone. You can also organise them into various subdirectories of `keydir`
|
||||
if you wish, since the entire tree is searched.
|
||||
|
||||
* edit the config file (`conf/gitolite.conf` in your admin repo clone). See
|
||||
`conf/example.conf` in the gitolite source for details on what goes in
|
||||
that file, syntax, etc. Just add new repos as needed, and add new users
|
||||
and give them permissions as required. The users names should be exactly
|
||||
the same as their keyfile names, but without the `.pub` extension
|
||||
|
||||
* when done, commit your changes and push. Any new repos you specified will
|
||||
automatically be created (empty, but clonable) and users' access will be
|
||||
updated as needed.
|
||||
|
||||
<a name="_using_hooks"></a>
|
||||
|
||||
### using hooks
|
||||
|
||||
<a name="_custom_hooks"></a>
|
||||
|
||||
#### custom hooks
|
||||
|
||||
You can supply your own, custom, hook scripts if you wish. Install gitolite
|
||||
as usual, then:
|
||||
|
||||
* if you installed using "from-client" method (gl-easy-install):
|
||||
* go to the gitolite *source* clone from which you did the original
|
||||
install
|
||||
* add your new hook into "hooks/common"
|
||||
* run src/gl-easy-install with the same arguments as you ran the first
|
||||
time
|
||||
* if you installed using one of the other methods
|
||||
* go to ~/.gitolite/hooks/common on the server and put your new hook
|
||||
there
|
||||
* now run "gl-setup" again
|
||||
|
||||
You can use this procedure to install new hooks as well as to update hooks
|
||||
that you had previously installed.
|
||||
|
||||
**VERY IMPORTANT SECURITY NOTE: the `update` hook in `hooks/common` is what
|
||||
implements all the branch-level permissions in gitolite. If you fiddle with
|
||||
the hooks directory, please make sure you do not mess with this file
|
||||
accidentally, or all your fancy per-branch permissions will stop working.**
|
||||
|
||||
<a name="_gl_post_init_hook"></a>
|
||||
|
||||
#### "gl-post-init" hook
|
||||
|
||||
Sometimes it is necessary to do something whenever a new repo is created. If
|
||||
you need this functionality, just supply a hook called "gl-post-init" with
|
||||
whatever code you want in it.
|
||||
|
||||
<a name="_hook_chaining"></a>
|
||||
|
||||
#### hook chaining
|
||||
|
||||
Gitolite basically takes over the update hook for all repos, but some setups
|
||||
really need the update hook functionality for their own purposes too. In
|
||||
order to allow this, Gitolite now exec's a hook called `update.secondary` when
|
||||
it's own "update" hook is done and everything is ready to go.
|
||||
|
||||
You can create this `update.secondary` hook manually on selected repos on the
|
||||
server, or use the mechanism in the previous section to make gitolite put it
|
||||
on *all* your repos.
|
||||
|
||||
Similarly, gitolite also takes over the post-update hook for the special
|
||||
"gitolite-admin" repo. This hook will also chain to a `post-update.secondary`
|
||||
if such a hook exists. People wishing to do exotic things on the server side
|
||||
when the admin repo is pushed should see doc/shell-games.notes for how to
|
||||
exploit this :-)
|
||||
|
||||
Finally, these names (`update.secondary` and `post-update.secondary`) are
|
||||
merely the defaults. You can change them to anything you want; look in
|
||||
conf/example.gitolite.rc for details.
|
||||
|
||||
<a name="_environment_variables_available_to_hooks"></a>
|
||||
|
||||
#### environment variables available to hooks
|
||||
|
||||
The following environment variables are set, and may be useful for any custom
|
||||
processing you wish to do in your hook code:
|
||||
|
||||
* `GL_USER` -- the user doing the push
|
||||
* `GL_REPO` -- the reponame
|
||||
* `GL_REPO_BASE_ABS` -- the absolute base path where all the repos are kept
|
||||
|
||||
The following variables are also set, but are generally less useful:
|
||||
|
||||
* `GL_BINDIR` -- where all the binaries live
|
||||
* `GL_ADMINDIR` -- common directory for many gitolite things
|
||||
|
||||
<a name="_other_features"></a>
|
||||
|
||||
### other features
|
||||
|
||||
<a name="_moving_pre_existing_repos_into_gitolite"></a>
|
||||
|
||||
#### moving pre-existing repos into gitolite
|
||||
|
||||
One simple way to add a pre-existing repo to gitolite is to let gitolite
|
||||
create it as a brand new repo as in the previous section, then do the
|
||||
following:
|
||||
|
||||
cd your-copy-of-the-repo
|
||||
# make sure all the branches are correct and no extra stuff, "temp"
|
||||
# branches, etc., are present
|
||||
git push --all git@server:reponame
|
||||
git push --tags git@server:reponame
|
||||
|
||||
(You could also use "git push --mirror" instead of separately doing branches
|
||||
and tags, but that will carry across *your* remote refs also, and typically
|
||||
you may not want that. Anyway please do a `git ls-remote git@server:repo` to
|
||||
make sure all the stuff you want went through, and is named correctly).
|
||||
|
||||
All this is actually very simple and easily done. However, if you have many
|
||||
existing repos to add, this can be time-consuming and error-prone. Here's how
|
||||
to take a bunch of existing repos and add them to gitolite:
|
||||
|
||||
* make sure they're *bare* repos ;-)
|
||||
|
||||
* log on to the server and copy the repos to `$REPO_BASE` (which defaults to
|
||||
`~/repositories`), making sure that the directory names end in ".git".
|
||||
|
||||
* back on your workstation, add each repo (without the `.git` suffix) to
|
||||
`conf/gitolite.conf` in your gitolite-admin repo clone. Then add, commit,
|
||||
push.
|
||||
|
||||
<a name="gwd"></a>
|
||||
|
||||
<a name="_specifying_gitweb_and_daemon_access"></a>
|
||||
|
||||
#### specifying gitweb and daemon access
|
||||
|
||||
This is a feature that I personally do not use (corporate environments don't
|
||||
like unauthenticated access of any kind to any repo!), but someone wanted it,
|
||||
so here goes.
|
||||
|
||||
Gitolite defines two "special" usernames: `daemon` and `gitweb`.
|
||||
|
||||
To make a repo or repo group accessible via "git daemon", just give read
|
||||
permission to the special user "daemon". Similarly, give read permission to
|
||||
`gitweb` to allow the gitweb CGI to show the repo.
|
||||
|
||||
This gives you a quick way to offer multiple repos up for gitweb/daemon
|
||||
access.
|
||||
|
||||
However, setting a description for the project also enables gitweb permissions
|
||||
so you may as well use that method and kill two birds with one stone, like so:
|
||||
|
||||
gitolite = "fast, secure, access control for git in a corporate environment"
|
||||
|
||||
You can also specify an owner for gitweb to show, if you like:
|
||||
|
||||
gitolite "Sitaram Chamarty" = "fast, secure, access control for git in a corporate environment"
|
||||
|
||||
Note that gitolite does **not** install or configure gitweb/git-daemon -- that
|
||||
is a one-time setup you must do separately. All gitolite does is:
|
||||
|
||||
* for daemon, create the file `git-daemon-export-ok` in the repository
|
||||
* for gitweb, add the repo (plus owner name, if given) to the list of
|
||||
projects to be served by gitweb (see the config file variable
|
||||
`$PROJECTS_LIST`, which should have the same value you specified for
|
||||
`$projects_list` when setting up gitweb)
|
||||
* put the description, if given, in `$repo/description`
|
||||
|
||||
The "compile" script will keep these files consistent with the config settings
|
||||
-- this includes removing such settings/files if you remove "read" permissions
|
||||
for the special usernames or remove the description line.
|
||||
|
||||
Please **note** that giving permissions to these special users via `@all`
|
||||
(that is, using either `repo @all` or `R = @all`), will not work unless you
|
||||
set the rc-file variable `$GL_ALL_INCLUDES_SPECIAL` to `1`. Also, **NOTE**
|
||||
that giving them read access to `repo @all` means the `gitolite-admin` repo is
|
||||
also accessible. **It is upto you to decide if that is OK in your
|
||||
environment**.
|
||||
|
||||
<a name="_custom_git_config"></a>
|
||||
|
||||
#### custom git config
|
||||
|
||||
The custom hooks feature is a blunt instrument -- all repos get the hook you
|
||||
specified and will run it. In order to make it a little more fine-grained,
|
||||
you could set your hooks to only work if a certain "gitconfig" variable was
|
||||
set. Which means we now need a way to specify "git config" settings on a per
|
||||
repository basis.
|
||||
|
||||
Thanks to Teemu (teemu dot matilainen at iki dot fi), gitolite now does this
|
||||
very easily. For security reasons, this can only be done from the master
|
||||
config file (i.e., if you're using delegation, the delegated admins cannot
|
||||
specify git config settings).
|
||||
|
||||
Please see `conf/example.conf` for syntax. Note that this only supports the
|
||||
basic forms of the "git config" command:
|
||||
|
||||
git config section.key value # value may be an empty string
|
||||
git config --unset-all section.key
|
||||
|
||||
It does not (currently) support other options like `--add`, the `value_regex`,
|
||||
etc.
|
||||
|
||||
[genpub]: http://sitaramc.github.com/0-installing/2-access-gitolite.html#generating_a_public_key
|
||||
|
724
doc/3-faq-tips-etc.mkd
Normal file
724
doc/3-faq-tips-etc.mkd
Normal file
|
@ -0,0 +1,724 @@
|
|||
# assorted faqs, tips, and notes on gitolite
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_common_errors_and_mistakes">common errors and mistakes</a>
|
||||
* <a href="#_git_version_dependency">git version dependency</a>
|
||||
* <a href="#_other_errors_warnings_notes_">other errors, warnings, notes...</a>
|
||||
* <a href="#_cloning_an_empty_repo">cloning an empty repo</a>
|
||||
* <a href="#_all_syntax_for_repos">`@all` syntax for repos</a>
|
||||
* <a href="#_umask_setting">umask setting</a>
|
||||
* <a href="#_getting_a_tar_file_from_a_clone">getting a tar file from a clone</a>
|
||||
* <a href="#_features">features</a>
|
||||
* <a href="#_syntax_and_normal_usage">syntax and normal usage</a>
|
||||
* <a href="#_simpler_syntax">simpler syntax</a>
|
||||
* <a href="#_one_user_many_keys">one user, many keys</a>
|
||||
* <a href="#_security_access_control_and_auditing">security, access control, and auditing</a>
|
||||
* <a href="#_two_levels_of_access_rights_checking">two levels of access rights checking</a>
|
||||
* <a href="#_better_logging">better logging</a>
|
||||
* <a href="#_exclude_or_deny_rules">"exclude" (or "deny") rules</a>
|
||||
* <a href="#_separating_delete_and_rewind_rights">separating delete and rewind rights</a>
|
||||
* <a href="#_separating_create_and_push_rights">separating create and push rights</a>
|
||||
* <a href="#_file_dir_NAME_based_restrictions">file/dir NAME based restrictions</a>
|
||||
* <a href="#_delegating_parts_of_the_config_file">delegating parts of the config file</a>
|
||||
* <a href="#_convenience_features">convenience features</a>
|
||||
* <a href="#_what_repos_do_I_have_access_to_">what repos do I have access to?</a>
|
||||
* <a href="#_including_config_lines_from_other_files">including config lines from other files</a>
|
||||
* <a href="#_support_for_git_installed_outside_default_PATH">support for git installed outside default PATH</a>
|
||||
* <a href="#_personal_branches">"personal" branches</a>
|
||||
* <a href="#_custom_hooks_and_custom_git_config">custom hooks and custom git config</a>
|
||||
* <a href="#_bypassing_gitolite">bypassing gitolite</a>
|
||||
* <a href="#_INconvenience_features">INconvenience features</a>
|
||||
* <a href="#_deleting_a_repo">deleting a repo</a>
|
||||
* <a href="#_helping_with_gitweb">helping with gitweb</a>
|
||||
* <a href="#_easier_to_specify_gitweb_description_and_gitweb_daemon_access">easier to specify gitweb "description" and gitweb/daemon access</a>
|
||||
* <a href="#_easier_to_link_gitweb_authorisation_with_gitolite">easier to link gitweb authorisation with gitolite</a>
|
||||
* <a href="#_advanced_features">advanced features</a>
|
||||
* <a href="#_repos_named_with_wildcards">repos named with wildcards</a>
|
||||
* <a href="#_admin_defined_commands">admin defined commands</a>
|
||||
* <a href="#_access_control_for_external_commands">access control for external commands</a>
|
||||
* <a href="#_svnserve">svnserve</a>
|
||||
* <a href="#_design_choices">design choices</a>
|
||||
* <a href="#_keeping_the_parser_and_the_access_control_separate">keeping the parser and the access control separate</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_common_errors_and_mistakes"></a>
|
||||
|
||||
### common errors and mistakes
|
||||
|
||||
* adding `repositories/` at the start of the repo name in the `git clone`.
|
||||
This error is typically made by the *admin* himself -- because he knows
|
||||
what `$REPO_BASE` is set to and thinks he has to provide that prefix on
|
||||
the client side also :-) In fact gitolite prepends `$REPO_BASE`
|
||||
internally, so you shouldn't also do the same thing!
|
||||
|
||||
* being able to clone but getting errors on push. Most likely caused by a
|
||||
combination of:
|
||||
|
||||
* you already have shell access to the server, not just "gitolite"
|
||||
access, *and*
|
||||
|
||||
* you cloned using `git clone git@server:repositories/repo.git` (notice
|
||||
there's an extra "repositories/" in there?)
|
||||
|
||||
In other words, you used a key that completely bypassed gitolite and went
|
||||
straight to the shell to do the clone.
|
||||
|
||||
Please see doc/ssh-troubleshooting.mkd for what all this means.
|
||||
|
||||
<a name="_git_version_dependency"></a>
|
||||
|
||||
### git version dependency
|
||||
|
||||
Gitolite (on the server) now refuses to run if git is not at least 1.6.2.
|
||||
|
||||
<a name="_other_errors_warnings_notes_"></a>
|
||||
|
||||
### other errors, warnings, notes...
|
||||
|
||||
<a name="_cloning_an_empty_repo"></a>
|
||||
|
||||
#### cloning an empty repo
|
||||
|
||||
Cloning an empty repo is only possible with clients greater than 1.6.2. So at
|
||||
least one of your clients needs to have a recent git. Once at least one
|
||||
commit has been made, older clients can also use it
|
||||
|
||||
When you clone an empty repo, git seems to complain about `fatal: The remote
|
||||
end hung up unexpectedly`. However, you can ignore this, since it doesn't
|
||||
seem to hurt anything. [Update 2009-09-14; this has been fixed in git
|
||||
1.6.4.3]
|
||||
|
||||
<a name="_all_syntax_for_repos"></a>
|
||||
|
||||
#### `@all` syntax for repos
|
||||
|
||||
There *is* a way to use the `@all` syntax for repos also, as described in
|
||||
`conf/example.conf`. However, there are a couple of minor cautions:
|
||||
|
||||
* don't use `NAME/` or such restrictions on the special `@all` repo. Due to
|
||||
the potential for defeating a crucial optimisation and slowing down *all*
|
||||
access, we do not support this.
|
||||
* don't try giving `@all` users some permission for `@all` repos
|
||||
|
||||
<a name="_umask_setting"></a>
|
||||
|
||||
#### umask setting
|
||||
|
||||
Gitweb not able to read your repos? You can change the umask for newly
|
||||
created repos to something more relaxed -- see the `~/.gitolite.rc` file
|
||||
|
||||
<a name="_getting_a_tar_file_from_a_clone"></a>
|
||||
|
||||
### getting a tar file from a clone
|
||||
|
||||
You can clone the repo from github or indefero, then execute a make command to
|
||||
extract a tar file of the branch you want. Please use the make command, not a
|
||||
plain "git archive", because the Makefile adds a file called
|
||||
`.GITOLITE-VERSION` that will help you identify which version you are using.
|
||||
|
||||
git clone git://github.com/sitaramc/gitolite.git
|
||||
# (OR)
|
||||
git clone git://sitaramc.indefero.net/sitaramc/gitolite.git
|
||||
cd gitolite
|
||||
make master.tar
|
||||
# or maybe "make pu.tar"
|
||||
|
||||
<a name="_features"></a>
|
||||
|
||||
### features
|
||||
|
||||
Apart from the big ones listed in the top level README, and subjective ones
|
||||
like "better config file format", gitolite has evolved to have many useful
|
||||
features than the original goal of branch-level access control.
|
||||
|
||||
<a name="_syntax_and_normal_usage"></a>
|
||||
|
||||
#### syntax and normal usage
|
||||
|
||||
<a name="_simpler_syntax"></a>
|
||||
|
||||
##### simpler syntax
|
||||
|
||||
The basic syntax is simpler and cleaner but it goes beyond that: **you can
|
||||
specify access in bits and pieces**, even if they overlap.
|
||||
|
||||
Some access needs are best grouped by repo, some by username, and some by
|
||||
both. So just do all of them, and gitolite will combine all the access lists!
|
||||
Here's an example:
|
||||
|
||||
# define groups of people
|
||||
@bosses = phb1 phb2 phb3
|
||||
@devs = dev1 dev2 dev3
|
||||
@interns = int1 int2 int3
|
||||
|
||||
# define groups of projects
|
||||
@open = git gitolite linux rakudo
|
||||
@closed = c1 c2 c3
|
||||
@topsecret = ts1 ts2 ts3
|
||||
|
||||
# all bosses have read access to all projects
|
||||
repo @open @closed @topsecret
|
||||
R = @bosses
|
||||
|
||||
# everyone has read access to "open" projects
|
||||
repo @open
|
||||
R = @bosses @devs @interns
|
||||
|
||||
[...or any other combination you want...]
|
||||
|
||||
# later in the file:
|
||||
|
||||
# specify access for individual repos (like RW, RW+, etc)
|
||||
repo c1
|
||||
[...]
|
||||
|
||||
[...etc...]
|
||||
|
||||
If you notice that `@bosses` are given read access to `@open` via both rules,
|
||||
do not worry that this causes some duplication or inefficiency. It doesn't
|
||||
:-)
|
||||
|
||||
See the "specify gitweb/daemon access" section below for one more example.
|
||||
|
||||
<a name="multikeys"></a>
|
||||
|
||||
<a name="_one_user_many_keys"></a>
|
||||
|
||||
##### one user, many keys
|
||||
|
||||
I have a laptop and a desktop I need to access the server from. I have
|
||||
different private keys on them, but as far as gitolite is concerned both of
|
||||
them should be treated as "sitaram". How does this work?
|
||||
|
||||
The way it works is that you copy one pubkey as "sitaram@laptop.pub" and the
|
||||
other as "sitaram@desktop.pub". The part before the "@" is the username, so
|
||||
gitolite knows these two keys belong to the same person. The part after the
|
||||
"@" can be anything you like, of course; gitolite doesn't care.
|
||||
|
||||
Note that you don't say "sitaram@laptop" and so on in the **config** file --
|
||||
as far as the config file is concerned there's just **one** user called
|
||||
"sitaram" -- so you only say "sitaram" there.
|
||||
|
||||
I think this is easier to maintain if you have to delete or change one of
|
||||
those keys.
|
||||
|
||||
However, now that `sitaramc@gmail.com` is also a valid username, we need to
|
||||
distinguish between `sitaramc@gmail.com.pub` and `sitaramc@desktop.pub`. We
|
||||
do that by requiring that the multi-key suffix you use (like "desktop" and
|
||||
"laptop") should not have a `"."` in it. If it does, it looks like an email
|
||||
address. The following table lists sample pubkey filenames and the
|
||||
corresponding derived usernames (which is what goes into the
|
||||
`conf/gitolite.conf` file):
|
||||
|
||||
* old style multikeys; not mistaken for emails because there is no "." in
|
||||
hostname part
|
||||
|
||||
sitaramc.pub sitaramc
|
||||
sitaramc@laptop.pub sitaramc
|
||||
sitaramc@desktop.pub sitaramc
|
||||
|
||||
* new style, email keys; there is a "." in hostname part; so it's an email
|
||||
address
|
||||
|
||||
sitaramc@gmail.com.pub sitaramc@gmail.com
|
||||
|
||||
* multikeys *with* email address
|
||||
|
||||
sitaramc@gmail.com@laptop.pub sitaramc@gmail.com
|
||||
sitaramc@gmail.com@desktop.pub sitaramc@gmail.com
|
||||
|
||||
<a name="_security_access_control_and_auditing"></a>
|
||||
|
||||
#### security, access control, and auditing
|
||||
|
||||
<a name="_two_levels_of_access_rights_checking"></a>
|
||||
|
||||
##### two levels of access rights checking
|
||||
|
||||
Gitolite has two levels of access checks. The **first check** is what I will
|
||||
call the **pre-git** level. At this stage, the `gl-auth-command` has been
|
||||
invoked by `sshd`, and it knows just three things:
|
||||
|
||||
* who,
|
||||
* what repository, and
|
||||
* what type of access (R or W)
|
||||
|
||||
Note that at this point no git program has entered the picture, and we have no
|
||||
way of knowing what **ref** (branch, tag, etc) he is trying to update, even if
|
||||
it is a "write" operation.
|
||||
|
||||
For a "read" operation to pass this check, the username (or `@all`) must have
|
||||
read permission (i.e., R, RW, or RW+) on at least one branch of the repo.
|
||||
|
||||
For a "write" operation, there is an additional restriction: lines specifying
|
||||
only `R` (read access) don't count. *The user must have write access to
|
||||
**some** ref in the repo in order to pass this stage!*
|
||||
|
||||
The **second check** is via a git `update hook`. This check only happens for
|
||||
write operations. By this time we know what "ref" he is trying to update, as
|
||||
well as the old and the new SHAs of that ref (by which we can also deduce
|
||||
whether it's a rewind or not). This is where the "per-branch" permissions
|
||||
come into play.
|
||||
|
||||
Each refex that allows `W` access (or `+` if this is a rewind) for *this*
|
||||
user, on *this* repo, is matched against the actual refname being updated. If
|
||||
any of the refexes match, the push succeeds. If none of them match, it fails.
|
||||
|
||||
Gitolite also allows "exclude" or "deny" rules. See later in this document
|
||||
for details.
|
||||
|
||||
<a name="_better_logging"></a>
|
||||
|
||||
##### better logging
|
||||
|
||||
If you have been too liberal with the permission to rewind, it has built-in
|
||||
logging as an emergency fallback if someone goes too far, or for audit
|
||||
purposes [`*`]. The logfile names and location are configurable, and can
|
||||
include the year/month/day etc in the filename for easy archival or further
|
||||
processing. The log file even tells you which pattern in the config file
|
||||
matched to allow that specific access to proceed.
|
||||
|
||||
> [`*`] setting `core.logAllRefUpdates true` does provide a safety net
|
||||
> against over-zealous rewinds, but it does not tell you "who". And
|
||||
> strangely, management does not seem to share the view that "blame" is just
|
||||
> a synonym for "annotate" ;-)]
|
||||
|
||||
The log lines look like this:
|
||||
|
||||
2009-09-19.10:24:37 + b4e76569659939 4fb16f2a88d8b5 myrepo refs/heads/master user2 refs/heads/master
|
||||
|
||||
The "+" at the start indicates a non-fast forward update, in this case from
|
||||
b4e76569659939 to 4fb16f2a88d8b5. So b4e76569659939 is the one to restore!
|
||||
Can it get easier?
|
||||
|
||||
The other parts of the log line are the name of the repo, the refname being
|
||||
updated, the user updating it, and the refex pattern (from the config file)
|
||||
that matched, in case you need to debug the config file itself.
|
||||
|
||||
<a name="_exclude_or_deny_rules"></a>
|
||||
|
||||
##### "exclude" (or "deny") rules
|
||||
|
||||
Here is an illustrative explanation of "deny" rules. However, please be sure
|
||||
to read the "DENY/EXCLUDE RULES" section in `conf/example.conf` for important
|
||||
notes/caveats before using "deny" rules.
|
||||
|
||||
Take a look at the following snippet, which *seems* to say that "bruce" can
|
||||
write versioned tags (anything containing `refs/tags/v[0-9]`), but the other
|
||||
staffers can't:
|
||||
|
||||
@staff = bruce whitfield martin
|
||||
[... and later ...]
|
||||
RW refs/tags/v[0-9] = bruce
|
||||
RW refs/tags = @staff
|
||||
|
||||
But that's not how the matching works. As long as any refex matches the
|
||||
refname being updated, it's a "yes". Since the second refex (which says
|
||||
"anything containing `refs/tags`") is a superset of the first one, it lets
|
||||
anyone on `@staff` create versioned tags, not just Bruce.
|
||||
|
||||
One way to fix this is to allow "excludes" -- some changes in syntax, combined
|
||||
with a rigorous, ordered, interpretation would do it.
|
||||
|
||||
Let's recap the **existing semantics**:
|
||||
|
||||
> the first matching refex that has the permission you're looking for (`W`
|
||||
> or `+`), results in success. A fallthrough results in failure
|
||||
|
||||
Here are the **new semantics**, with changes from the "main" one in bold:
|
||||
|
||||
> the first matching refex that has the permission you're looking for (`W`
|
||||
> or `+`) **or a minus (`-`)**, results in success **or failure,
|
||||
> respectively**. A fallthrough **also** results in failure
|
||||
|
||||
So the example we started with becomes, if you use "deny" rules:
|
||||
|
||||
RW refs/tags/v[0-9] = bruce
|
||||
- refs/tags/v[0-9] = @staff
|
||||
RW refs/tags = @staff
|
||||
|
||||
And here's how it works:
|
||||
|
||||
* for non-version tags, only the 3rd rule matches, so anyone on staff can
|
||||
push them
|
||||
* for version tags by bruce, the first rule matches so he can push them
|
||||
* for version tags by staffers *other than bruce*, the second rule matches
|
||||
before the third one, and it has a `-` as the permission, so the push
|
||||
fails
|
||||
|
||||
<a name="_separating_delete_and_rewind_rights"></a>
|
||||
|
||||
##### separating delete and rewind rights
|
||||
|
||||
Since the beginning, `RW+` meant being able to rewind *or* delete a ref. My
|
||||
stand is that these two are fairly similar, and infact a rewind is almost the
|
||||
same as a delete+push (the only difference I can see is if you had
|
||||
core.logAllRefUpdates set, which is *not* a default setting).
|
||||
|
||||
However, there seem to be cases where it is useful to distinguish them --
|
||||
situations where one of them should be restricted more than the other.
|
||||
([Arguments][sdrr] exist for both sides: restrict delete more than rewind, and
|
||||
vice versa).
|
||||
|
||||
So we now allow these two rights to be separated. Here's how:
|
||||
|
||||
* branch deletion is permitted by using `RWD` or `RW+D` -- essentially the
|
||||
current branch permissions with a `D` suffixed
|
||||
* if a repo has a rule containing such a `D`, all `RW+` permissions (for
|
||||
that repo) cease to permit deletion of the ref matched.
|
||||
|
||||
This provides the *greatest* backward compatibility, while also enabling the
|
||||
new semantics at the granularity of a repo, instead of the entire config.
|
||||
|
||||
Note 1: if you find that `RW+` no longer allows deletion but you can't see a
|
||||
`D` permission in the rules, remember that gitolite allows a repo config to be
|
||||
specified in multiple places for convenience, included delegated or included
|
||||
files. Be sure to search everywhere :)
|
||||
|
||||
Note 2: a quick way to make this the default for *all* your repos is:
|
||||
|
||||
repo @all
|
||||
RWD dummy-branch = foo
|
||||
|
||||
where foo can be either the administrator, or if you can ignore the warning
|
||||
message when you push, a non-existant user.
|
||||
|
||||
Note 3: you can combine this with the "create a branch" permissions described
|
||||
in the next section, as the example line in conf/example.conf shows.
|
||||
|
||||
<a name="_separating_create_and_push_rights"></a>
|
||||
|
||||
##### separating create and push rights
|
||||
|
||||
This feature is similar in spirit to the previous one, so please read that
|
||||
section for a general understanding.
|
||||
|
||||
Briefly:
|
||||
|
||||
* branch creation is permitted by using `RWC` or `RW+C` -- essentially the
|
||||
current branch permissions with a `C` suffixed
|
||||
* if a repo has a rule containing such a `C`, then the `RW` and `RW+`
|
||||
permissions (for that repo) no longer permit creation of the ref matched;
|
||||
they will only allow pushing to an existing ref
|
||||
|
||||
Note: you can combine this with the "delete a branch" permissions described in
|
||||
the previous section, as the example line in conf/example.conf shows.
|
||||
|
||||
<a name="_file_dir_NAME_based_restrictions"></a>
|
||||
|
||||
##### file/dir NAME based restrictions
|
||||
|
||||
In addition to branch-name based restrictions, gitolite also allows you to
|
||||
restrict what files or directories can be involved in changes being pushed.
|
||||
This basically uses `git diff --name-only` to obtain the list of files being
|
||||
changed, treating each filename as a "ref" to be matched.
|
||||
|
||||
Please see `conf/example.conf` for syntax and examples.
|
||||
|
||||
<a name="_delegating_parts_of_the_config_file"></a>
|
||||
|
||||
##### delegating parts of the config file
|
||||
|
||||
You can now split up the config file and delegate the authority to specify
|
||||
access control for their own pieces. See [delegation][] for details.
|
||||
|
||||
<a name="_convenience_features"></a>
|
||||
|
||||
#### convenience features
|
||||
|
||||
<a name="_what_repos_do_I_have_access_to_"></a>
|
||||
|
||||
##### what repos do I have access to?
|
||||
|
||||
Sometimes there are too many repos, maybe even named similarly, or with the
|
||||
potential for typos, confusion about hyphens/underscores or upper/lower case,
|
||||
etc. You'd just like a simple way to know what repos you have access to.
|
||||
|
||||
Gitolite provides two commands (`info` and `expand`) to help you find this
|
||||
information; please check [doc/report-output.mkd][repout] for details.
|
||||
|
||||
<a name="_including_config_lines_from_other_files"></a>
|
||||
|
||||
##### including config lines from other files
|
||||
|
||||
See the entry under "INCLUDE SOME OTHER FILE" in `conf/example.conf`.
|
||||
|
||||
<a name="_support_for_git_installed_outside_default_PATH"></a>
|
||||
|
||||
##### support for git installed outside default PATH
|
||||
|
||||
The normal solution is to add to the system default PATH somehow, either by
|
||||
munging `/etc/profile` or by enabling `PermitUserEnvironment` in
|
||||
`/etc/ssh/sshd_config` and then setting the PATH in `~/.ssh/.environment`.
|
||||
All these are security risks because they allow a lot more than just you and
|
||||
your git install :-)
|
||||
|
||||
And if you don't have root, you can't do this anyway.
|
||||
|
||||
The only solution till now has been to ask every client to set the config
|
||||
parameters `remote.<name>.receivepack` and `remote.<name>.uploadpack`. But
|
||||
telling *every* client to do so is a pain...
|
||||
|
||||
Gitolite lets you specify the directory in which git binaries are to be found,
|
||||
via a new variable (`$GIT_PATH`) in the "rc" file. If this variable is
|
||||
non-empty, it will be appended to the PATH environment variable before
|
||||
attempting to run git stuff.
|
||||
|
||||
Very easy, very simple, and completely transparent to the users :-)
|
||||
|
||||
**Note**: sometimes you have a system that already has an older "git"
|
||||
installed in one of the system PATHs, but you've installed a newer git in some
|
||||
non-standard location and want that picked up. Because of security reasons,
|
||||
gitolite will not prepend `GIT_PATH` to the PATH variable, so the older git
|
||||
comes first and it gets kinda frustrating!
|
||||
|
||||
Here's a simple workaround. Ignore the `GIT_PATH` variable, and directly set
|
||||
the full PATH in the rc file, like so:
|
||||
|
||||
$ENV{PATH} = "/home/sitaram/bin:$ENV{PATH}";
|
||||
|
||||
<a name="_personal_branches"></a>
|
||||
|
||||
##### "personal" branches
|
||||
|
||||
"personal" branches are great for corporate environments, where
|
||||
unauthenticated pull/clone is a no-no. Since a dev workstation cannot do
|
||||
authentication, even work shared just between 2 devs has to go *via* the
|
||||
server. This causes the same branch name clutter as in a centralised VCS,
|
||||
plus setting up permissions for this becomes a chore for the admin.
|
||||
|
||||
gitolite lets you define a "personal" or "scratch" namespace prefix for each
|
||||
developer (e.g., `refs/personal/<devname>/*`). Just add a line like:
|
||||
|
||||
RW+ personal/USER/ = @userlist
|
||||
|
||||
This means I (user "sitaram") can do anything to any branch whose name starts
|
||||
with `personal/sitaram/` assuming I'm in "userlist".
|
||||
|
||||
You can have any number of such lines with different prefixes (for example,
|
||||
using topic names instead of "personal") or even suffixes if you like. The
|
||||
important thing is that the "branch" name should contain `/USER/` (including
|
||||
the slashes). At runtime this will match whoever is the current user. Access
|
||||
is still determined by the right hand side of course.
|
||||
|
||||
<a name="_custom_hooks_and_custom_git_config"></a>
|
||||
|
||||
##### custom hooks and custom git config
|
||||
|
||||
You can specify hooks that you want to propagate to all repos, as well as
|
||||
per-repo "gitconfig" settings. Please see `doc/2-admin.mkd` and
|
||||
`conf/example.conf` for details.
|
||||
|
||||
<a name="_bypassing_gitolite"></a>
|
||||
|
||||
##### bypassing gitolite
|
||||
|
||||
Sometimes you'll need to access one of the gitolite-managed repos directly on
|
||||
the server, without going through gitolite. Reasons may be some automatic
|
||||
updates or some other ad hoc purposes you can dream up.
|
||||
|
||||
Cloning a gitolite-controlled repo is easy enough -- just use the full path
|
||||
(typically `~/repositories/reponame.git`) instead of just `reponame`, to
|
||||
compensate for gitolite not sitting in between and adding those things to the
|
||||
repo path.
|
||||
|
||||
But when you push, the update hook (which git will invoke anyway) will fail
|
||||
because it needs all sorts of access control info that it now doesn't have,
|
||||
because the push was invoked without going through gitolite.
|
||||
|
||||
In order to bypass the update hook, just set the `GL_BYPASS_UPDATE_HOOK`
|
||||
environment variable to "1" or something, export it, and push. I prefer not
|
||||
to set that variable permanently, preferring this mode instead:
|
||||
|
||||
GL_BYPASS_UPDATE_HOOK=1 git push
|
||||
|
||||
<a name="_INconvenience_features"></a>
|
||||
|
||||
#### INconvenience features
|
||||
|
||||
<a name="_deleting_a_repo"></a>
|
||||
|
||||
##### deleting a repo
|
||||
|
||||
By design, there is no code in gitolite to *delete* a repo if the repo was
|
||||
specified by name in the config file. (Wildcard repos *can* be deleted by the
|
||||
user; see [here][rmrepo] for details).
|
||||
|
||||
If you *do* want to permanently delete a *non*-wildcard repo, here's what you
|
||||
do:
|
||||
|
||||
* remove the repo from the gitolite-admin repo clone's `conf/gitolite.conf`
|
||||
file. "add" the change, commit, and push.
|
||||
|
||||
* *then* remove the repo from `~/repositories` on the server (or whatever
|
||||
you set `$GL_REPO_BASE` to in the `~/.gitolite.rc`)
|
||||
|
||||
<a name="_helping_with_gitweb"></a>
|
||||
|
||||
#### helping with gitweb
|
||||
|
||||
Although gitweb is a completely separate program, gitolite can do quite a
|
||||
lot to help you manage gitweb access as well; once the initial setup is
|
||||
complete, you can do it all from within the gitolite config file!
|
||||
|
||||
<a name="gwd"></a>
|
||||
|
||||
<a name="_easier_to_specify_gitweb_description_and_gitweb_daemon_access"></a>
|
||||
|
||||
##### easier to specify gitweb "description" and gitweb/daemon access
|
||||
|
||||
Please see [gwd] for details on how to do this if you've never done this
|
||||
before. This section is only about how gitolite makes it easy to specify
|
||||
different combinations of access for different sets of repos.
|
||||
|
||||
[gwd]: http://github.com/sitaramc/gitolite/blob/pu/doc/2-admin.mkd#gwd
|
||||
|
||||
Remember gitolite lets you specify the access control specs in bits and
|
||||
pieces, so you can keep all the daemon/gitweb access in one place, even if
|
||||
each repo has more specific branch-level access config specified elsewhere.
|
||||
Here's an example, using really short reponames because I'm lazy:
|
||||
|
||||
# maybe near the top of the file, for ease of access:
|
||||
|
||||
@only_web = r1 r2 r3
|
||||
@only_daemon = r4 r5 r6
|
||||
@web_and_daemon = r7 r8 r9
|
||||
|
||||
repo @only_web
|
||||
R = gitweb
|
||||
repo @only_daemon
|
||||
R = daemon
|
||||
repo @web_and_daemon
|
||||
R = gitweb
|
||||
R = daemon
|
||||
|
||||
# ...maybe much later in the file:
|
||||
|
||||
repo r1
|
||||
# normal developer access lists for r1 and its branches/tags in the
|
||||
# usual way
|
||||
|
||||
repo r2
|
||||
# ...and so on...
|
||||
|
||||
<a name="_easier_to_link_gitweb_authorisation_with_gitolite"></a>
|
||||
|
||||
##### easier to link gitweb authorisation with gitolite
|
||||
|
||||
Over and above whether a repo is even *shown* by gitweb, you may want to
|
||||
further restrict people, allowing them to view *only* those repos for which
|
||||
they have been given read access by gitolite.
|
||||
|
||||
This requires that:
|
||||
|
||||
* you have to have some sort of HTTP auth on your web server (out of my
|
||||
scope, sorry!)
|
||||
* the HTTP auth should use the same username (like "sitaram") as used in the
|
||||
gitolite config (for the corresponding user)
|
||||
|
||||
Normally a superuser sets up passwords for users using the "htpasswd" command,
|
||||
but this is an administrative chore.
|
||||
|
||||
Robin Smidsrød had the *great* idea that, since each user already has pubkey
|
||||
access to `git@server`, this gives us a very neat way of using gitolite to let
|
||||
the users *manage their own HTTP passwords*. Here's how:
|
||||
|
||||
* setup apache so that the htaccess file it looks for is owned by the "git"
|
||||
user
|
||||
* in the `~/.gitolite.rc` file, look for the variable `$HTPASSWD_FILE` and
|
||||
point it to this file
|
||||
* tell your users to type in `ssh git@server htpasswd` to set or change
|
||||
their HTTP passwords
|
||||
|
||||
Of course some other authentication method can be used (e.g. `mod_ldap`) as
|
||||
long as the usernames match.
|
||||
|
||||
Gitweb allows you to specify a subroutine to decide on access. We use that
|
||||
feature and tie it to gitolite. Configuration example can be found in
|
||||
`contrib/gitweb/`.
|
||||
|
||||
<a name="_advanced_features"></a>
|
||||
|
||||
#### advanced features
|
||||
|
||||
<a name="_repos_named_with_wildcards"></a>
|
||||
|
||||
##### repos named with wildcards
|
||||
|
||||
Please see `doc/wildcard-repositories.mkd` for all the details.
|
||||
|
||||
<a name="_admin_defined_commands"></a>
|
||||
|
||||
##### admin defined commands
|
||||
|
||||
This requires the wildcards feature to be enabled, but is then an extremely
|
||||
powerful feature. See `doc/admin-defined-commands.mkd`.
|
||||
|
||||
<a name="_access_control_for_external_commands"></a>
|
||||
|
||||
##### access control for external commands
|
||||
|
||||
Gitolite now has a mechanism for allowing access control for arbitrary
|
||||
external commands, as long as they are invoked via ssh and present a
|
||||
server-side command that contains enough information to make an access control
|
||||
decision.
|
||||
|
||||
Note that this is incompatible with giving people shell access as described in
|
||||
`doc/ssh-troubleshooting.mkd` -- people who have shell access are not
|
||||
subject to this mechanism (it wouldn't make sense to try and control someone
|
||||
who has shell access anyway).
|
||||
|
||||
In general, external commands require changes in one or both the config files;
|
||||
the sample files in `conf/` double as documentation, so you should look there
|
||||
for examples and usage.
|
||||
|
||||
Commands implemented so far are:
|
||||
|
||||
* rsync
|
||||
* svnserve (see next section for a brief description; this has been
|
||||
contributed by Simon and Vladimir)
|
||||
|
||||
<a name="_svnserve"></a>
|
||||
|
||||
###### svnserve
|
||||
|
||||
If you are transitioning from SVN to gitolite, and have a lot of users using
|
||||
public-key authentication with SVN, this feature may be useful to you. Once
|
||||
you migrate all users' public keys into gitolite, you can set the `$SVNSERVE`
|
||||
variable in `~/.gitolite.rc` to tie `svnserve` with gitolite's authentication
|
||||
system. Assuming you installed gitolite to the same user as the one you used
|
||||
for SVN, SVN connectivity will be retained, and users will be able to use
|
||||
both SVN and git using the same SSH configuration.
|
||||
|
||||
<a name="_design_choices"></a>
|
||||
|
||||
### design choices
|
||||
|
||||
<a name="_keeping_the_parser_and_the_access_control_separate"></a>
|
||||
|
||||
#### keeping the parser and the access control separate
|
||||
|
||||
There are two programs concerned with access control:
|
||||
|
||||
* `gl-auth-command`, the program that is run via `~/.ssh/authorized_keys`;
|
||||
this decides whether git should even be allowed to run (basic R/W/no
|
||||
access). (This one cannot decide on the branch-level access; it is not
|
||||
known at this point what branch is being accessed)
|
||||
* the update-hook on each repo, which decides the per-branch permissions
|
||||
|
||||
I have chosen to keep the relatively complex task of parsing the config file
|
||||
out of them to keep them simpler (and faster). So any changes to the config
|
||||
have to be first "compiled", and the access control programs use this
|
||||
"compiled" version of the config. (The compile step also refreshes
|
||||
`~/.ssh/authorized_keys`).
|
||||
|
||||
If you choose the "easy install" method, all this is quite transparent to you
|
||||
anyway. If you cannot use the easy install and must install manually, I have
|
||||
clear instructions on how to set it up.
|
||||
|
||||
[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd
|
||||
[sdrr]: http://groups.google.com/group/gitolite/browse_thread/thread/9f2b4358ce406d4c#
|
||||
[delegation]: http://github.com/sitaramc/gitolite/blob/pu/doc/delegation.mkd
|
||||
[rmrepo]: http://github.com/sitaramc/gitolite/blob/pu/doc/admin-defined-commands.mkd#rmrepo
|
164
doc/CHANGELOG
Normal file
164
doc/CHANGELOG
Normal file
|
@ -0,0 +1,164 @@
|
|||
Major changes to gitolite, master branch only, most recent first, no dates but
|
||||
the tags can help you position stuff approximately
|
||||
[NYD = not yet documented due to lack of time...]
|
||||
|
||||
- v1.5.6
|
||||
|
||||
- new method for passing usergroup info (minor backward compat breakage);
|
||||
see commit message and doc/big-config.mkd for more on this
|
||||
|
||||
- added "sudo" (adc)
|
||||
- added "gl-reflog" (adc) to get a fake reflog from the server!
|
||||
- added support for a post-repo-create hook called "gl-post-init"
|
||||
|
||||
- (BIG ONE!) SMART HTTP SUPPORT!
|
||||
|
||||
- @all can now include gitweb/daemon also (default, not include)
|
||||
- allow @groups in setperms
|
||||
- gitweb/daemon now work within setperms
|
||||
|
||||
- log elapsed time (optional)
|
||||
|
||||
- more than one wildcard may match a repo, plus it can also be matched by a
|
||||
normal repo line
|
||||
|
||||
- test suite has lots of new tests for the below
|
||||
- (big change) all combinations of wild repos and big configs, including
|
||||
daemon/gitweb/git-config settings, should work now!
|
||||
|
||||
- v1.5.5
|
||||
|
||||
- mirroring support
|
||||
|
||||
- setup_authkeys is now separate; can be called from outside also; useful
|
||||
for people who want to maintain ssh keys via LDAP or something, and not
|
||||
within gitolite
|
||||
|
||||
- (two months too late for towel day) gl-dont-panic!
|
||||
[replaces the old "gl-emergency-addkey" program. It does more (including
|
||||
recovering from a botched push, not just lost keys), is cleaner, and works
|
||||
for all install methods]
|
||||
|
||||
- document on how to create a mob branch
|
||||
|
||||
- info command now takes a parameter to limit output; this is mandatory if
|
||||
GL_BIG_CONFIG is on
|
||||
|
||||
- v1.5.4
|
||||
|
||||
- new RC variables: GL_NO_CREATE_REPOS and GL_NO_SETUP_AUTHKEYS (inspired by
|
||||
the specific needs that Fedora have, but made as generic as possible)
|
||||
- separating push branch rights from create branch rights changed to use the
|
||||
same mechanism as the (older) mechanism for separating rewind from delete
|
||||
|
||||
- v1.5.3
|
||||
|
||||
- log file format changed; minor backward compat breakage if you've been
|
||||
doing any automated log processing
|
||||
- some small but important doc updates
|
||||
- adc "fork" now much faster and more space-efficient (uses git clone -l)
|
||||
|
||||
- v1.5.2
|
||||
|
||||
- added test suite
|
||||
|
||||
- v1.5.1
|
||||
|
||||
- disallow creation of new refs if you want (while allowing push of the
|
||||
same)
|
||||
|
||||
- adc "able" command added to contrib
|
||||
- easy-install now takes a host-nickname parameter for convenience in
|
||||
installing more than one gitolite server
|
||||
- major doc revamp; contrib/autotoc added to make docs look nicer
|
||||
- eliminate the need to run gl-setup on data version change, thus hopefully
|
||||
obsoleting the upgrade note for v1.5 (just below).
|
||||
|
||||
- v1.5 -- IMPORTANT UPGRADE NOTES below
|
||||
|
||||
Upgrading to v1.5 from any version prior to v1.5 requires an extra step
|
||||
for people who installed gitolite using the "system install / user setup"
|
||||
method described in doc/0-INSTALL.mkd. For such installations, after the
|
||||
administrator has upgraded gitolite system-wide, each "gitolite host" user
|
||||
must run `gl-setup` once (this time without any arguments).
|
||||
|
||||
- "deny" rules should now work even in "big-config" due to previous change
|
||||
- proper rule sequencing (required major format change)
|
||||
|
||||
- allow usergroup info to be passed in from outside, say via LDAP; see
|
||||
doc/big-config.mkd for details
|
||||
- (new) big-config is now part of mainline (old one had bitrotted); see
|
||||
doc/big-config.mkd for details
|
||||
|
||||
- gl-system-install: help people simulate an RPM/DEB install by just running
|
||||
that commmand with appropriate arguments; see doc/0-INSTALL.mkd
|
||||
|
||||
- admin-defined commands; see doc/admin-defined-commands.mkd
|
||||
|
||||
- v1.4.2 (prep for major refactor on rights queries
|
||||
- v1.4.1 (security fix)
|
||||
|
||||
- REFUSE TO RUN ON SERVER GIT < 1.6.2 (do NOT upgrade gitolite to or beyond
|
||||
this point if you are unable to upgrade git itself to at least 1.6.2)
|
||||
|
||||
- "D" must be combined with RW or RW+ (warning: minor backward compat breakage)
|
||||
|
||||
- v1.4
|
||||
|
||||
- recurse through keydir for pubkeys
|
||||
- bypass update hook if GL_BYPASS_UPDATE_HOOK is available in ENV
|
||||
- new server-side program "gl-tool", subcommand "shell-add"
|
||||
- new "D" permission (makes RW+ no longer imply "D" if used)
|
||||
- @all for repos is now a true @all
|
||||
- allow setperms to specify @all
|
||||
- post-update hook and gl-setup should be dash compat now
|
||||
- workaround for a Data::Dumper crash; see 412a691
|
||||
- both hooks chain to "<hookname>.secondary" now
|
||||
|
||||
- new style personal branches (see 2456cc1 for advantages)
|
||||
|
||||
- v1.3
|
||||
|
||||
- easier to move repos into gitolite
|
||||
- pattern for expand is no longer anchored
|
||||
|
||||
- v1.2
|
||||
|
||||
- distro packaging support -- easy to install systemwide now
|
||||
|
||||
- v1.1
|
||||
|
||||
- contrib directory added
|
||||
- expand now lists non-wildcard repos also
|
||||
- refs also have groups now
|
||||
- allow admins to get "info" for other users
|
||||
|
||||
- wildrepos merged
|
||||
- getdesc and setdesc for wildrepos
|
||||
- htpasswd subcommand
|
||||
- access control for rsync
|
||||
|
||||
- v1.0
|
||||
|
||||
- sshkeys-lint program added, doc/6 revamped
|
||||
- @SHELL in config changed to $SHELL_USERS in rc
|
||||
- "include" mechanism
|
||||
- delegation now uses NAME/ instead of branches
|
||||
- PATH/ changed to NAME/
|
||||
|
||||
- @SHELL in config
|
||||
- use of @all for repos also (see doc for caveat)
|
||||
- config entries for repos
|
||||
|
||||
- deny rules (no more "rebel" branch!)
|
||||
- PATH/
|
||||
- specify gitweb owner
|
||||
|
||||
- v0.95
|
||||
- easy install can run from msysgit also
|
||||
- v0.90
|
||||
- allow admin defined hooks
|
||||
- specify gitweb desc
|
||||
- v0.85
|
||||
- emergency addkey program
|
||||
- v0.80
|
|
@ -276,3 +276,64 @@ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
242
doc/admin-defined-commands.mkd
Normal file
242
doc/admin-defined-commands.mkd
Normal file
|
@ -0,0 +1,242 @@
|
|||
# admin defined commands
|
||||
|
||||
**WARNING: Use this feature only if you really really know what you're doing.
|
||||
If you come back to me saying this feature caused a problem, I will only
|
||||
laugh. The worse the problem, the louder I will laugh. You won't actually
|
||||
hear me laughing, but you'll feel it in your bones, trust me!**
|
||||
|
||||
There may be other such **WARNING** sections below. **Read all of them**.
|
||||
|
||||
----
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_background">background</a>
|
||||
* <a href="#_setting_it_up">setting it up</a>
|
||||
* <a href="#_anatomy_of_a_command">anatomy of a command</a>
|
||||
* <a href="#_example_uses_and_sample_commands_in_contrib">example uses and sample commands in contrib</a>
|
||||
* <a href="#_fork">fork</a>
|
||||
* <a href="#_rmrepo">rmrepo</a>
|
||||
* <a href="#_enable_disable_push_access_temporarily">enable/disable push access temporarily</a>
|
||||
* <a href="#_bonus_restricted_admin">(bonus) restricted admin</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_background"></a>
|
||||
|
||||
### background
|
||||
|
||||
Gitolite was named to be short for "gitosis-lite". Someone now wants to turn
|
||||
it into a "github-lite" :-) and even had some code to start me off thinking.
|
||||
|
||||
Since my first impulse on being asked for a feature is to say no, I was
|
||||
casting about for a reason when he gave me one: he first made some noises
|
||||
about perl, then said something about rewriting it all in scheme. Nice... I
|
||||
resisted the urge to point him to [this][xkcd224], told him that's a great
|
||||
idea and he should go for it, mentally blessing him for letting me off the
|
||||
hook on coding it ;-) [Laziness][lazy] *is* the first virtue you know!
|
||||
|
||||
And that was that. For a couple of days.
|
||||
|
||||
Soon, though, I realised that there could be a pretty big bonus in this for
|
||||
tightly controlled setups, so I went and coded it all anyway. See the section
|
||||
on "restricted admin" for what's really exciting about this for *me*.
|
||||
|
||||
----
|
||||
|
||||
It may be a good idea to read [doc/wildcard-repositories.mkd][wild] before
|
||||
you continue here though, because most of the uses of this feature also need
|
||||
wildcard repos. (This also means you must set `$GL_WILDREPOS` to "1" in the
|
||||
rc file).
|
||||
|
||||
The wildcard repo feature is a way to create repositories matching a pattern
|
||||
(even if it as simple as `personal/CREATOR/.+`), and a way to specify two
|
||||
categories of permissions for each such user-created repo.
|
||||
|
||||
What we want now is more than that, as you'll see in the examples below. And
|
||||
I'm sure if you think of more uses you'll send them to me as "contrib"
|
||||
entries, right?
|
||||
|
||||
<a name="_setting_it_up"></a>
|
||||
|
||||
### setting it up
|
||||
|
||||
This can only be setup by someone who has shell access to the server. Edit
|
||||
the rc file and update the `$GL_ADC_PATH` variable to point to, say,
|
||||
`/home/git/bin/adc`. *Nothing happens unless this variable is set and
|
||||
pointing to a directory*. Then put in whatever such commands you create into
|
||||
that directory. If you have a command called "foo" in that directory, then a
|
||||
user can invoke it by saying:
|
||||
|
||||
ssh git@server foo argument list
|
||||
|
||||
**WARNING: When gitolite takes control, this directory is checked first, and
|
||||
if the requested command exists, it is executed. It is therefore quite easy
|
||||
to inadvertently *hide* some of the "official" commands (like "info",
|
||||
"expand", "setperms", etc., or worse, say "git-upload-pack"!) by creating
|
||||
executable files with those names in this directory. So don't do that -- you
|
||||
have been warned!**
|
||||
|
||||
<a name="_anatomy_of_a_command"></a>
|
||||
|
||||
### anatomy of a command
|
||||
|
||||
You can basically do whatever you want in such a command -- go wild! It's
|
||||
upto you to check the permissions of *each* repo that the user is manipulating
|
||||
using your command -- you can `rm -rf $GL_REPO_BASE_ABS` if you like and
|
||||
gitolite wouldn't stop you.
|
||||
|
||||
The current directory (`$PWD`) will be set to the `$HOME` of `git@server` (or
|
||||
whatever id you're using). It won't be any specific repo, it won't even be
|
||||
the base directory of all the repos.
|
||||
|
||||
Gitolite defines a few environment variables, as well as allows you to
|
||||
directly query the ownership and access permissions of any repository.
|
||||
|
||||
The environment variables available are:
|
||||
|
||||
* `GL_USER` -- the name of the user invoking the command
|
||||
* `GL_BINDIR` -- the directory containing all the binaries (in particular,
|
||||
`gitolite.pm`, which is all we really care about here)
|
||||
* `GL_REPO_BASE_ABS` -- the absolute path of the base directory containing
|
||||
all the repos
|
||||
|
||||
There are a few other variables also available but the above are the only ones
|
||||
you should rely on. Please treat any other variables you notice as being
|
||||
internal/undocumented/subject to change.
|
||||
|
||||
[Implementation note: some of the distro packagers don't seem to like
|
||||
`GL_BINDIR`. I have not tested this in those scenarios, but they probably put
|
||||
`gitolite.pm` somewhere in perl's lib path anyway, so it ought to work].
|
||||
|
||||
In addition, all the arguments of the command are also available to you, so
|
||||
you can define your own command syntaxes. Gitolite checks these arguments to
|
||||
make sure they fit a somewhat conservative pattern (see `$REPOPATT_PATT` in
|
||||
`src/gitolite.pm`), so take that into consideration when designing your
|
||||
commands and usage.
|
||||
|
||||
Finally, you can call gitolite to query ownership and permissions for the
|
||||
current user (which may not necessarily be the owner). This is done loosely
|
||||
as follows (don't use this exact code yet though):
|
||||
|
||||
perl -I$HOME/.gitolite/src -Mgitolite -e "cli_repo_rights('reponame')"
|
||||
|
||||
which will print two space-separated words, something like `_____R__W u1` or
|
||||
maybe `____@R_@W <gitolite>`. (The former is the response for a wildcard repo
|
||||
created by user `u1`, the latter is for a non-wildcard repo)
|
||||
|
||||
But that's cumbersome. There's a bash shell function called
|
||||
`get_rights_and_owner` in `contrib/adc/adc.common-functions` that is much more
|
||||
convenient. See any of the other samples for how to use it.
|
||||
|
||||
If you don't like this, roll your own. If you don't like bash, do the eqvt in
|
||||
your language of choice.
|
||||
|
||||
<a name="_example_uses_and_sample_commands_in_contrib"></a>
|
||||
|
||||
### example uses and sample commands in contrib
|
||||
|
||||
<a name="_fork"></a>
|
||||
|
||||
#### fork
|
||||
|
||||
A user would use the fork command like this:
|
||||
|
||||
ssh git@server fork from to
|
||||
|
||||
where "from" is a repo to which the user invoking the fork has "R" access, and
|
||||
"to" is a repo that does not yet exist and to which he has "C" access.
|
||||
|
||||
(Reminder: these access checks are done by the "fork" script, **not** within
|
||||
gitolite -- once again, you are responsible for making sure your scripts
|
||||
maintain the security of the system!)
|
||||
|
||||
Strictly speaking this command is not really needed. Even without all this
|
||||
"admin-defined commands" setup you could still do the following, purely from
|
||||
the client side:
|
||||
|
||||
git clone git@server:from
|
||||
cd from
|
||||
git remote add new git@server:to
|
||||
git push new refs/*:refs/*
|
||||
|
||||
or some such incantation.
|
||||
|
||||
<a name="rmrepo"></a>
|
||||
|
||||
<a name="_rmrepo"></a>
|
||||
|
||||
#### rmrepo
|
||||
|
||||
This is one thing that you really could not do before this setup was created.
|
||||
Use it like this:
|
||||
|
||||
ssh git@server rmrepo reponame
|
||||
|
||||
The script checks to make sure that the repo being deleted was *created* by
|
||||
the user invoking it.
|
||||
|
||||
<a name="_enable_disable_push_access_temporarily"></a>
|
||||
|
||||
#### enable/disable push access temporarily
|
||||
|
||||
If you want to disable push access to gitolite temporarily (maybe for
|
||||
maintenance), anyone with write access to the gitolite-admin repo can do this:
|
||||
|
||||
ssh git@server able dis @all # able dis ==> dis able
|
||||
|
||||
To re-enable after the maint work is done:
|
||||
|
||||
ssh git@server able en @all # able en ==> en able
|
||||
|
||||
You can also do this for one or more individual repos; in place of `@all`,
|
||||
just use a space separated list of reponames (exactly as they would appear in
|
||||
the config file). Wildcards are not supported; patches welcome ;-)
|
||||
|
||||
**NOTE: This needs a specific secondary update hook**. Creating a secondary
|
||||
update hook is described in the sections on "custom hooks" and "hook chaining"
|
||||
in doc/2. You need code like this in `update.secondary` (don't forget to
|
||||
`chmod +x` the file):
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
for f in $HOME/.gitolite.down $PWD/.gitolite.down
|
||||
do
|
||||
if [ -f $f ]
|
||||
then
|
||||
echo >&2
|
||||
echo '*** ABORT ***' >&2
|
||||
echo >&2
|
||||
cat $f >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
|
||||
<a name="_bonus_restricted_admin"></a>
|
||||
|
||||
#### (bonus) restricted admin
|
||||
|
||||
It's rather important to me (and presumably others in the "corporate" world)
|
||||
to separate permission to push to the "gitolite-admin" repo from unrestricted
|
||||
shell access to the server. This issue has been visited often in the past.
|
||||
|
||||
Until now, though, this was binary -- you either had full shell access or none
|
||||
at all. If there were tasks that legitimately needed to be done from the
|
||||
shell on the server, it often meant you had to break that separation or load
|
||||
the few people who did have shell access already.
|
||||
|
||||
Now, however, it is possible to provide scripts to do what you want, and put
|
||||
them in `$GL_ADC_PATH`. `contrib/adc/restrict-admin` is a commented sample --
|
||||
as you can see, it cleverly makes use of the fact that you can now check for
|
||||
the invoking uses access to any repo in the system. In this case it checks if
|
||||
he has "W" access to the gitolite-admin repo, and if he does, allows the
|
||||
script to proceed.
|
||||
|
||||
[Note that this particular use does not require `$GL_WILDREPOS` to be enabled,
|
||||
because it's not using any wildcard repos].
|
||||
|
||||
[xkcd224]: http://xkcd.com/224/
|
||||
[lazy]: http://c2.com/cgi/wiki?LazinessImpatienceHubris
|
||||
[wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/wildcard-repositories.mkd
|
249
doc/big-config.mkd
Normal file
249
doc/big-config.mkd
Normal file
|
@ -0,0 +1,249 @@
|
|||
# what is a "big-config"
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_when_why_do_we_need_it_">when/why do we need it?</a>
|
||||
* <a href="#_how_do_we_use_it_">how do we use it?</a>
|
||||
* <a href="#_other_optimisations">other optimisations</a>
|
||||
* <a href="#_what_are_the_downsides_">what are the downsides?</a>
|
||||
* <a href="#_storing_usergroup_information_outside_gitolite_like_in_LDAP_">storing usergroup information outside gitolite (like in LDAP)</a>
|
||||
* <a href="#_why">why</a>
|
||||
* <a href="#_how">how</a>
|
||||
|
||||
<a name="_when_why_do_we_need_it_"></a>
|
||||
|
||||
### when/why do we need it?
|
||||
|
||||
A "big config" is anything that has a few thousand users and a few thousand
|
||||
repos, organised into groups that are much smaller in number (like maybe a few
|
||||
hundreds of repogroups and a few dozens of usergroups).
|
||||
|
||||
So let's say you have
|
||||
|
||||
@wbr = lynx firefox
|
||||
@devs = alice bob
|
||||
|
||||
repo @wbr
|
||||
RW+ next = @devs
|
||||
RW master = @devs
|
||||
|
||||
Gitolite internally translates this to
|
||||
|
||||
repo lynx firefox
|
||||
RW+ next = alice bob
|
||||
RW master = alice bob
|
||||
|
||||
Not just that -- it now generates the actual config rules once for each
|
||||
user-repo-ref combination (there are 8 combinations above; the compiled config
|
||||
file looks partly like this:
|
||||
|
||||
%repos = (
|
||||
'firefox' => {
|
||||
'R' => {
|
||||
'alice' => 1,
|
||||
'bob' => 1
|
||||
},
|
||||
'W' => {
|
||||
'alice' => 1,
|
||||
'bob' => 1
|
||||
},
|
||||
'alice' => [
|
||||
{
|
||||
'refs/heads/next' => 'RW+'
|
||||
},
|
||||
{
|
||||
'refs/heads/master' => 'RW'
|
||||
}
|
||||
],
|
||||
'bob' => [
|
||||
{
|
||||
'refs/heads/next' => 'RW+'
|
||||
},
|
||||
{
|
||||
'refs/heads/master' => 'RW'
|
||||
}
|
||||
]
|
||||
},
|
||||
'lynx' => {
|
||||
'R' => {
|
||||
'alice' => 1,
|
||||
'bob' => 1
|
||||
},
|
||||
'W' => {
|
||||
'alice' => 1,
|
||||
'bob' => 1
|
||||
},
|
||||
'alice' => [
|
||||
{
|
||||
'refs/heads/next' => 'RW+'
|
||||
},
|
||||
{
|
||||
'refs/heads/master' => 'RW'
|
||||
}
|
||||
],
|
||||
'bob' => [
|
||||
{
|
||||
'refs/heads/next' => 'RW+'
|
||||
},
|
||||
{
|
||||
'refs/heads/master' => 'RW'
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
Phew!
|
||||
|
||||
You can imagine what that does when you have 10,000 users and 10,000 repos.
|
||||
Let's just say it's not pretty :)
|
||||
|
||||
<a name="_how_do_we_use_it_"></a>
|
||||
|
||||
### how do we use it?
|
||||
|
||||
Now, if you had all those 10,000 users and repos explicitly listed (no
|
||||
groups), then there is no help. But if, like the above example, you had
|
||||
groups like we used above, there is hope.
|
||||
|
||||
Just set
|
||||
|
||||
$GL_BIG_CONFIG = 1;
|
||||
|
||||
in the `~/.gitolite.rc` file on the server (see next section for more
|
||||
variables). When you do that, and push this configuration, the compiled file
|
||||
looks like this:
|
||||
|
||||
%repos = (
|
||||
'@wbr' => {
|
||||
'@devs' => [
|
||||
{
|
||||
'refs/heads/next' => 'RW+'
|
||||
},
|
||||
{
|
||||
'refs/heads/master' => 'RW'
|
||||
}
|
||||
],
|
||||
'R' => {
|
||||
'@devs' => 1
|
||||
},
|
||||
'W' => {
|
||||
'@devs' => 1
|
||||
}
|
||||
},
|
||||
);
|
||||
%groups = (
|
||||
'@devs' => {
|
||||
'alice' => 'master',
|
||||
'bob' => 'master'
|
||||
},
|
||||
'@wbr' => {
|
||||
'firefox' => 'master',
|
||||
'lynx' => 'master'
|
||||
}
|
||||
);
|
||||
|
||||
That's a lot smaller, and allows orders of magintude more repos and groups to
|
||||
be supported.
|
||||
|
||||
<a name="_other_optimisations"></a>
|
||||
|
||||
### other optimisations
|
||||
|
||||
The default RC file contains the following lines (we've already discussed the
|
||||
first one):
|
||||
|
||||
$GL_BIG_CONFIG = 0;
|
||||
$GL_NO_DAEMON_NO_GITWEB = 0;
|
||||
$GL_NO_CREATE_REPOS = 0;
|
||||
$GL_NO_SETUP_AUTHKEYS = 0;
|
||||
|
||||
`GL_NO_DAEMON_NO_GITWEB` is a very useful optimisation that you *must* enable
|
||||
if you *do* have a large number of repositories, and do *not* use gitolite's
|
||||
support for gitweb or git-daemon access (see "[easier to specify gitweb
|
||||
description and gitweb/daemon access][gwd]" for details). This will save a
|
||||
lot of time when you push the gitolite-admin repo with changes. This variable
|
||||
also control whether "git config" lines (such as `config hooks.emailprefix =
|
||||
"[gitolite]"`) will be processed or not.
|
||||
|
||||
Setting this is relatively harmless to a normal installation, unlike the next
|
||||
two variables :-) `GL_NO_CREATE_REPOS` and `GL_NO_SETUP_AUTHKEYS` are meant
|
||||
for installations where some backend system already exists that does all the
|
||||
actual repo creation, and all the authentication setup (ssh auth keys),
|
||||
respectively.
|
||||
|
||||
Summary: Please **leave those two variables alone** unless you're initials are
|
||||
"JK" ;-)
|
||||
|
||||
Also note that using all 3 of the `GL_NO_*` variables will result in
|
||||
*everything* after the config compile being skipped. In other words, gitolite
|
||||
is being used **only** for its access control language.
|
||||
|
||||
<a name="_what_are_the_downsides_"></a>
|
||||
|
||||
### what are the downsides?
|
||||
|
||||
There is one minor issue.
|
||||
|
||||
If you use the delegation feature, you can no longer define or extend
|
||||
@groups in a fragment, for security reasons. It will also not let you use any
|
||||
group other than the @fragname itself (specifically, groups which contained a
|
||||
subset of the allowed @fragname, which would work normally, do not work now).
|
||||
|
||||
(If you didn't understand all that, you're probably not using delegation, so
|
||||
feel free to ignore it!)
|
||||
|
||||
<a name="_storing_usergroup_information_outside_gitolite_like_in_LDAP_"></a>
|
||||
|
||||
### storing usergroup information outside gitolite (like in LDAP)
|
||||
|
||||
[Please NOTE: this is all about *user* groups, not *repo* groups]
|
||||
|
||||
[WARNING: the earlier method of doing this has been discontinued; please see
|
||||
the commit message for details]
|
||||
|
||||
Gitolite now allows usergroup information to be stored outside its own config
|
||||
file. We'll see "why" first, then the "how".
|
||||
|
||||
<a name="_why"></a>
|
||||
|
||||
#### why
|
||||
|
||||
Large sites often have LDAP servers that already contain user and group
|
||||
information, including group membership details. Such sites may prefer that
|
||||
gitolite just pick up that info instead of having to redundantly put it in
|
||||
gitolite's config file.
|
||||
|
||||
Consider this example config for one repo:
|
||||
|
||||
repo foo
|
||||
RW+ = @lead_devs
|
||||
RW = @devs
|
||||
R = @interns
|
||||
|
||||
Normally, you would also need to specify:
|
||||
|
||||
@lead_devs = dilbert alice
|
||||
@devs = wally
|
||||
@interns = ashok
|
||||
|
||||
However, if the corporate LDAP server already tags these people correctly, and
|
||||
if there is some way of getting that information out **at run time**, that
|
||||
would be cool.
|
||||
|
||||
<a name="_how"></a>
|
||||
|
||||
#### how
|
||||
|
||||
All you need is a script that, given a username, queries your LDAP or similar
|
||||
server, and returns a space-separated list of all the groups she is a member
|
||||
of. If an invalid user name is sent in, or the user is valid but is not part
|
||||
of any groups, it should print nothing.
|
||||
|
||||
This script will probably be specific to your site. [**Help wanted**: I don't
|
||||
know LDAP, so if someone wants to contribute some sample code I'd be happy to
|
||||
put it in contrib/, with credit of course!]
|
||||
|
||||
Then set the `$GL_GET_MEMBERSHIPS_PGM` variable in the rc file to the full
|
||||
path to this program, set `$GL_BIG_CONFIG` to 1, and that will be that.
|
||||
|
||||
[gwd]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#gwd
|
136
doc/delegation.mkd
Normal file
136
doc/delegation.mkd
Normal file
|
@ -0,0 +1,136 @@
|
|||
# delegating access control responsibilities
|
||||
|
||||
[Thanks to jeromeag for forcing me to think through this...]
|
||||
|
||||
----
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_lots_of_repos_lots_of_users">lots of repos, lots of users</a>
|
||||
* <a href="#_splitting_up_the_set_of_repos_into_groups">splitting up the set of repos into groups</a>
|
||||
* <a href="#_delegating_ownership_of_groups_of_repos">delegating ownership of groups of repos</a>
|
||||
* <a href="#_security_philosophy_note">security/philosophy note</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_lots_of_repos_lots_of_users"></a>
|
||||
|
||||
### lots of repos, lots of users
|
||||
|
||||
Gitolite tries to make it easy to manage access to lots of users and repos,
|
||||
exploiting commonalities wherever possible. It lets you specify bits and
|
||||
pieces of the access control separately -- i.e., *all* the access specs for a
|
||||
certain repo need not be together; they can be scattered, which makes it
|
||||
easier to manage the sort of slice and dice needed in that example.
|
||||
|
||||
But eventually the config file will become too big. If you let only one
|
||||
person have control, he could become a bottleneck. If you give it to multiple
|
||||
people, they might make mistakes or stomp on each others' work accidentally.
|
||||
|
||||
The best way is to divide up the config file and give parts of it to different
|
||||
people.
|
||||
|
||||
Ideally, we would delegate authority for *groups* of repos, not individual
|
||||
repos, otherwise it doesn't scale. It would also be nice if we could prevent
|
||||
an admin from creating access rules for *any* repo in the system -- i.e., set
|
||||
limits on what repos he can control. This would be a nice "security" feature.
|
||||
|
||||
Delegation offers a way to do all that. Note that delegated admins cannot
|
||||
create or remove users, not can they define new repos. They can only define
|
||||
access control rules for a set of repos they have been given authority for.
|
||||
|
||||
----
|
||||
|
||||
It's easier to show how it all works with an example instead of long
|
||||
descriptions.
|
||||
|
||||
<a name="_splitting_up_the_set_of_repos_into_groups"></a>
|
||||
|
||||
### splitting up the set of repos into groups
|
||||
|
||||
To start with, recall that gitolite allows you to specify **groups** (of users
|
||||
or repos, same syntax). So the basic idea is that the main config file
|
||||
(`conf/gitolite.conf` in your admin repo clone) will specify some repo groups:
|
||||
|
||||
# group your projects/repos however you want
|
||||
@webbrowser_repos = firefox lynx
|
||||
@webserver_repos = apache nginx
|
||||
@malware_repos = conficker storm
|
||||
|
||||
# any other config as usual, including access control lines for any of the
|
||||
# above projects or groups
|
||||
|
||||
<a name="_delegating_ownership_of_groups_of_repos"></a>
|
||||
|
||||
### delegating ownership of groups of repos
|
||||
|
||||
Once the repos are grouped, give each person charge of one or more groups.
|
||||
For example, Alice may be in charge of all web browser development projects,
|
||||
Bob takes care of web servers, and Mallory, as [tradition][abe] dictates, is
|
||||
in charge of malware ;-)
|
||||
|
||||
[abe]: http://en.wikipedia.org/wiki/Alice_and_Bob#List_of_characters
|
||||
|
||||
You do this by adding branches to the `gitolite-admin` repo:
|
||||
|
||||
# the admin repo access was probably like this to start with:
|
||||
repo gitolite-admin
|
||||
RW+ = sitaram
|
||||
# now add these lines to the config for the admin repo
|
||||
RW = alice bob mallory
|
||||
RW+ NAME/ = sitaram
|
||||
RW NAME/conf/fragments/webbrowser_repos = alice
|
||||
RW NAME/conf/fragments/webserver_repos = bob
|
||||
RW NAME/conf/fragments/malware_repos = mallory
|
||||
|
||||
This uses gitolite's ability to restrict pushes by file/dir name being changed
|
||||
-- the syntax you see above ensures that, while "sitaram" does not have any
|
||||
NAME based restrictions, the other 3 users do. See `conf/example.conf` for
|
||||
syntax and notes.
|
||||
|
||||
As you can see, **for each repo group** you want to delegate authority over,
|
||||
there's a rule for a **corresponding file** in `conf/fragments` in the
|
||||
`gitolite-admin` repo. If you have write access to that file, you are allowed
|
||||
to define rules for repos in that repo group.
|
||||
|
||||
In other words, we use gitolite's file/dir NAME-based permissions to "enforce"
|
||||
the separation between the delegated configs!
|
||||
|
||||
Here's how to use this in practice:
|
||||
|
||||
* Alice clones the `gitolite-admin` repo, and adds a file called
|
||||
`conf/fragments/webbrowser_repos.conf`
|
||||
|
||||
* she writes in this file any access control rules for the "firefox" and
|
||||
"lynx" repos. She should not write access rules for any other project --
|
||||
they will be ignored
|
||||
|
||||
* Alice then commits and pushes to the `gitolite-admin` repo
|
||||
|
||||
Naturally, a successful push invokes the post-update hook that the admin repo
|
||||
has, which eventually runs the compile script. The **net effect** is as if
|
||||
you appended the contents of all the "fragment" files, in alphabetical order,
|
||||
to the bottom of the main file.
|
||||
|
||||
----
|
||||
|
||||
<a name="_security_philosophy_note"></a>
|
||||
|
||||
### security/philosophy note
|
||||
|
||||
The delegation feature is meant only for access control rules, not pubkeys.
|
||||
Adding/removing pubkeys is a much more significant event than changing branch
|
||||
level permissions for people already on staff, and only the main admin should
|
||||
be allowed to do it.
|
||||
|
||||
Gitolite's "userids" all live in the same namespace. This is unlikely to
|
||||
change, so please don't ask -- it gets real complicated to do otherwise.
|
||||
Allowing delegated admins to add users means username collisions, which also
|
||||
means security problems (admin-A creates a pubkey for Admin-B, thus gaining
|
||||
access to all of Admin-B's stuff).
|
||||
|
||||
If you feel the need to delegate even that, please just go the whole hog and
|
||||
give them separate gitolite instances! It's pretty easy to setup the
|
||||
*software* itself system-wide, so that many users can use it without all the
|
||||
"easy install" fuss. See the "system install / user setup" section in
|
||||
doc/1-INSTALL.mkd for details.
|
154
doc/gitolite-and-ssh.mkd
Normal file
154
doc/gitolite-and-ssh.mkd
Normal file
|
@ -0,0 +1,154 @@
|
|||
# how gitolite uses ssh
|
||||
|
||||
***Gitolite is heavily dependent on ssh***!
|
||||
|
||||
Most people didn't realise this, and even if they did they didn't know ssh
|
||||
well enough to help themselves. If you don't understand how ssh public key
|
||||
authentication works, or how the `~/.ssh/authorized_keys` file can be used to
|
||||
restrict users, etc., you will have endless amounts of trouble getting
|
||||
gitolite to work, because you'll be attacking the wrong problem.
|
||||
|
||||
So please please please understand this before tearing your hair out and
|
||||
blaming ***git/gitolite*** for whatever is going wrong with your setup :-)
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_ssh_basics">ssh basics</a>
|
||||
* <a href="#_how_does_gitolite_use_all_this_ssh_magic_">how does gitolite use all this ssh magic?</a>
|
||||
* <a href="#_restricting_shell_access_distinguishing_one_user_from_another">restricting shell access/distinguishing one user from another</a>
|
||||
* <a href="#_restricting_branch_level_actions">restricting branch level actions</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_ssh_basics"></a>
|
||||
|
||||
### ssh basics
|
||||
|
||||
Let's start with some basics, focusing *only* on the pieces relevant to
|
||||
`gitolite`. If this is not detailed enough, please use google and learn more
|
||||
from somewhere, or maybe buy the OReilly ssh book.
|
||||
|
||||
* You can login to an ssh server by typing a password, but ssh can also use
|
||||
***public-private keys*** (also called "key pairs") for authentication.
|
||||
`gitolite` *requires* you to use this mechanism for your users -- they
|
||||
cannot log in using passwords. Hopefully by the time you finish reading
|
||||
this document you will understand why :-)
|
||||
|
||||
The way you set this up is you generate a key pair on your workstation,
|
||||
and give the server the public key. (I need not add that the "private"
|
||||
key must be, well, kept *private*!)
|
||||
|
||||
* **generating a key pair on your workstation** is done by running the
|
||||
command `ssh-keygen -t rsa`. This produces two files in `~/.ssh`. One is
|
||||
`id_rsa`; this is the **private** key -- ***never*** let it out of your
|
||||
machine. The other is `id_rsa.pub`, which is the corresponding public
|
||||
key. This public key is usually just one long line of text.
|
||||
|
||||
* on Windows machines with msysgit installed, you should do this from
|
||||
within a "git bash" window. The command will report the full path where
|
||||
the files have been written; make a note of this, and use those files in
|
||||
any of the description that follows
|
||||
|
||||
* **adding your public key to the server**'s `~/.ssh/authorized_keys`
|
||||
file is how ssh uses pubkeys to authenticate users. Let's say
|
||||
sita@work.station is trying to log in as git@serv.er. What you have to do
|
||||
is take the `~/.ssh/id_rsa.pub` file for user sita on work.station and
|
||||
append its contents (remember it's only one line) to
|
||||
`~/.ssh/authorized_keys` for user git on serv.er.
|
||||
|
||||
The `authorized_keys` file can have multiple public keys (from many
|
||||
different people) added to it so any of them can log in to git@serv.er.
|
||||
|
||||
In the normal case (not gitolite, but your normal everyday shell access),
|
||||
there's a command that does this, `ssh-copy-id`, which also fixes up
|
||||
permissions etc., as needed, since sshd is a little picky about allowing
|
||||
pubkey access if permissions on the server are loose. Or you can do it
|
||||
manually, as long as you know what you're doing and you're careful not to
|
||||
erase or overwrite the existing contents of `~/.ssh/authorized_keys` on
|
||||
the server!
|
||||
|
||||
But in the gitolite case, it's different; we'll get to that in a minute.
|
||||
|
||||
* **troubleshooting pubkey authentication failures**: if you are unable to
|
||||
get ssh access to the server after doing all this, you'll have to look
|
||||
in `/var/log/secure` or `/var/log/auth.log` or some such file on the
|
||||
server to see what specific error `sshd` is complaining about.
|
||||
|
||||
* **restricting users to specific commands** is very important for gitolite.
|
||||
If you read `man sshd` and look for `authorized_keys file format`, you'll
|
||||
see a lot of options you can add to the public key line, which restrict
|
||||
the incoming user in various ways. In particular, note the `command=`
|
||||
option, which means "regardless of what the incoming user is asking to do,
|
||||
forcibly run this command instead".
|
||||
|
||||
Also note that when there are many public keys (i.e., lines) in the
|
||||
`authorized_keys` file, each line can have a *different* set of options
|
||||
and `command=` values.
|
||||
|
||||
**This is the backbone of what makes gitolite work; please make sure you
|
||||
understand this**
|
||||
|
||||
<a name="_how_does_gitolite_use_all_this_ssh_magic_"></a>
|
||||
|
||||
### how does gitolite use all this ssh magic?
|
||||
|
||||
These are two different questions you ought to be having by now:
|
||||
|
||||
* how does it distinguish between me and someone else, since we're all
|
||||
logging in as the same remote user "git"
|
||||
* how does it restrict what I can do within a repository
|
||||
|
||||
<a name="_restricting_shell_access_distinguishing_one_user_from_another"></a>
|
||||
|
||||
#### restricting shell access/distinguishing one user from another
|
||||
|
||||
The answer to the first question is the `command=` we talked about before. If
|
||||
you look in the `authorized_keys` file, you'll see entries like this (I chopped
|
||||
off the ends of course; they're pretty long lines):
|
||||
|
||||
command="[path]/gl-auth-command sitaram",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA18S2t...
|
||||
command="[path]/gl-auth-command usertwo",[more options] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArXtCT...
|
||||
|
||||
First, it finds out which of the public keys in this file match the incoming
|
||||
login. That's crypto stuff, and I won't go into it. Once the match has been
|
||||
found, it will run the command given on that line; e.g., if I logged in, it
|
||||
would run `[path]/gl-auth-command sitaram`. So the first thing to note is
|
||||
that such users do not get "shell access", which is good!
|
||||
|
||||
Before running the command, however, sshd sets up an environment variable
|
||||
called `SSH_ORIGINAL_COMMAND` which contains the actual git command that your
|
||||
workstation sent out. This is the command that *would have run* if you did
|
||||
not have the `command=` part in the authorised keys file.
|
||||
|
||||
When `gl-auth-command` gets control, it looks at the first argument
|
||||
("sitaram", "usertwo", etc) to determine who you are. It then looks at the
|
||||
`SSH_ORIGINAL_COMMAND` variable to find out which repository you want to
|
||||
access, and whether you're reading or writing.
|
||||
|
||||
Now is has user, repository, and access requested (read/write), gitolite looks
|
||||
at its config file, and either allows or rejects the request.
|
||||
|
||||
But this cannot differentiate between different branches within a repo; that
|
||||
has to be done separately.
|
||||
|
||||
<a name="_restricting_branch_level_actions"></a>
|
||||
|
||||
#### restricting branch level actions
|
||||
|
||||
[If you look inside the git source tree, there's a file among the "howto"s in
|
||||
there called `update-hook-example.txt`, which was the inspiration for this
|
||||
part of gitolite.]
|
||||
|
||||
Git allows you to specify many "hooks", which get control as various events
|
||||
happen -- see `git help hooks` for details. One of those hooks is the
|
||||
`update` hook, which, if it is present, is invoked just before a branch or a
|
||||
tag is about to be updated. The hook is passed the name of the branch or tag,
|
||||
the old SHA1 value, and the new SHA1 value, as arguments. Hooks that are
|
||||
called *before* an action happens are allowed to prevent that action from
|
||||
happening y returning an error code.
|
||||
|
||||
When gitolite is told to create a new repository (by the admin), it installs
|
||||
a special update hook. This hook takes all the information presented, looks
|
||||
at the config file, and decides to allow or reject the update.
|
||||
|
||||
And that's basically it.
|
175
doc/hook-propagation.mkd
Normal file
175
doc/hook-propagation.mkd
Normal file
|
@ -0,0 +1,175 @@
|
|||
# hook propagation in gitolite
|
||||
|
||||
Advanced users need to know how hooks propagate, and when. They also need to
|
||||
know where to place their hooks, and since there appear to be two places to
|
||||
put them, what takes precedence. I'll try and set out the logic here.
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_hooks_used_by_gitolite">hooks used by gitolite</a>
|
||||
* <a href="#_where_do_I_the_admin_put_the_hooks_">**where** do I (the admin) put the hooks?</a>
|
||||
* <a href="#_the_from_client_method">the "from-client" method</a>
|
||||
* <a href="#_the_other_3_methods">the other 3 methods</a>
|
||||
* <a href="#_the_GL_PACKAGE_HOOKS_directory">the `GL_PACKAGE_HOOKS` directory</a>
|
||||
* <a href="#_the_HOME_gitolite_directory">the `$HOME/.gitolite` directory</a>
|
||||
* <a href="#_why_two_places_">why two places?</a>
|
||||
* <a href="#_special_case_the_non_root_method">special case: the "non-root" method</a>
|
||||
* <a href="#_when_do_hooks_propagate_">**when** do hooks propagate?</a>
|
||||
|
||||
<a name="_hooks_used_by_gitolite"></a>
|
||||
|
||||
### hooks used by gitolite
|
||||
|
||||
Gitolite uses only 2 hooks. **All** repos have an `update` hook, without
|
||||
which there is no write-level access control (per-branch permissions). The
|
||||
special **gitolite-admin** repo has a special `post-update` hook, which is
|
||||
required to do its, umm, special things, like running the "compile" script,
|
||||
etc.
|
||||
|
||||
In addition there is a "sentinel file" -- an empty file called
|
||||
"gitolite-hooked". We'll see later what this does.
|
||||
|
||||
The final objective of all this is that each repo's `hooks/` directory should
|
||||
get all the hooks that it is meant to get.
|
||||
|
||||
<a name="_where_do_I_the_admin_put_the_hooks_"></a>
|
||||
|
||||
### **where** do I (the admin) put the hooks?
|
||||
|
||||
In general, **all** hooks go into the `hooks/common` directory. Only the
|
||||
special `post-update` hook meant for the admin repo goes into
|
||||
`hooks/gitolite-admin`.
|
||||
|
||||
Now we'll discuss the locations of these `hooks/common` and
|
||||
`hooks/gitolite-admin` directories. This depends on which install method you
|
||||
used.
|
||||
|
||||
(Please refer to [doc/1-INSTALL.mkd][0inst] for what these "methods" are).
|
||||
|
||||
<a name="_the_from_client_method"></a>
|
||||
|
||||
#### the "from-client" method
|
||||
|
||||
Let's get this out of the way first, because it is simple: if you're using the
|
||||
"from-client" method, there's only one place: the `hooks` directory in your
|
||||
gitolite clone on the client side. This is where you run
|
||||
`src/gl-easy-install` from. Nothing else in this section is relevant to this
|
||||
method; skip to the next section ("when do hooks propagate") if you installed
|
||||
using the "from-client" method.
|
||||
|
||||
<a name="_the_other_3_methods"></a>
|
||||
|
||||
#### the other 3 methods
|
||||
|
||||
<a name="_the_GL_PACKAGE_HOOKS_directory"></a>
|
||||
|
||||
##### the `GL_PACKAGE_HOOKS` directory
|
||||
|
||||
You might recall that the "root", and "non-root" methods run a command called
|
||||
`gl-system-install`, the third argument of which is some directory of your
|
||||
choice (like maybe `/usr/share/gitolite/hooks`). Even though it is not
|
||||
necessary to know this, internally this becomes the value of the
|
||||
`$GL_PACKAGE_HOOKS` variable, so in this document we will refer to that
|
||||
variable instead of the location (because you might choose any location you
|
||||
like for it).
|
||||
|
||||
The "package" method also has the same property, except that the packager has
|
||||
already decided what that location is, and the package creation/install
|
||||
process does the equivalent of `gl-system-install`.
|
||||
|
||||
So now we know there's a location called `$GL_PACKAGE_HOOKS` where you can
|
||||
place your hooks.
|
||||
|
||||
<a name="_the_HOME_gitolite_directory"></a>
|
||||
|
||||
##### the `$HOME/.gitolite` directory
|
||||
|
||||
You might also recall that, in these three methods, each **hosting user** has
|
||||
to run `gl-setup`. This sets up, among other things, `$HOME/.gitolite`
|
||||
directory, which also contains a `hooks/` directory.
|
||||
|
||||
So now there are two places you can put your hooks, apparently.
|
||||
|
||||
<a name="_why_two_places_"></a>
|
||||
|
||||
#### why two places?
|
||||
|
||||
Just think of the "package" and "root" methods for now, even if you're using
|
||||
the "non-root" method.
|
||||
|
||||
In these two methods, it is reasonable to assume that the entire site (or
|
||||
server) has certain policies that they want to implement using hooks. They
|
||||
want to enforce these hooks on *each hosting user*. These hooks go into
|
||||
`$GL_PACKAGE_HOOKS`.
|
||||
|
||||
Each hosting user then has the discretion to add his own hooks (modulo name
|
||||
clashes, which may necessitate hook chaining, etc., like we already do for the
|
||||
hooks that gitolite cares about). He adds these hooks to his
|
||||
`$HOME/.gitolite/hooks` directory.
|
||||
|
||||
When hooks propagate, the ones in `$GL_PACKAGE_HOOKS` override/overwrite the
|
||||
ones in `$HOME/.gitolite/hooks`. Otherwise it wouldn't make sense; you
|
||||
wouldn't be able to enforce site-wide hooks.
|
||||
|
||||
[NOTE: due to a minor quirk, the site-wide hooks in `$GL_PACKAGE_HOOKS` also
|
||||
get copied to `$HOME/.gitolite/hooks` when you "install". I need to fix and
|
||||
thoroughly test this later; for now, just ignore the extra files you see in
|
||||
there; they're harmless/redundant (TODO)]
|
||||
|
||||
<a name="_special_case_the_non_root_method"></a>
|
||||
|
||||
#### special case: the "non-root" method
|
||||
|
||||
This method was created later, just piggy-backing on everything that already
|
||||
existed to cater to the "package" and "root" methods. In this method, the
|
||||
`$GL_PACKAGE_HOOKS` is as accessible or under your control as
|
||||
`$HOME/.gitolite`, so it doesn't matter where you put your hooks. I
|
||||
*strongly* suggest putting them in `$GL_PACKAGE_HOOKS` and ignoring
|
||||
`$HOME/.gitolite` completely.
|
||||
|
||||
<a name="_when_do_hooks_propagate_"></a>
|
||||
|
||||
### **when** do hooks propagate?
|
||||
|
||||
First: realise that gitolite *wants to make sure* that all the hooks in your
|
||||
`hooks/common` directory get copied (symlinked, actually) to *every* repo that
|
||||
gets created. **Not doing so is generally a security risk; because the
|
||||
primary purpose of gitolite is access control, people generally *want* hooks
|
||||
to run.**
|
||||
|
||||
Here's how/when hooks are created/propagated:
|
||||
|
||||
1. anytime you do an install, gitolite trawls through *all* existing repos
|
||||
(using the unix `find` command) and force-links all the hooks in all the
|
||||
repos so they all get the latest and greatest hooks.
|
||||
|
||||
2. anytime you do a "compile" (meaning push changes to the admin repo),
|
||||
gitolite looks through all the repos named in the config. It first checks
|
||||
if the repo exists, creating it if needed. It then looks for a sentinel
|
||||
file called "gitolite-hooked" (an empty file in the hooks directory). If
|
||||
it doesn't find it, it will assume that hooks need to be propagated.
|
||||
|
||||
This is because people often copy a repo from elsewhere, add it to the
|
||||
config, and expect things to work. Without this step, those repos don't
|
||||
get the hooks, which is bad -- the access control would have failed
|
||||
silently!
|
||||
|
||||
3. anytime a new repo is created, the same force-linking of hooks happens.
|
||||
The 3 places a new repo is created are:
|
||||
|
||||
* the "compile" case mentioned above, where the admin added a normal
|
||||
repo to the config and pushed
|
||||
|
||||
* the wildrepos case, where you have "C" permissions and the repo does
|
||||
not already exist
|
||||
|
||||
* the `fork` command in `contrib/adc`. In this case the hooks are
|
||||
explicitly copied from the source repo using the `cp` command, not
|
||||
using the code internal to gitolite.
|
||||
|
||||
For people who do not want certain hooks to run for certain repos, one simple
|
||||
solution that will work right now is to check the value of `$GL_REPO` at the
|
||||
start of the hook, and `exit 0` based on what it contains/matches.
|
||||
|
||||
[0inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd
|
||||
|
141
doc/http-backend.mkd
Normal file
141
doc/http-backend.mkd
Normal file
|
@ -0,0 +1,141 @@
|
|||
# how to setup gitolite to use smart http mode
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_WARNINGS_plus_stuff_I_need_help_with">WARNINGS, plus stuff I need help with</a>
|
||||
* <a href="#_additional_requirements">additional requirements</a>
|
||||
* <a href="#_detailed_instructions">detailed instructions</a>
|
||||
* <a href="#_install_gitolite_under_apache_">install gitolite under "apache"</a>
|
||||
* <a href="#_setup_the_http_backend">setup the http-backend</a>
|
||||
* <a href="#_usage">usage</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_WARNINGS_plus_stuff_I_need_help_with"></a>
|
||||
|
||||
### WARNINGS, plus stuff I need help with
|
||||
|
||||
* I have NOT converted the test suite to use this mode. Volunteers to
|
||||
convert it to http access are welcome :-)
|
||||
|
||||
* I have no idea how to handle the password issue other than creating a
|
||||
`~/.netrc` file and making it `chmod 600`. Anyway, http based access is
|
||||
inherently less secure than pubkeys so not much point worrying about it.
|
||||
|
||||
* I have not tested any of the ancillary standalone programs (like
|
||||
gl-dont-panic) in this mode. They're most likely going to crash and burn
|
||||
because `$HOME` is not defined or in the wrong place; manually set
|
||||
`HOME=$GITOLITE_HTTP_HOME` and hope for the best. Luckily most of them
|
||||
have to do with sshkeys so this may not matter. YMMV.
|
||||
|
||||
* tested on stock Fedora 13; if you test on other environments please let me
|
||||
know how it worked out and if we need to adjust this document
|
||||
|
||||
* tested https with dummy certs and `GIT_SSL_NO_VERIFY`; no reason why it
|
||||
shouldn't work on a proper setup with everything in place
|
||||
|
||||
* have not tried making repos available to both ssh *and* http mode clients;
|
||||
(I'd guess it ought to work fine if the "apache" user was made login-able
|
||||
and given a proper $HOME and `~/.ssh/authorized_keys` and all that). If
|
||||
anyone has the energy to try that please let me know how that went.
|
||||
|
||||
<a name="_additional_requirements"></a>
|
||||
|
||||
### additional requirements
|
||||
|
||||
* requires `GIT_PROJECT_ROOT` (see "man git-http-backend" for what this is)
|
||||
set explicitly (i.e., it is no longer optional). Please set it to some
|
||||
place outside apache's `DOCUMENT_ROOT`.
|
||||
|
||||
<a name="_detailed_instructions"></a>
|
||||
|
||||
### detailed instructions
|
||||
|
||||
I assume you've installed apache 2.x and git on the server.
|
||||
|
||||
I assume your httpd runs under the "apache" userid; adjust instructions below
|
||||
if it does not. Similarly for "/var/www" and other file names/locations.
|
||||
|
||||
<a name="_install_gitolite_under_apache_"></a>
|
||||
|
||||
#### install gitolite under "apache"
|
||||
|
||||
* follow the "non-root" method, but since you can't even "su - apache", make
|
||||
the following variations when doing this as root:
|
||||
|
||||
* `cd ~apache` first; this is `/var/www` on Fedora 13
|
||||
|
||||
* do this in the shell
|
||||
|
||||
mkdir gitolite-home
|
||||
export GITOLITE_HTTP_HOME
|
||||
GITOLITE_HTTP_HOME=/var/www/gitolite-home
|
||||
PATH=$PATH:$GITOLITE_HTTP_HOME/bin
|
||||
|
||||
* now run the first 3 install steps for "non-root" method (clone, mkdir,
|
||||
and gl-system-install), but **substitute `GITOLITE_HTTP_HOME` in place of
|
||||
`HOME`** in the mkdir and gl-system-install steps.
|
||||
|
||||
**Do NOT run the gl-setup step yet**.
|
||||
|
||||
* after the gl-system-install step, add these to the **top** of
|
||||
/var/www/gitolite-home/share/gitolite/conf/example.gitolite.rc
|
||||
|
||||
$ENV{GIT_HTTP_BACKEND} = "/usr/libexec/git-core/git-http-backend";
|
||||
# or wherever you have that file; not NO trailing slash
|
||||
$ENV{PATH} .= ":$ENV{GITOLITE_HTTP_HOME}/bin";
|
||||
# note the ".=" here, not "="
|
||||
|
||||
* run gl-setup with the name of your admin user
|
||||
|
||||
gl-setup sitaram
|
||||
|
||||
* IMPORTANT: fix up ownerships
|
||||
|
||||
chown -R apache.apache $GITOLITE_HTTP_HOME
|
||||
|
||||
<a name="_setup_the_http_backend"></a>
|
||||
|
||||
#### setup the http-backend
|
||||
|
||||
* when you setup the apache config according to "man git-http-backend",
|
||||
change these two as below (please note the trailing slash on the
|
||||
ScriptAlias line):
|
||||
|
||||
SetEnv GIT_PROJECT_ROOT /var/www/gitolite-home/repositories
|
||||
ScriptAlias /git/ /var/www/gitolite-home/bin/gl-auth-command/
|
||||
|
||||
You also need this new variable:
|
||||
|
||||
SetEnv GITOLITE_HTTP_HOME /var/www/gitolite-home
|
||||
|
||||
And that's it... you're done for the setup!
|
||||
|
||||
<a name="_usage"></a>
|
||||
|
||||
### usage
|
||||
|
||||
Git URLs look like `http://user:password@server/git/reponame.git`.
|
||||
|
||||
The custom commands, like "info", "expand" should be handled as follows. The
|
||||
command name will come just after the `/git/`, followed by a `?`, followed by
|
||||
the arguments, with `+` representing a space. Here are some examples:
|
||||
|
||||
# ssh git@server info
|
||||
curl http://user:password@server/git/info
|
||||
# ssh git@server info repopatt
|
||||
curl http://user:password@server/git/info?repopatt
|
||||
# ssh git@server info repopatt user1 user2
|
||||
curl http://user:password@server/git/info?repopatt+user1+user2
|
||||
|
||||
It gets even more interesting for the `setperms` command, which expects STDIN.
|
||||
I didn't want to get too much into the code here, so I found that the
|
||||
following works and I'm leaving it at that:
|
||||
|
||||
(echo R user1 user2; echo RW user3 user4) |
|
||||
curl --data-binary @- http://user:password@server/git/setperms?reponame.git
|
||||
|
||||
With a few nice shell aliases, you won't even notice the horrible convolutions
|
||||
here ;-)
|
||||
|
||||
Enjoy!
|
279
doc/install-transcript.mkd
Normal file
279
doc/install-transcript.mkd
Normal file
|
@ -0,0 +1,279 @@
|
|||
# gitolite install transcript
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_about_this_document">about this document</a>
|
||||
* <a href="#_create_userids_on_server_and_client_optional_">create userids on server and client (optional)</a>
|
||||
* <a href="#_get_pubkey_access_from_client_to_server">get pubkey access from client to server</a>
|
||||
* <a href="#_get_gitolite_source">get gitolite source</a>
|
||||
* <a href="#_install_gitolite">install gitolite</a>
|
||||
* <a href="#_VERY_IMPORTANT_">VERY IMPORTANT...</a>
|
||||
* <a href="#_examine_what_you_have">examine what you have</a>
|
||||
* <a href="#_emergency_password_access">emergency password access</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_about_this_document"></a>
|
||||
|
||||
### about this document
|
||||
|
||||
This is a *complete* transcript of a full gitolite install, *from scratch*,
|
||||
using brand new userids ("sita" on the client, "git" on the server). Please
|
||||
note that you can use existing userids also, it is not necessary to use
|
||||
dedicated user IDs for this. In particular, people who have a single user
|
||||
hosting account can also use this method, as long as they have password access
|
||||
as a fallback if they screw up the keys somewhere. Also, you don't have to
|
||||
use some *other* server for all this, both server and client can be
|
||||
"localhost" if you like.
|
||||
|
||||
Please note that this entire transcript can be summarised as:
|
||||
|
||||
* create users on client and server (optional)
|
||||
* get pubkey access to server from client (`ssh-copy-id` or manual eqvt)
|
||||
* run one command ***on client*** (`gl-easy-install`)
|
||||
|
||||
...and only that last step is actually gitolite. In fact, the bulk of the
|
||||
transcript is **non**-gitolite stuff :)
|
||||
|
||||
**Please also note that this method will setup everything on the server, but
|
||||
you have to run it on your workstation, NOT on the server!**
|
||||
|
||||
----
|
||||
|
||||
<a name="_create_userids_on_server_and_client_optional_"></a>
|
||||
|
||||
### create userids on server and client (optional)
|
||||
|
||||
Client side: add user, give him a password
|
||||
|
||||
sita-lt:~ # useradd sita
|
||||
|
||||
sita-lt:~ # passwd sita
|
||||
Changing password for user sita.
|
||||
New UNIX password:
|
||||
Retype new UNIX password:
|
||||
passwd: all authentication tokens updated successfully.
|
||||
|
||||
Server side: (log on to server, then) add user, give it a password
|
||||
|
||||
sita-lt:~ # ssh sitaram@server
|
||||
sitaram@server's password:
|
||||
Last login: Fri Dec 18 20:25:06 2009
|
||||
-bash-3.2$ su -
|
||||
Password:
|
||||
|
||||
sita-sv:~ # useradd git
|
||||
|
||||
sita-sv:~ # passwd git
|
||||
Changing password for user git.
|
||||
New UNIX password:
|
||||
Retype new UNIX password:
|
||||
passwd: all authentication tokens updated successfully.
|
||||
|
||||
Server side: allow ssh access to "git" user
|
||||
|
||||
This is done by editing the sshd config file and adding "git" to the
|
||||
"AllowUsers" list (the grep command is just confirming the change we made,
|
||||
because I'm not showing the actual "vi" session):
|
||||
|
||||
sita-sv:~ # vim /etc/ssh/sshd_config
|
||||
|
||||
sita-sv:~ # grep -i allowusers /etc/ssh/sshd_config
|
||||
AllowUsers sitaram git
|
||||
|
||||
sita-sv:~ # service sshd restart
|
||||
Stopping sshd: [ OK ]
|
||||
Starting sshd: [ OK ]
|
||||
|
||||
**NOTE**: if the `AllowUsers` setting is completely missing from the sshd
|
||||
config file, all users are allowed (see `man sshd_config`). You may prefer to
|
||||
leave it that way -- your choice. I prefer to make the usernames explicit
|
||||
because I'm paranoid ;-)
|
||||
|
||||
----
|
||||
|
||||
<a name="_get_pubkey_access_from_client_to_server"></a>
|
||||
|
||||
### get pubkey access from client to server
|
||||
|
||||
This involves creating a keypair for yourself (using `ssh-keygen`), and
|
||||
copying the public part of that keypair to the `~/.ssh/authorized_keys` file
|
||||
on the server (using `ssh-copy-id`, if you're on Linux, or the manual method
|
||||
described in the `ssh-copy-id` section in `doc/3-faq-tips-etc.mkd`).
|
||||
|
||||
sita-lt:~ $ su - sita
|
||||
Password:
|
||||
|
||||
sita@sita-lt:~ $ ssh-keygen
|
||||
Generating public/private rsa key pair.
|
||||
Enter file in which to save the key (/home/sita/.ssh/id_rsa):
|
||||
Created directory '/home/sita/.ssh'.
|
||||
Enter passphrase (empty for no passphrase):
|
||||
Enter same passphrase again:
|
||||
Your identification has been saved in /home/sita/.ssh/id_rsa.
|
||||
Your public key has been saved in /home/sita/.ssh/id_rsa.pub.
|
||||
The key fingerprint is:
|
||||
8a:e0:60:1b:04:58:68:50:a4:d7:d0:3a:a5:2d:bf:0a sita@sita-lt.atc.tcs.com
|
||||
The key's randomart image is:
|
||||
+--[ RSA 2048]----+
|
||||
|===. |
|
||||
|+o oo |
|
||||
|o..=. |
|
||||
|..= . |
|
||||
|.o.+ S |
|
||||
|.oo... . |
|
||||
|E.. ... |
|
||||
| . . |
|
||||
| .. |
|
||||
+-----------------+
|
||||
|
||||
sita@sita-lt:~ $ ssh-copy-id -i ~/.ssh/id_rsa git@server
|
||||
git@server's password:
|
||||
/usr/bin/xauth: creating new authority file /home/git/.Xauthority
|
||||
Now try logging into the machine, with "ssh 'git@server'", and check in:
|
||||
|
||||
.ssh/authorized_keys
|
||||
|
||||
to make sure we haven't added extra keys that you weren't expecting.
|
||||
|
||||
Double check to make sure you can log on to `git@server` without a password:
|
||||
|
||||
sita@sita-lt:~ $ ssh git@server pwd
|
||||
/home/git
|
||||
|
||||
**DO NOT PROCEED UNTIL THIS WORKS OK!**
|
||||
|
||||
----
|
||||
|
||||
<a name="_get_gitolite_source"></a>
|
||||
|
||||
### get gitolite source
|
||||
|
||||
sita@sita-lt:~ $ git clone git://github.com/sitaramc/gitolite gitolite-source
|
||||
Initialized empty Git repository in /home/sita/gitolite-source/.git/
|
||||
remote: Counting objects: 1157, done.
|
||||
remote: Compressing objects: 100% (584/584), done.
|
||||
remote: Total 1157 (delta 756), reused 912 (delta 562)
|
||||
Receiving objects: 100% (1157/1157), 270.08 KiB | 61 KiB/s, done.
|
||||
Resolving deltas: 100% (756/756), done.
|
||||
|
||||
<a name="_install_gitolite"></a>
|
||||
|
||||
### install gitolite
|
||||
|
||||
Note that gitolite is installed from the *client*. The `easy-install` script
|
||||
runs on the client but installs gitolite on the server!
|
||||
|
||||
sita@sita-lt:~ $ cd gitolite-source/src
|
||||
|
||||
<font color="red"> **This is the only gitolite specific command in a typical
|
||||
install sequence**. </font> Run it without any arguments to see a usage
|
||||
message. Run it without the `-q` to get a more verbose, pause-at-every-step,
|
||||
install mode that allows you to change the defaults (for example, if you want
|
||||
a different UMASK setting, or you want the repos to be in a different place,
|
||||
etc.)
|
||||
|
||||
sita@sita-lt:src $ ./gl-easy-install -q git server sitaram
|
||||
you are upgrading (or installing first-time) to v0.95-38-gb0ce84d
|
||||
setting up keypair...
|
||||
Generating public/private rsa key pair.
|
||||
Enter passphrase (empty for no passphrase):
|
||||
Enter same passphrase again:
|
||||
Your identification has been saved in /home/sita/.ssh/sitaram.
|
||||
Your public key has been saved in /home/sita/.ssh/sitaram.pub.
|
||||
The key fingerprint is:
|
||||
2a:8e:88:42:36:7e:71:e8:cc:ff:4c:54:64:8e:cf:19 sita@sita-lt.atc.tcs.com
|
||||
The key's randomart image is:
|
||||
+--[ RSA 2048]----+
|
||||
| o |
|
||||
| = |
|
||||
| . E |
|
||||
| + o |
|
||||
| . .S+ |
|
||||
| + o ... |
|
||||
|+ = + .. |
|
||||
|oo B .o |
|
||||
|+ o o..o |
|
||||
+-----------------+
|
||||
creating gitolite para in ~/.ssh/config...
|
||||
finding/creating gitolite rc...
|
||||
installing/upgrading...
|
||||
Initialized empty Git repository in /home/git/repositories/gitolite-admin.git/
|
||||
Initialized empty Git repository in /home/git/repositories/testing.git/
|
||||
Pseudo-terminal will not be allocated because stdin is not a terminal.
|
||||
fatal: No HEAD commit to compare with (yet)
|
||||
[master (root-commit) 2f40d4b] start
|
||||
2 files changed, 13 insertions(+), 0 deletions(-)
|
||||
create mode 100644 conf/gitolite.conf
|
||||
create mode 100644 keydir/sitaram.pub
|
||||
cloning gitolite-admin repo...
|
||||
Initialized empty Git repository in /home/sita/gitolite-admin/.git/
|
||||
remote: Counting objects: 6, done.
|
||||
remote: Compressing objects: 100% (4/4), done.
|
||||
remote: Total 6 (delta 0), reused 0 (delta 0)
|
||||
Receiving objects: 100% (6/6), done.
|
||||
|
||||
|
||||
---------------------------------------------------------------
|
||||
|
||||
done!
|
||||
|
||||
Reminder:
|
||||
*Your* URL for cloning any repo on this server will be
|
||||
gitolite:reponame.git
|
||||
*Other* users you set up will have to use
|
||||
git@server:reponame.git
|
||||
|
||||
If this is your first time installing gitolite, please also:
|
||||
tail -31 ./gl-easy-install
|
||||
for next steps.
|
||||
|
||||
----
|
||||
|
||||
<a name="_VERY_IMPORTANT_"></a>
|
||||
|
||||
### VERY IMPORTANT...
|
||||
|
||||
Please read the text that the easy-install command produces as output when you
|
||||
run it. People who fail to read this get into trouble later. And I didn't
|
||||
write all that because I wanted to practice typing.
|
||||
|
||||
The text just above this section is an approximation; your version will
|
||||
contain the correct URLs for your install, including port numbers if
|
||||
non-standard ports were used).
|
||||
|
||||
Try out that `tail -31 ./gl-easy-install` too :)
|
||||
|
||||
<a name="_examine_what_you_have"></a>
|
||||
|
||||
### examine what you have
|
||||
|
||||
The last step of the previous command creates a local clone of your
|
||||
gitolite-admin repo in `~/gitolite-admin`.
|
||||
|
||||
sita@sita-lt:src $ cd ~/gitolite-admin/
|
||||
|
||||
sita@sita-lt:gitolite-admin $ git --no-pager log --stat
|
||||
commit 2f40d4bb80d424dc39aae5d0973f8c1b2e395666
|
||||
Author: git <git@sita-lt.atc.tcs.com>
|
||||
Date: Thu Dec 24 21:39:15 2009 +0530
|
||||
|
||||
start
|
||||
|
||||
conf/gitolite.conf | 12 ++++++++++++
|
||||
keydir/sitaram.pub | 1 +
|
||||
2 files changed, 13 insertions(+), 0 deletions(-)
|
||||
|
||||
And that's really all. Add keys to keydir here, edit conf/gitolite.conf as
|
||||
needed, then add, commit, and push the changes to the server.
|
||||
|
||||
<a name="_emergency_password_access"></a>
|
||||
|
||||
### emergency password access
|
||||
|
||||
If you lose your keys or the worst happens and you use the wrong key for the
|
||||
wrong thing and apparently lose all access, but you still know the password,
|
||||
this is what you do:
|
||||
|
||||
sita@sita-lt:~ $ ssh -o preferredauthentications=password git@server
|
||||
git@server's password:
|
105
doc/migrate.mkd
Normal file
105
doc/migrate.mkd
Normal file
|
@ -0,0 +1,105 @@
|
|||
# migrating from gitosis to gitolite
|
||||
|
||||
[TODO: make the migration tool fix up gitweb and daemon control also...]
|
||||
|
||||
Migrating from gitosis to gitolite is pretty easy, because the basic design is
|
||||
the same.
|
||||
|
||||
Here's how we migrated my work repos:
|
||||
|
||||
1. login as the `git` user on the server, and get a bash shell prompt
|
||||
|
||||
2. **disable gitosis** by renaming `/usr/bin/gitosis-serve` to something
|
||||
else. This will prevent users from pushing anything while you do the
|
||||
backup, migration, etc.
|
||||
|
||||
3. **edit** `~/.ssh/authorized_keys` and **carefully** remove all the lines
|
||||
containing "gitosis-serve", as well as the marker line that says
|
||||
"auto-generated by gitosis, DO NOT REMOVE", then save the file. If the
|
||||
file did not have any other keys and is now empty, don't worry -- save it
|
||||
anyway because gitolite expects the file to be present (even if it is
|
||||
empty).
|
||||
|
||||
4. For added safety, **delete** the post-update hook that gitosis-admin
|
||||
installed
|
||||
|
||||
rm ~/repositories/gitosis-admin.git/hooks/post-update
|
||||
|
||||
or at least rename it to `.sample` like all the other hooks hanging
|
||||
around, or edit it and comment out the line that calls `gitosis-run-hook
|
||||
post-update`.
|
||||
|
||||
If you do not do this, an accidental push to the gitosis-admin repo will
|
||||
mess up your `~/.ssh/authorized_keys` file
|
||||
|
||||
5. take a **backup** of the `~/repositories` directory
|
||||
|
||||
Now, log off the server and get back to the client:
|
||||
|
||||
1. follow instructions to install gitolite; see the [install document][inst].
|
||||
Make sure that you **don't** change the default path for `$REPO_BASE` if
|
||||
you edit the config file!
|
||||
|
||||
This will give you a gitolite config that has the required entries for the
|
||||
"gitolite-admin" repo.
|
||||
|
||||
2. **convert** your gitosis config file and append it to your gitolite config
|
||||
file. Substitute the path for your gitosis-admin clone in `$GSAC` below,
|
||||
and similarly the path for your gito**lite**-admin clone in `$GLAC`
|
||||
|
||||
src/gl-conf-convert < $GSAC/gitosis.conf >> $GLAC/gitolite.conf
|
||||
|
||||
Be sure to check the file to make sure it converted correctly
|
||||
|
||||
3. **copy** the keys from gitosis's keydir (same meanings for GSAC and GLAC)
|
||||
|
||||
cp $GSAC/keydir/* $GLAC/keydir
|
||||
|
||||
4. **IMPORTANT**: if you have any users with names like `user@foo`, where the
|
||||
part after the `@` does *not* have a `.` in it (i.e., does not look like
|
||||
an email address), you need to change them, because gitolite uses that
|
||||
syntax for enabling multi keys.
|
||||
|
||||
You have two choices in how to fix this. You can change the gitolite
|
||||
config so that all mention of `user@foo` is changed to just `user`.
|
||||
|
||||
Or you can change each occurrence of `user@foo` to, say, `user_foo` *and*
|
||||
change the pubkey filename in keydir/ also the same way (`user_foo.pub`).
|
||||
|
||||
Just to repeat, you do NOT need to do this if the username was like
|
||||
`user@foo.bar`, i.e., the part after the `@` had a `.` in it, because then
|
||||
it looks like an email address.
|
||||
|
||||
[This][mk] will tell you more about these nuances.
|
||||
|
||||
5. **IMPORTANT: expand any multi-key files you may have**. [Here][mk]'s an
|
||||
explanation of what multi-keys are, how gitosis does them and how gitolite
|
||||
does it differently.
|
||||
|
||||
You can split the keys manually, or use the following code (just
|
||||
copy-paste it into your xterm after "cd"-ing to your gitolite-admin repo
|
||||
clone):
|
||||
|
||||
wc -l keydir/*.pub | grep -v total | grep -v -w 1 | while read a b
|
||||
do
|
||||
i=1
|
||||
cat $b|while read l
|
||||
do
|
||||
echo "$l" > ${b%.pub}@$i.pub
|
||||
(( i++ ))
|
||||
done
|
||||
mv $b $b.done
|
||||
done
|
||||
|
||||
This will split each multi-key file (say "sitaram.pub") into individual
|
||||
files called "sitaram@1.pub", "sitaram@2.pub", etc., and rename the
|
||||
original to "sitaram.pub.done" so gitolite won't pick it up.
|
||||
|
||||
At this point you can rename the split parts more appropriately, like
|
||||
"sitaram@laptop.pub" and "sitaram@desktop.pub" or whatever. *Please check
|
||||
the files to make sure this worked properly*
|
||||
|
||||
6. Check all your changes to your gitolite-admin clone, commit, and push
|
||||
|
||||
[mk]: http://github.com/sitaramc/gitolite/blob/pu/doc/3-faq-tips-etc.mkd#multikeys
|
||||
[inst]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd
|
313
doc/mirroring.mkd
Normal file
313
doc/mirroring.mkd
Normal file
|
@ -0,0 +1,313 @@
|
|||
# mirroring a gitolite setup
|
||||
|
||||
Mirroring git repos is essentially a one-liner. For each mirror you want to
|
||||
update, you just add a post-receive hook that says
|
||||
|
||||
#!/bin/bash
|
||||
git push --mirror slave_user@mirror.host:/path/to/repo.git
|
||||
|
||||
But life is never that simple...
|
||||
|
||||
**This document has been tested using a 3-server setup, all installed using
|
||||
the "non-root" method (see doc/1-INSTALL.mkd). However, the process is
|
||||
probably not going to be very forgiving of human error -- like anything that
|
||||
is this deep in "system admin" territory, errors are likely to be costly. If
|
||||
you're the kind who hits enter first and then thinks about what he typed,
|
||||
you're in for some fun times ;-)**
|
||||
|
||||
**On the plus side, everything we do is done using git commands, so things are
|
||||
never *really* lost until you do a `git gc`**.
|
||||
|
||||
----
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_RULE_NUMBER_ONE_">RULE NUMBER ONE!</a>
|
||||
* <a href="#_things_that_will_NOT_be_mirrored_by_this_process">things that will NOT be mirrored by this process</a>
|
||||
* <a href="#_conventions_in_this_document">conventions in this document</a>
|
||||
* <a href="#_setting_up_mirroring">setting up mirroring</a>
|
||||
* <a href="#_install_gitolite_on_all_servers">install gitolite on all servers</a>
|
||||
* <a href="#_generate_keypairs">generate keypairs</a>
|
||||
* <a href="#_setup_the_mirror_shell_on_each_server">setup the mirror-shell on each server</a>
|
||||
* <a href="#_set_slaves_to_slave_mode">set slaves to slave mode</a>
|
||||
* <a href="#_set_slave_server_lists">set slave server lists</a>
|
||||
* <a href="#_syncing_the_mirrors_the_first_time">syncing the mirrors the first time</a>
|
||||
* <a href="#_switching_over">switching over</a>
|
||||
* <a href="#_the_return_of_foo">the return of foo</a>
|
||||
* <a href="#_switching_back">switching back</a>
|
||||
* <a href="#_making_foo_a_slave">making foo a slave</a>
|
||||
* <a href="#_URLs_that_your_users_will_use">URLs that your users will use</a>
|
||||
|
||||
<a name="_RULE_NUMBER_ONE_"></a>
|
||||
|
||||
### RULE NUMBER ONE!
|
||||
|
||||
**RULE OF GIT MIRRORING: users should push directly to only one server**! All
|
||||
the other machines (the slaves) should be updated by the master server.
|
||||
|
||||
If a user pushes directly to one of the slaves, those changes will get wiped
|
||||
out on the next mirror push from the real master server.
|
||||
|
||||
Corollary: if the primary went down and you effected a changeover, you must
|
||||
make sure that the primary does not come up in a push-enabled mode when it
|
||||
recovers.
|
||||
|
||||
<a name="_things_that_will_NOT_be_mirrored_by_this_process"></a>
|
||||
|
||||
### things that will NOT be mirrored by this process
|
||||
|
||||
Let's get this out of the way. This procedure will only mirror your git
|
||||
repositories, using `git push --mirror`. Therefore, certain files will not be
|
||||
mirrored:
|
||||
|
||||
* gitolite log files
|
||||
* "gl-creator" and "gl-perms" files
|
||||
* "projects.list", "description", and entries in the "config" files within
|
||||
each repo
|
||||
|
||||
None of these affect actual repo contents of course, but they could be
|
||||
important, (especially the gl-creator, although if your wildcard pattern had
|
||||
"CREATOR" in it you can recreate those files easily enough anyway).
|
||||
|
||||
Your best bet is to use rsync for the log files, and tar for the others, at
|
||||
regular intervals.
|
||||
|
||||
<a name="_conventions_in_this_document"></a>
|
||||
|
||||
### conventions in this document
|
||||
|
||||
The userid hosting gitolite is `gitolite` on all machines. The servers are
|
||||
foo, bar, and baz. At the beginning, foo is the master, the other 2 are
|
||||
slaves.
|
||||
|
||||
<a name="_setting_up_mirroring"></a>
|
||||
|
||||
### setting up mirroring
|
||||
|
||||
<a name="_install_gitolite_on_all_servers"></a>
|
||||
|
||||
#### install gitolite on all servers
|
||||
|
||||
* before running the final step in the install sequence, make sure you go to
|
||||
the `hooks/common` directory and rename `post-receive.mirrorpush` to
|
||||
`post-receive`. See doc/hook-propagation.mkd if you're not sure where you
|
||||
should look for `hooks/common`.
|
||||
|
||||
* if the server already has gitolite installed, use the normal methods to
|
||||
make sure this hook gets in.
|
||||
|
||||
* Use the same "admin key" on all the machines, so that the same person has
|
||||
gitolite-admin access to all of them.
|
||||
|
||||
<a name="_generate_keypairs"></a>
|
||||
|
||||
#### generate keypairs
|
||||
|
||||
Each server will be potentially logging on to one or more of the other
|
||||
servers, so first generate keypairs for all of them (`ssh-keygen`) and copy
|
||||
the `.pub` files to all other servers, named appropriately. So foo will have
|
||||
bar.pub and baz.pub, etc.
|
||||
|
||||
<a name="_setup_the_mirror_shell_on_each_server"></a>
|
||||
|
||||
#### setup the mirror-shell on each server
|
||||
|
||||
If you installed gitolite using the from client method, run the following:
|
||||
|
||||
# on foo
|
||||
export GL_ADMINDIR=` cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'`
|
||||
cat bar.pub baz.pub |
|
||||
sed -e 's,^,command="'$GL_ADMINDIR'/src/gl-mirror-shell" ,' >> ~/.ssh/authorized_keys
|
||||
|
||||
If you installed using any of the other 3 methods do this:
|
||||
|
||||
cat bar.pub baz.pub |
|
||||
sed -e 's,^,command="'$(which gl-mirror-shell)'" ,' >> ~/.ssh/authorized_keys
|
||||
|
||||
Also do the same thing on the other machines.
|
||||
|
||||
Now test this access:
|
||||
|
||||
# on foo
|
||||
ssh gitolite@bar pwd
|
||||
# should print /home/gitolite/repositories
|
||||
ssh gitolite@bar uname -a
|
||||
# should print the appropriate info for that server
|
||||
|
||||
Similarly test the other combinations.
|
||||
|
||||
<a name="_set_slaves_to_slave_mode"></a>
|
||||
|
||||
#### set slaves to slave mode
|
||||
|
||||
Set slave mode on all the *slave* servers by setting `$GL_SLAVE_MODE = 1`
|
||||
(uncommenting the line if necessary).
|
||||
|
||||
Leave the master server's file as is.
|
||||
|
||||
<a name="_set_slave_server_lists"></a>
|
||||
|
||||
#### set slave server lists
|
||||
|
||||
On the master (foo), set the names of the slaves by editing the
|
||||
`~/.gitolite.rc` to contain:
|
||||
|
||||
$ENV{GL_SLAVES} = 'gitolite@bar gitolite@baz';
|
||||
|
||||
**Note the syntax well; this is critical**:
|
||||
|
||||
* **this must be in single quotes** (or you must remember to escape the `@`)
|
||||
* the variable is an ENV var, not a plain perl var
|
||||
* the values are *space separated*
|
||||
* each value represents the userid and hostname for one server
|
||||
|
||||
The basic idea is that this string, should be usable in both the following
|
||||
syntaxes:
|
||||
|
||||
git clone gitolite@bar:repo
|
||||
ssh gitolite@bar pwd
|
||||
|
||||
You can also use ssh host aliases. Let's say server "bar" has a non-standard
|
||||
port number:
|
||||
|
||||
# in ~/.ssh/config on foo
|
||||
host mybar
|
||||
hostname bar
|
||||
user gitolite
|
||||
port 2222
|
||||
|
||||
# in ~/.gitolite.rc on foo
|
||||
$ENV{GL_SLAVES} = 'bar gitolite@baz';
|
||||
|
||||
And that's really all there is, unless...
|
||||
|
||||
<a name="_syncing_the_mirrors_the_first_time"></a>
|
||||
|
||||
### syncing the mirrors the first time
|
||||
|
||||
This is fine if you're setting up everything from scratch. But if your master
|
||||
server already had some repos with commits on them, you have to manually sync
|
||||
them up once.
|
||||
|
||||
# on foo
|
||||
gl-mirror-sync gitolite@bar
|
||||
# path to "sync" program is ~/.gitolite/src if "from-client" install
|
||||
|
||||
<a name="_switching_over"></a>
|
||||
|
||||
### switching over
|
||||
|
||||
Let's say foo goes down. You want to make bar the main server, and continue
|
||||
to have "baz" be a slave.
|
||||
|
||||
* on bar, edit `~/.gitolite.rc` and set
|
||||
|
||||
$GL_SLAVE_MODE = 0;
|
||||
$ENV{GL_SLAVES} = 'gitolite@baz';
|
||||
|
||||
* **sanity check**: go to your gitolite-admin clone, add a remote for "bar",
|
||||
fetch it, and make sure they are the same:
|
||||
|
||||
git remote add bar gitolite@bar:gitolite-admin
|
||||
git fetch bar
|
||||
git branch -a -v
|
||||
# check that all SHAs are the same
|
||||
|
||||
* inform everyone of the new URL for their repos (see next section for more
|
||||
on this)
|
||||
|
||||
* make sure that if "foo" does come up, it will not immediately start
|
||||
serving requests. You'll be in trouble if (a) foo comes up as it was
|
||||
before, and (b) some developer still had the old URL lying around and
|
||||
started pushing changes to it.
|
||||
|
||||
You could jump in quickly and set `$GL_SLAVE_MODE = 1` as soon as the
|
||||
system comes up. Better still, use extraneous means to block incoming
|
||||
connections from normal users (out of scope for this document).
|
||||
|
||||
<a name="_the_return_of_foo"></a>
|
||||
|
||||
### the return of foo
|
||||
|
||||
<a name="_switching_back"></a>
|
||||
|
||||
#### switching back
|
||||
|
||||
Switching back is fairly easy.
|
||||
|
||||
* synchronise all repos from bar to foo. This may take some time, depending
|
||||
on how long foo was down.
|
||||
|
||||
# on bar
|
||||
gl-mirror-sync gitolite@foo
|
||||
# path to "sync" program is ~/.gitolite/src if "from-client" install
|
||||
|
||||
* turn off pushes on "bar" by setting slave mode to 1
|
||||
* run the sync once again; this should complete quickly
|
||||
|
||||
* **double check by comparing some the repos on both sides if needed**. You
|
||||
could run the following snippet on all servers for a quick check:
|
||||
|
||||
cd ~/repositories # or wherever $REPO_BASE is
|
||||
find . -type d -name "*.git" | sort |
|
||||
while read r
|
||||
do
|
||||
echo $r
|
||||
git ls-remote $r | sort
|
||||
done | md5sum
|
||||
|
||||
* on foo, set the slave list (or check that it is correct)
|
||||
* on foo, set slave mode off
|
||||
* tell everyone to switch back
|
||||
|
||||
<a name="_making_foo_a_slave"></a>
|
||||
|
||||
#### making foo a slave
|
||||
|
||||
If "foo" does come up in a controlled manner, you might not want to switch
|
||||
back right away. Unless you're doing DNS tricks, users may be peeved at
|
||||
having to do 2 switches.
|
||||
|
||||
If you want to make foo a slave, you know the drill by now:
|
||||
|
||||
* set slave mode to 1 on foo
|
||||
* on bar, add foo as a slave
|
||||
|
||||
# in ~/.gitolite.rc on bar
|
||||
$ENV{GL_SLAVES} = 'gitolite@foo gitolite@baz';
|
||||
|
||||
I think that should cover pretty much everything. I *have* tested most of
|
||||
this, but YMMV.
|
||||
|
||||
----
|
||||
|
||||
<a name="_URLs_that_your_users_will_use"></a>
|
||||
|
||||
### URLs that your users will use
|
||||
|
||||
Unless you play DNS tricks, it is more than likely that your users would have
|
||||
to change the URLs they use to access their repos if you change the server
|
||||
they push to.
|
||||
|
||||
I cannot speak for the plethora of git client software out there but for
|
||||
normal git, this problem can be mitigated somewhat by doing this:
|
||||
|
||||
* in `~/.ssh/config` on my workstation, I have
|
||||
|
||||
host gl
|
||||
hostname=primary.server.ip
|
||||
user=gitolite
|
||||
|
||||
* all my `git clone` commands use `gl:reponame` as the URL
|
||||
|
||||
* if the primary goes down, and I have to access the secondary, I just
|
||||
change the `hostname` line in `~/.ssh/config`.
|
||||
|
||||
That's it. Every clone of every repo used anywhere in this userid is now
|
||||
changed.
|
||||
|
||||
To repeat, this may or may not work with all the git clients that exist (like
|
||||
jgit, or any of the GUI tools, and especially if you're on Windows).
|
||||
|
||||
If anyone has a better idea, something that works more universally, I'd love
|
||||
to hear it.
|
76
doc/mob-branches.mkd
Normal file
76
doc/mob-branches.mkd
Normal file
|
@ -0,0 +1,76 @@
|
|||
# mob branches in gitolite
|
||||
|
||||
WARNING: This is hairy stuff. But what's life without a little danger?
|
||||
|
||||
WARNING 2: girocco does mob branches quite differently; the controls on what a
|
||||
mob branch can do are much more fundamental. Here we just trick gitolite into
|
||||
accepting anonymous ssh connections and pretending they're from a mythical
|
||||
user called "mob". **This means all the access control is -- as you might
|
||||
expect -- in the gitolite.conf file, so make sure you don't give the `mob`
|
||||
user too many rights!**
|
||||
|
||||
(tested on Fedora 13; assumes your gitolite server userid is "gitolite" and
|
||||
install was "from-client" method; adjust according to your environment. If
|
||||
you need more than this, you should not be enabling mob branches anyway ;-)
|
||||
|
||||
[hah! Easy way out of being badgered with questions!]
|
||||
|
||||
* create a file called `/tmp/mobshell` (put it somewhere more permanent if
|
||||
you wish). This file should be `chmod +x` and contain
|
||||
|
||||
#!/bin/sh
|
||||
shift
|
||||
export SSH_ORIGINAL_COMMAND
|
||||
SSH_ORIGINAL_COMMAND="$*"
|
||||
|
||||
/home/gitolite/.gitolite/src/gl-auth-command mob
|
||||
# see one of the lines in ~gitolite/.ssh/authorized_keys for the
|
||||
# precise location of the gl-auth-command script
|
||||
|
||||
* create a user called mob. Give it the same UID number and `$HOME` as your
|
||||
gitolite server userid, and set the login shell to be the script you just
|
||||
created. Also delete the password.
|
||||
|
||||
id -u gitolite
|
||||
# returns 503 or something...
|
||||
useradd -d /home/gitolite -s /tmp/mobshell -u 503 -o mob
|
||||
passwd -d mob
|
||||
|
||||
* make sure you have a recent enough sshd and put these lines at the bottom,
|
||||
then restart sshd
|
||||
|
||||
Match user mob
|
||||
PermitEmptyPasswords yes
|
||||
|
||||
That's it. Now you can add stuff to your gitolite.conf file. Here's some
|
||||
examples:
|
||||
|
||||
* This allows the mob user to do anything to the "mob" branch:
|
||||
|
||||
repo foo
|
||||
RW+ = alice bob
|
||||
R = eve
|
||||
RW+ mob$ = mob
|
||||
# only the mob branch, nothing more
|
||||
|
||||
* This is the same, except it can be any branch under "mob/" so you get some
|
||||
flexibility:
|
||||
|
||||
RW+ mob/ = mob
|
||||
|
||||
* Girocco allows pushing to the mob branch only if it already exists (that
|
||||
is, the mob user cannot *create* the mob branch, but if it already exists
|
||||
he can push to it). Here's how you'd do that in gitolite:
|
||||
|
||||
repo foo
|
||||
RW+C = alice bob
|
||||
R = eve
|
||||
RW+ mob$ = mob
|
||||
|
||||
* This gives *every* repo a mob branch (be careful!)
|
||||
|
||||
repo @all
|
||||
RW+ mob$ = mob
|
||||
|
||||
How do mob users access it? The URLs just look like: `mob@server:repo`
|
||||
instead of `gitolite@server:repo` That's it!
|
51
doc/overkill.mkd
Normal file
51
doc/overkill.mkd
Normal file
|
@ -0,0 +1,51 @@
|
|||
# when gitolite is overkill
|
||||
|
||||
Note: I wrote this to help people for whom gitolite is genuinely overkill. I
|
||||
believe it will all work, but YMMV.
|
||||
|
||||
----
|
||||
|
||||
You don't always need something like gitolite. If you have a fixed (or very
|
||||
rarely changing) number of users, and all of them have full access to all your
|
||||
repos, you can use plain Unix permissions to get a lot of this done:
|
||||
|
||||
* dedicate a userid (say "git") to host all your repos. This user will also
|
||||
have a group (normally called "git" on most distros I think)
|
||||
|
||||
* create a directory that is accessible (at least "r" and "x" permissions)
|
||||
to the group "git", all the way upto the root. (That is, if the directory
|
||||
you chose is /home/git/repos, then /, /home, /home/git, and
|
||||
/home/git/repos must all be "g+rx").
|
||||
|
||||
* create all repos in this directory, as the "git" user, using the following
|
||||
command:
|
||||
|
||||
git init --bare --shared reponame.git
|
||||
|
||||
* For each user who needs access to the repos, add them as members to the
|
||||
"git" group also. On Mandriva this is:
|
||||
|
||||
usermod -G git username
|
||||
|
||||
Don't forget that `-G` *replaces* the list of supplementary groups for the
|
||||
user, so be sure to first check if he is already member of some groups and
|
||||
keep those in the command (comma-separated).
|
||||
|
||||
And that's basically it. The "init --shared" will create the repos with
|
||||
"chmod -R g+s". If you have existing repos where you forgot (or didn't know)
|
||||
the "--shared" argument, do this on each of them:
|
||||
|
||||
cd reponame.git
|
||||
git init --shared --bare
|
||||
chmod -R g+w .
|
||||
chmod g+s `find . -type d`
|
||||
|
||||
I think that should do it.
|
||||
|
||||
----
|
||||
|
||||
You can do more complex things using Unix acls. If you do, and feel like
|
||||
writing it up, send it to me and I will add it here (with credit given of
|
||||
course). Personally, I can't be bothered -- once you have differing needs for
|
||||
different people, you really need gitolite anyway, because you probably need
|
||||
different rights for branches as well and Unix ACLs can't do that.
|
56
doc/packaging.mkd
Normal file
56
doc/packaging.mkd
Normal file
|
@ -0,0 +1,56 @@
|
|||
# packaging gitolite
|
||||
|
||||
Here's how you'd package gitolite. In the following description, location "X"
|
||||
can be, say, `/usr/share/gitolite/conf` or some such, and similarly location
|
||||
"Y" can be perhaps `/usr/share/gitolite/hooks`. It's upto your distro
|
||||
policies where they are.
|
||||
|
||||
**Step 1**: Clone the gitolite repo and run the make command inside the clone
|
||||
|
||||
git clone git://github.com/sitaramc/gitolite.git
|
||||
cd gitolite
|
||||
make pu.tar # or "make master.tar" or "make v1.2.tar" etc
|
||||
|
||||
Then you explode the tar file in some temporary location.
|
||||
|
||||
*Alternatively, you can `git checkout` the tag or branch you want, and run
|
||||
this command in the clone directly*:
|
||||
|
||||
git describe --tags --long > conf/VERSION
|
||||
|
||||
**Step 2**: Now make the following changes (no trailing slashes in the
|
||||
location values please):
|
||||
|
||||
* `src/gl-setup` should have the following line:
|
||||
|
||||
GL_PACKAGE_CONF="X"
|
||||
|
||||
* `conf/example.gitolite.rc` should have the following lines:
|
||||
|
||||
$GL_PACKAGE_CONF="X";
|
||||
$GL_PACKAGE_HOOKS="Y";
|
||||
|
||||
* delete `src/gl-easy-install`; that script is meant for a totally different
|
||||
mode of installation and does *not* play well in this mode :-)
|
||||
|
||||
**Step 3**: Move (or arrange to move) the files to their proper locations as
|
||||
given below:
|
||||
|
||||
* everything in "src" goes somewhere on the PATH
|
||||
* everything in "conf" goes to location "X"
|
||||
* everything in "hooks" goes to location "Y"
|
||||
|
||||
**Step 4**: There is no step 4. Unless you count telling your users to run
|
||||
`gl-setup` as a step :)
|
||||
|
||||
On the initial install (urpmi, yum install, or apt-get install), you could
|
||||
also choose to setup a userid called "gitolite", and run "gl-setup" as that
|
||||
user; however I do not know how you would come up with the initial pubkey that
|
||||
is needed. Anyway, the point is that the "gitolite" user is no more special
|
||||
than any other in terms of hosting gitolite. Any user can host gitolite on
|
||||
his userid by just running "gl-setup".
|
||||
|
||||
When you upgrade, just overwrite all the files; it'll all just work. In fact,
|
||||
other than the initial "gl-setup" run, the only time a gitolite hosting user
|
||||
has to actually do anything is to edit their own `~/.gitolite.rc` file if they
|
||||
want to enable or disable specific features.
|
184
doc/progit-article.mkd
Normal file
184
doc/progit-article.mkd
Normal file
|
@ -0,0 +1,184 @@
|
|||
## Gitolite ##
|
||||
|
||||
Note: the latest copy of this section of the ProGit book is always available within the [gitolite documentation][gldpg]. The author would also like to humbly state that, while this section is accurate, and *can* (and often *has*) been used to install gitolite without reading any other documentation, it is of necessity not complete, and cannot completely replace the enormous amount of documentation that gitolite comes with.
|
||||
|
||||
[gldpg]: http://github.com/sitaramc/gitolite/blob/pu/doc/progit-article.mkd
|
||||
|
||||
Git has started to become very popular in corporate environments, which tend to have some additional requirements in terms of access control. Gitolite was originally created to help with those requirements, but it turns out that it's equally useful in the open source world: the Fedora Project controls access to their package management repositories (over 10,000 of them!) using gitolite, and this is probably the largest gitolite installation anywhere too.
|
||||
|
||||
Gitolite allows you to specify permissions not just by repository, but also by branch or tag names within each repository. That is, you can specify that certain people (or groups of people) can only push certain "refs" (branches or tags) but not others.
|
||||
|
||||
<a name="_Installing_"></a>
|
||||
|
||||
### Installing ###
|
||||
|
||||
Installing Gitolite is very easy, even if you don't read the extensive documentation that comes with it. You need an account on a Unix server of some kind; various Linux flavours, and Solaris 10, have been tested. You do not need root access, assuming git, perl, and an openssh compatible ssh server are already installed. In the examples below, we will use the `gitolite` account on a host called `gitserver`.
|
||||
|
||||
Gitolite is somewhat unusual as far as "server" software goes -- access is via ssh, and so every userid on the server is a potential "gitolite host". As a result, there is a notion of "installing" the software itself, and then "setting up" a user as a "gitolite host".
|
||||
|
||||
Gitolite has 4 methods of installation. People using Fedora or Debian systems can obtain an RPM or a DEB and install that. People with root access can install it manually. In these two methods, any user on the system can then become a "gitolite host".
|
||||
|
||||
People without root access can install it within their own userids. And finally, gitolite can be installed by running a script *on the workstation*, from a bash shell. (Even the bash that comes with msysgit will do, in case you're wondering.)
|
||||
|
||||
We will describe this last method in this article; for the other methods please see the documentation.
|
||||
|
||||
You start by obtaining public key based access to your server, so that you can log in from your workstation to the server without getting a password prompt. The following method works on Linux; for other workstation OSs you may have to do this manually. We assume you already had a key pair generated using `ssh-keygen`.
|
||||
|
||||
$ ssh-copy-id -i ~/.ssh/id_rsa gitolite@gitserver
|
||||
|
||||
This will ask you for the password to the gitolite account, and then set up public key access. This is **essential** for the install script, so check to make sure you can run a command without getting a password prompt:
|
||||
|
||||
$ ssh gitolite@gitserver pwd
|
||||
/home/gitolite
|
||||
|
||||
Next, you clone Gitolite from the project's main site and run the "easy install" script (the third argument is your name as you would like it to appear in the resulting gitolite-admin repository):
|
||||
|
||||
$ git clone git://github.com/sitaramc/gitolite
|
||||
$ cd gitolite/src
|
||||
$ ./gl-easy-install -q gitolite gitserver sitaram
|
||||
|
||||
And you're done! Gitolite has now been installed on the server, and you now have a brand new repository called `gitolite-admin` in the home directory of your workstation. You administer your gitolite setup by making changes to this repository and pushing.
|
||||
|
||||
That last command does produce a fair amount of output, which might be interesting to read. Also, the first time you run this, a new keypair is created; you will have to choose a passphrase or hit enter for none. Why a second keypair is needed, and how it is used, is explained in the "ssh troubleshooting" document that comes with Gitolite. (Hey the documentation has to be good for *something*!)
|
||||
|
||||
<a name="_Customising_the_Install_"></a>
|
||||
|
||||
### Customising the Install ###
|
||||
|
||||
While the default, quick, install works for most people, there are some ways to customise the install if you need to. If you omit the `-q` argument, you get a "verbose" mode install -- detailed information on what the install is doing at each step. The verbose mode also allows you to change certain server-side parameters, such as the location of the actual repositories, by editing an "rc" file that the server uses. This "rc" file is liberally commented so you should be able to make any changes you need quite easily, save it, and continue. This file also contains various settings that you can change to enable or disable some of gitolite's advanced features.
|
||||
|
||||
<a name="_Config_File_and_Access_Control_Rules_"></a>
|
||||
|
||||
### Config File and Access Control Rules ###
|
||||
|
||||
Once the install is done, you switch to the `gitolite-admin` repository (placed in your HOME directory) and poke around to see what you got:
|
||||
|
||||
$ cd ~/gitolite-admin/
|
||||
$ ls
|
||||
conf/ keydir/
|
||||
$ find conf keydir -type f
|
||||
conf/gitolite.conf
|
||||
keydir/sitaram.pub
|
||||
$ cat conf/gitolite.conf
|
||||
#gitolite conf
|
||||
# please see conf/example.conf for details on syntax and features
|
||||
|
||||
repo gitolite-admin
|
||||
RW+ = sitaram
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
|
||||
Notice that "sitaram" (the last argument in the `gl-easy-install` command you gave earlier) has read-write permissions on the `gitolite-admin` repository as well as a public key file of the same name.
|
||||
|
||||
The config file syntax for gitolite is liberally documented in `conf/example.conf`, so we'll only mention some highlights here.
|
||||
|
||||
You can group users or repos for convenience. The group names are just like macros; when defining them, it doesn't even matter whether they are projects or users; that distinction is only made when you *use* the "macro".
|
||||
|
||||
@oss_repos = linux perl rakudo git gitolite
|
||||
@secret_repos = fenestra pear
|
||||
|
||||
@admins = scott # Adams, not Chacon, sorry :)
|
||||
@interns = ashok # get the spelling right, Scott!
|
||||
@engineers = sitaram dilbert wally alice
|
||||
@staff = @admins @engineers @interns
|
||||
|
||||
You can control permissions at the "ref" level. In the following example, interns can only push the "int" branch. Engineers can push any branch whose name starts with "eng-", and tags that start with "rc" followed by a digit. And the admins can do anything (including rewind) to any ref.
|
||||
|
||||
repo @oss_repos
|
||||
RW int$ = @interns
|
||||
RW eng- = @engineers
|
||||
RW refs/tags/rc[0-9] = @engineers
|
||||
RW+ = @admins
|
||||
|
||||
The expression after the `RW` or `RW+` is a regular expression (regex) that the refname (ref) being pushed is matched against. So we call it a "refex"! Of course, a refex can be far more powerful than shown here, so don't overdo it if you're not comfortable with perl regexes.
|
||||
|
||||
Also, as you probably guessed, Gitolite prefixes `refs/heads/` as a syntactic convenience if the refex does not begin with `refs/`.
|
||||
|
||||
An important feature of the config file's syntax is that all the rules for a repository need not be in one place. You can keep all the common stuff together, like the rules for all `oss_repos` shown above, then add specific rules for specific cases later on, like so:
|
||||
|
||||
repo gitolite
|
||||
RW+ = sitaram
|
||||
|
||||
That rule will just get added to the ruleset for the `gitolite` repository.
|
||||
|
||||
At this point you might be wondering how the access control rules are actually applied, so let's go over that briefly.
|
||||
|
||||
There are two levels of access control in gitolite. The first is at the repository level; if you have read (or write) access to *any* ref in the repository, then you have read (or write) access to the repository.
|
||||
|
||||
The second level, applicable only to "write" access, is by branch or tag within a repository. The username, the access being attempted (`W` or `+`), and the refname being updated are known. The access rules are checked in order of appearance in the config file, looking for a match for this combination (but remember that the refname is regex-matched, not merely string-matched). If a match is found, the push succeeds. A fallthrough results in access being denied.
|
||||
|
||||
<a name="_Advanced_Access_Control_with_deny_rules_"></a>
|
||||
|
||||
### Advanced Access Control with "deny" rules ###
|
||||
|
||||
So far, we've only seen permissions to be one or `R`, `RW`, or `RW+`. However, gitolite allows another permission: `-`, standing for "deny". This gives you a lot more power, at the expense of some complexity, because now fallthrough is not the *only* way for access to be denied, so the *order of the rules now matters*!
|
||||
|
||||
Let us say, in the situation above, we want engineers to be able to rewind any branch *except* master and integ. Here's how to do that:
|
||||
|
||||
RW master integ = @engineers
|
||||
- master integ = @engineers
|
||||
RW+ = @engineers
|
||||
|
||||
Again, you simply follow the rules top down until you hit a match for your access mode, or a deny. Non-rewind push to master or integ is allowed by the first rule. A rewind push to those refs does not match the first rule, drops down to the second, and is therefore denied. Any push (rewind or non-rewind) to refs other than master or integ won't match the first two rules anyway, and the third rule allows it.
|
||||
|
||||
<a name="_Restricting_pushes_by_files_changed_"></a>
|
||||
|
||||
### Restricting pushes by files changed ###
|
||||
|
||||
In addition to restricting what branches a user can push changes to, you can also restrict what files they are allowed to touch. For example, perhaps the Makefile (or some other program) is really not supposed to be changed by just anyone, because a lot of things depend on it or would break if the changes are not done *just right*. You can tell gitolite:
|
||||
|
||||
repo foo
|
||||
RW = @junior_devs @senior_devs
|
||||
|
||||
RW NAME/ = @senior_devs
|
||||
- NAME/Makefile = @junior_devs
|
||||
RW NAME/ = @junior_devs
|
||||
|
||||
This powerful feature is documented in `conf/example.conf`.
|
||||
|
||||
<a name="_Personal_Branches_"></a>
|
||||
|
||||
### Personal Branches ###
|
||||
|
||||
Gitolite also has a feature called "personal branches" (or rather, "personal branch namespace") that can be very useful in a corporate environment.
|
||||
|
||||
A lot of code exchange in the git world happens by "please pull" requests. In a corporate environment, however, unauthenticated access is a no-no, and a developer workstation cannot do authentication, so you have to push to the central server and ask someone to pull from there.
|
||||
|
||||
This would normally cause the same branch name clutter as in a centralised VCS, plus setting up permissions for this becomes a chore for the admin.
|
||||
|
||||
Gitolite lets you define a "personal" or "scratch" namespace prefix for each developer (for example, `refs/personal/<devname>/*`); see the "personal branches" section in `doc/3-faq-tips-etc.mkd` for details.
|
||||
|
||||
<a name="_Wildcard_repositories_"></a>
|
||||
|
||||
### "Wildcard" repositories ###
|
||||
|
||||
Gitolite allows you to specify repositories with wildcards (actually perl regexes), like, for example `assignments/s[0-9][0-9]/a[0-9][0-9]`, to pick a random example. This is a *very* powerful feature, which has to be enabled by setting `$GL_WILDREPOS = 1;` in the rc file. It allows you to assign a new permission mode ("C") which allows users to create repositories based on such wild cards, automatically assigns ownership to the specific user who created it, allows him/her to hand out R and RW permissions to other users to collaborate, etc. This feature is documented in `doc/wildcard-repositories.mkd`.
|
||||
|
||||
<a name="_Other_Features_"></a>
|
||||
|
||||
### Other Features ###
|
||||
|
||||
We'll round off this discussion with a sampling of other features, all of which, and many more, are described in great detail in the "faqs, tips, etc" and other documents.
|
||||
|
||||
**Logging**: Gitolite logs all successful accesses. If you were somewhat relaxed about giving people rewind permissions (`RW+`) and some kid blew away "master", the log file is a life saver, in terms of easily and quickly finding the SHA that got hosed.
|
||||
|
||||
**Git outside normal PATH**: One extremely useful convenience feature in gitolite is support for git installed outside the normal `$PATH` (this is more common than you think; some corporate environments or even some hosting providers refuse to install things system-wide and you end up putting them in your own directories). Normally, you are forced to make the *client-side* git aware of this non-standard location of the git binaries in some way. With gitolite, just choose a verbose install and set `$GIT_PATH` in the "rc" files. No client-side changes are required after that :-)
|
||||
|
||||
**Access rights reporting**: Another convenient feature is what happens when you try and just ssh to the server. Gitolite shows you what repos you have access to, and what that access may be. Here's an example:
|
||||
|
||||
hello sitaram, the gitolite version here is v1.5.4-19-ga3397d4
|
||||
the gitolite config gives you the following access:
|
||||
R anu-wsd
|
||||
R entrans
|
||||
R W git-notes
|
||||
R W gitolite
|
||||
R W gitolite-admin
|
||||
R indic_web_input
|
||||
R shreelipi_converter
|
||||
|
||||
**Delegation**: For really large installations, you can delegate responsibility for groups of repositories to various people and have them manage those pieces independently. This reduces the load on the main admin, and makes him less of a bottleneck. This feature has its own documentation file in the `doc/` directory.
|
||||
|
||||
**Gitweb support**: Gitolite supports gitweb in several ways. You can specify which repos are visible via gitweb. You can set the "owner" and "description" for gitweb from the gitolite config file. Gitweb has a mechanism for you to implement access control based on HTTP authentication, so you can make it use the "compiled" config file that gitolite produces, which means the same access control rules (for read access) apply for gitweb and gitolite.
|
||||
|
||||
**Mirroring**: Gitolite can help you maintain multiple mirrors, and switch between them easily if the primary server goes down.
|
124
doc/report-output.mkd
Normal file
124
doc/report-output.mkd
Normal file
|
@ -0,0 +1,124 @@
|
|||
# output of the "info" and "expand" commands
|
||||
|
||||
Running "ssh git@server info" or "ssh git@server expand" gives you certain
|
||||
output. This doclet describes the output; you're welcome to help me make it
|
||||
clearer :)
|
||||
|
||||
(Side note: if you installed using the "from-client" method, and you're the
|
||||
administrator, please replace `ssh git@server` with `ssh gitolite`, all
|
||||
through this document).
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_the_info_command">the "info" command</a>
|
||||
* <a href="#_interpreting_the_output">interpreting the output</a>
|
||||
* <a href="#_using_patterns_to_limit_output">using patterns to limit output</a>
|
||||
* <a href="#_the_expand_command">the "expand" command</a>
|
||||
|
||||
----
|
||||
|
||||
<a name="_the_info_command"></a>
|
||||
|
||||
### the "info" command
|
||||
|
||||
Usage:
|
||||
|
||||
ssh git@server info [optional_pattern [list of users]]
|
||||
|
||||
The "info" command shows you all the repos (and repo patterns) in the config
|
||||
file that you have been given any kind of access to. If you supply an
|
||||
optional pattern the output will be limited to repos matching that pattern.
|
||||
If you're an admin you can append a list of users to see their permissions
|
||||
instead of your own; in this mode the pattern is mandatory, even if you just
|
||||
use `.` to cheat.
|
||||
|
||||
Here is a sample output of the info command. There are 3 columns of
|
||||
permissions (create, read, and write) in the output, although the first column
|
||||
is often blank.
|
||||
|
||||
$ ssh git@server info
|
||||
hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc
|
||||
the gitolite config gives you the following access:
|
||||
R W SecureBrowse
|
||||
R W anu-wsd
|
||||
R W entrans
|
||||
@R W git-notes
|
||||
@R W gitolite
|
||||
R W gitolite-admin
|
||||
R W indic_web_input
|
||||
@C R W private/sitaram/[\w.-]+
|
||||
R W proxy
|
||||
@C @R W public/sitaram/[\w.-]+
|
||||
@R_ @W_ testing
|
||||
R W vkc
|
||||
|
||||
<a name="_interpreting_the_output"></a>
|
||||
|
||||
#### interpreting the output
|
||||
|
||||
The meaning of C, R, and W are self-explanatory, but they may be prefixed or
|
||||
suffixed by a symbol:
|
||||
|
||||
* an `@` prefix means "@all" users have been given this permission
|
||||
|
||||
repo foo
|
||||
R = @all
|
||||
|
||||
* a `#` prefix means this user is a "superuser" (think root's shell prompt)
|
||||
and so has access to `@all` repos. Which means you'll see this prefix
|
||||
(or, in some cases, an `&`; see next bullet) for *all* the repos, or none
|
||||
of them
|
||||
|
||||
repo @all
|
||||
R = sitaram
|
||||
|
||||
* an `&` prefix means both of the above are true
|
||||
|
||||
The `_` suffix is special. This says the user has only implicit access (due
|
||||
to one of the `@all` uses), but no explicit access.
|
||||
|
||||
<a name="_using_patterns_to_limit_output"></a>
|
||||
|
||||
#### using patterns to limit output
|
||||
|
||||
Here are a couple of samples with optional patterns:
|
||||
|
||||
$ ssh git@server info git
|
||||
hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc
|
||||
the gitolite config gives you the following access:
|
||||
@R W git-notes
|
||||
@R W gitolite
|
||||
R W gitolite-admin
|
||||
|
||||
$ ssh git@server info admin
|
||||
hello sitaram, the gitolite version here is v1.5.5-24-g2b066fc
|
||||
the gitolite config gives you the following access:
|
||||
R W gitolite-admin
|
||||
|
||||
In "big-config" mode (i.e., when `GL_BIG_CONFIG` is set) the pattern is
|
||||
**mandatory**. You can try and cheat the system by passing in a "." but
|
||||
gitolite truncates the output after 20 results to prevent a DOS.
|
||||
|
||||
The pattern is also mandatory when an admin wants to find out what access some
|
||||
*other* user has, which you may have guessed from the syntax in the "usage"
|
||||
line above.
|
||||
|
||||
<a name="_the_expand_command"></a>
|
||||
|
||||
### the "expand" command
|
||||
|
||||
Usage:
|
||||
|
||||
ssh git@server expand [optional_pattern]
|
||||
|
||||
The "expand" command trawls through all the repositories on the server,
|
||||
limiting to repos matching the pattern you provide (default is all repos
|
||||
found).
|
||||
|
||||
For each repo found, it searches for it in the config -- either the actual
|
||||
repo entry (when the repo is not a wildcard repo), or an entry for the
|
||||
wildcard that matches it -- and reports permissions. It also takes into
|
||||
account extra permissions enabled by the `setperms` command (see
|
||||
doc/wildcard-repositories.mkd). It shows you the "creator" of the repo as
|
||||
an additional column, defaulting to `<gitolite>` if it was not a wildcard
|
||||
repo.
|
71
doc/shell-games.mkd
Normal file
71
doc/shell-games.mkd
Normal file
|
@ -0,0 +1,71 @@
|
|||
# avoiding the shell on the server
|
||||
|
||||
Gitolite now tries to prevent gitolite-admin push privileges from being used
|
||||
to obtain a shell on the server. This was not always the case (older gitolite
|
||||
did not make this distinction), but I've been moving towards this for a while
|
||||
now, and, while there could still be holes in that separation, they will be
|
||||
fixed as and when found.
|
||||
|
||||
Thus, settings that have security implications can be set only from the rc
|
||||
file, which needs to be edited directly on the server. And adding a new hook
|
||||
requires adding it to the *gitolite* clone and running easy install again, or
|
||||
gl-setup, if you used the server-side install method, both of which require
|
||||
shell access.
|
||||
|
||||
While this is great for my main target (corporate environments), some people
|
||||
don't like it. They want to do all of this from the *gitolite-admin* repo,
|
||||
because the security concern mentioned above does not bother them. They don't
|
||||
want to log on to the server to make a change in the rc file or don't want to
|
||||
run easy install to propagate a new set of hooks. In addition, they may want
|
||||
all of these animals versioned in the "gitolite-admin" repo itself, which
|
||||
certainly makes sense.
|
||||
|
||||
So here's how you might do that.
|
||||
|
||||
First, arrange to have all your special files added to the gitolite-admin
|
||||
repo. The best option is to keep all of this in a single subdirectory (let's
|
||||
call it "local" in our example). So your `~/.gitolite.rc` might go into
|
||||
`local/gitolite.rc`, and all your local hooks into `local/hooks` etc. Add
|
||||
them, commit, and push.
|
||||
|
||||
Note: do not create any top level directory called "conf", "contrib", "doc",
|
||||
"hooks", or "src" -- those names are used by gitolite itself.
|
||||
|
||||
Second, create a `post-update.secondary` hook and place it in the *gitolite*
|
||||
clone's `hooks/common` directory, containing the following code:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
GL_ADMINDIR=` cd;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'`
|
||||
|
||||
cp $GL_ADMINDIR/local/gitolite.rc $HOME/.gitolite.rc
|
||||
cp -a $GL_ADMINDIR/local/hooks/* $GL_ADMINDIR/hooks/common
|
||||
$HOME/gitolite-install/src/gl-install -q
|
||||
|
||||
Now run easy-install (or gl-setup) once, and you're done.
|
||||
|
||||
All future changes to the rc file can be done via local/gitolite.rc in the
|
||||
admin repo, and hooks can be added to local/hooks.
|
||||
|
||||
**Warning**: Nothing in gitolite *removes* hooks, so if you delete (or even
|
||||
rename) a script, it still stays on the server -- you'll have to delete them
|
||||
manually from the server.
|
||||
|
||||
----
|
||||
|
||||
So what's this actually doing?
|
||||
|
||||
Well, first, note that `$GL_ADMINDIR` contains files from both gitolite
|
||||
itself, as well as from the gitolite-admin repo. "conf/VERSION", "src",
|
||||
"doc", and "hooks" come from gitolite itself, while the other 2 files in
|
||||
"conf", and all of "keydir" come from the gitolite-admin repo. ("logs"
|
||||
doesn't come from anywhere).
|
||||
|
||||
In addition, any other files in the "master" branch of the gitolite-admin repo
|
||||
get checked out here, which in this case would mean the entire "local/"
|
||||
hierarchy you created above.
|
||||
|
||||
Now, since the "hooks/common" directory is coming from gitolite itself,
|
||||
clearly this is where the internal "install" routine expects to find new or
|
||||
updated hooks to propagate. So you just copy your local hooks (in the
|
||||
"local/hooks" directory) to "hooks/common" and run the installer again. Done!
|
468
doc/ssh-troubleshooting.mkd
Normal file
468
doc/ssh-troubleshooting.mkd
Normal file
|
@ -0,0 +1,468 @@
|
|||
# ssh troubleshooting
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_common_ssh_asks_for_a_password">(common) ssh asks for a password</a>
|
||||
* <a href="#_problems_when_using_package_root_or_non_root_install_methods">problems when using package, root, or non-root install methods</a>
|
||||
* <a href="#_problems_when_using_the_from_client_install_method">problems when using the "from-client" install method</a>
|
||||
* <a href="#_sidebar_why_two_keys_on_client_for_the_admin">(sidebar) why two keys on client for the admin</a>
|
||||
* <a href="#_bypassing_gitolite_without_intending_to">bypassing gitolite without intending to</a>
|
||||
* <a href="#_basic_ssh_troubleshooting_for_the_admin">basic ssh troubleshooting for the admin</a>
|
||||
* <a href="#_windows_issues">windows issues</a>
|
||||
* <a href="#_details">details</a>
|
||||
* <a href="#_files_on_the_server">files on the server</a>
|
||||
* <a href="#_files_on_client">files on client</a>
|
||||
* <a href="#_some_other_tips_and_tricks">some other tips and tricks</a>
|
||||
* <a href="#_giving_shell_access_to_gitolite_users">giving shell access to gitolite users</a>
|
||||
* <a href="#_losing_your_admin_key">losing your admin key</a>
|
||||
* <a href="#_simulating_ssh_copy_id">simulating ssh-copy-id</a>
|
||||
|
||||
----
|
||||
|
||||
----
|
||||
|
||||
This document should help you troubleshoot ssh-related problems in installing
|
||||
and accessing gitolite.
|
||||
|
||||
**This is about all the help I can give you in terms of the ssh aspect of
|
||||
using gitolite. If you're installing gitolite, you're a "system admin", like
|
||||
it or not. Ssh is therefore a necessary skill. Please take the time to learn
|
||||
at least enough to get passwordless access working.**
|
||||
|
||||
**I have spent more than my share of time helping people debug their
|
||||
misconfigured servers, simply because they tried to blame gitolite for their
|
||||
troubles. This stops now. I'd rather spend time on actual gitolite features,
|
||||
code, and documentation.**
|
||||
|
||||
Other resources:
|
||||
|
||||
* people who think this is too hard should take a look at this
|
||||
[transcript][] to **see how simple it *actually* is**.
|
||||
|
||||
* I **strongly** recommend reading [doc/gitolite-and-ssh.mkd][doc9gas],
|
||||
which is a very detailed look at how gitolite uses ssh's features on the
|
||||
server side. Most people don't know ssh as well as they *think* they do;
|
||||
even if you don't have any problems right now, it's worth skimming over.
|
||||
|
||||
* there's a program called `sshkeys-lint` that you can run on your client.
|
||||
Run it without arguments to get help on how to run it and what inputs it
|
||||
needs.
|
||||
|
||||
----
|
||||
|
||||
<a name="_common_ssh_asks_for_a_password"></a>
|
||||
|
||||
### (common) ssh asks for a password
|
||||
|
||||
**NOTE**: This section should be useful to anyone trying to get password-less
|
||||
access working. It is **not** specific to gitolite.
|
||||
|
||||
You have generated a keypair on your workstation (`ssh-keygen`) and copied the
|
||||
public part of it (`~/.ssh/id_rsa.pub`, by default) to the server.
|
||||
|
||||
On the server you have appended this file to `~/.ssh/authorized_keys`. Or you
|
||||
ran something, like the `gl-setup` or `gl-easy-install` steps during a
|
||||
gitolite install, which should have done that for you.
|
||||
|
||||
You now expect to log in without having to type in a password, but when you
|
||||
try, you are being asked for a password.
|
||||
|
||||
This is a quick checklist:
|
||||
|
||||
* make sure you're being asked for a password and not a pass*phrase*. Do
|
||||
not confuse or mistake a prompt saying `Enter passphrase for key
|
||||
'/home/sitaram/.ssh/id_rsa':` for a password prompt from the remote
|
||||
server!
|
||||
|
||||
When you create an ssh keypair using `ssh-keygen`, you have the option of
|
||||
protecting it with a passphrase. When you subsequently use that keypair
|
||||
to access a remote host, your *local* ssh client needs to unlock the
|
||||
corresponding private key, and ssh will probably ask for the passphrase
|
||||
you set when you created the keypair.
|
||||
|
||||
You have two choices to avoid this prompt every time you try to use the
|
||||
private key. The first is to create keypairs *without* a passphrase (just
|
||||
hit enter when prompted for one). **Be sure to add a passphrase later,
|
||||
once everything is working, using `ssh-keygen -p`**.
|
||||
|
||||
The second is to use `ssh-agent` (or `keychain`, which in turn uses
|
||||
`ssh-agent`) or something like that to manage your keys. Other than
|
||||
discussing one more potential trouble-spot with ssh-agent (see below),
|
||||
further discussion of ssh-agent/keychain is out of scope of this document.
|
||||
|
||||
* make sure the right private key is being offered. Run ssh in very
|
||||
verbose mode and look for the word "Offering", like so:
|
||||
|
||||
ssh -vvvv user@host pwd 2> >(grep -i offer)
|
||||
|
||||
If some keys *are* being offered, but not the key that was supposed to be
|
||||
used, you may be using ssh-agent; see next bullet.
|
||||
|
||||
If you don't see any offers being made at all, then you probably don't
|
||||
have any protocol 2 keys in your `~/.ssh` (names are `id_rsa` or `id_dsa`,
|
||||
with corresponding `.pub` files).
|
||||
|
||||
* If `ssh-add -l` responds with either "The agent has no identities." or
|
||||
"Could not open a connection to your authentication agent.", then you can
|
||||
skip this bullet.
|
||||
|
||||
However, if `ssh-add -l` lists *any* keys at all, then something weird
|
||||
happens. Due to a quirk in ssh-agent, ssh will now *only* use one of
|
||||
those keys, *even if you explicitly ask* for some other key to be used.
|
||||
|
||||
In that case, add the key you want using `ssh-add ~/.ssh/mykey` and try
|
||||
the access again.
|
||||
|
||||
* ssh is very sensitive to permissions. An extremely conservative setup is
|
||||
given below, but be sure to do this on **both the client and the server**:
|
||||
|
||||
cd $HOME
|
||||
chmod go-rwx .
|
||||
chmod -R go-rwx .ssh
|
||||
|
||||
* actually, every component of the path to `~/.ssh/authorized_keys` all the
|
||||
way upto the root directory must be at least `chmod go-w`. So be sure to
|
||||
check `/` and `/home` also.
|
||||
|
||||
* while you're doing this, make sure the owner and group info for each of
|
||||
these components are correct. `ls -ald ~ ~/.ssh ~/.ssh/authorized_keys`
|
||||
will tell you what they are.
|
||||
|
||||
* if all that fails, log onto the server as root, `cd /var/log`, and look
|
||||
for a file called `auth.log` or `secure` or some such name. Look inside
|
||||
this file for messages matching the approximate time of your last attempt
|
||||
to login, to see if they tell you what is the problem.
|
||||
|
||||
----
|
||||
|
||||
<a name="_problems_when_using_package_root_or_non_root_install_methods"></a>
|
||||
|
||||
### problems when using package, root, or non-root install methods
|
||||
|
||||
This section applies if you installed using any of the [first 3 methods][o3]
|
||||
of install.
|
||||
|
||||
In these 3 modes, installation is done on the server, by logging in as some
|
||||
other user and doing and `su - git`. The admin's workstation has only one key
|
||||
that is known to the server's authkeys file, and this key invokes gitolite.
|
||||
**Note** that this key is not supposed to get you a shell; it is supposed to
|
||||
invoke gitolite.
|
||||
|
||||
As a result, it's a lot easier to debug. Just run `ssh git@server info`. If
|
||||
this get you the [gitolite version and access info][repout], everything is
|
||||
fine. If it asks you for a password, see the very first section of this
|
||||
document for help.
|
||||
|
||||
If it gets you the GNU info command output, you have shell access. This
|
||||
probably means you had passwordless shell access to the server *before* you
|
||||
were added as a gitolite user, and you sent that same key to your gitolite
|
||||
admin to include in the admin repo. This won't work -- the same key appears
|
||||
twice in the authkeys file now, and since the ssh server will always use the
|
||||
first match, the second occurrence (which invokes gitolite) is ignored.
|
||||
|
||||
You'll have to (create and) use a different keypair for gitolite access.
|
||||
|
||||
<a name="_problems_when_using_the_from_client_install_method"></a>
|
||||
|
||||
### problems when using the "from-client" install method
|
||||
|
||||
This section applies if you installed using the [from-client method][fc].
|
||||
|
||||
This method of install is unique in that the admin will have 2 distinct keys
|
||||
to access the server. The default key (`~/.ssh/id_rsa`) is used to get a
|
||||
shell prompt and to run commands; for example, `gl-easy-install` uses this key
|
||||
to do all its server-side work.
|
||||
|
||||
In addition, there is a named key created just to invoke gitolite instead of
|
||||
starting a shell. The name is whatever you gave as the third argument to the
|
||||
`gl-easy-install` command (for example, `~/.ssh/sitaram.pub` in my case).
|
||||
|
||||
Finally, a `host gitolite` para is added to `~/.ssh/config`:
|
||||
|
||||
host gitolite
|
||||
user git
|
||||
hostname server
|
||||
identityfile ~/.ssh/sitaram
|
||||
|
||||
so that you can use `gitolite:reponame` as the URL to make ssh use the named
|
||||
key.
|
||||
|
||||
All this applies *only* to the admin. Normal users will only have one key and
|
||||
do not need any of this.
|
||||
|
||||
<a name="twokeys"></a>
|
||||
|
||||
<a name="_sidebar_why_two_keys_on_client_for_the_admin"></a>
|
||||
|
||||
#### (sidebar) why two keys on client for the admin
|
||||
|
||||
> There are two types of access the admin will make to the server: a normal
|
||||
> login, to get a shell prompt, and gitolite access (clone/fetch/push etc).
|
||||
> The first access needs an authkeys line *without* any "command="
|
||||
> restrictions, while the second requires a line *with* such a restriction.
|
||||
|
||||
> And we can't use the same key for both because there is no way to
|
||||
> disambiguate them; the ssh server will always (*always*) pick the first
|
||||
> one in sequence when the key is offered by the ssh client.
|
||||
|
||||
> So the next question is usually "I have other ways to get a shell on that
|
||||
> account (like `su - git` from some other account), so why do I need a key
|
||||
> for shell access at all?"
|
||||
|
||||
> The answer to this is that the "easy install" script, being written for
|
||||
> the most general case, needs shell access via ssh to do its stuff. If you
|
||||
> have access otherwise, you really should use one of the other 3 install
|
||||
> methods to install gitolite. Please see the [install doc][install] for
|
||||
> details.
|
||||
|
||||
<a name="_bypassing_gitolite_without_intending_to"></a>
|
||||
|
||||
#### bypassing gitolite without intending to
|
||||
|
||||
These problems happen to the person who has **utterly failed** to read/heed
|
||||
the message that shows up at the end of running the `gl-easy-install` command.
|
||||
Both these problems are caused by using the wrong key, thus **bypassing
|
||||
gitolite completely**:
|
||||
|
||||
* you get `fatal: 'reponame' does not appear to be a git repository`, and
|
||||
yet you are sure 'reponame' exists, you haven't mis-spelled it, etc.
|
||||
|
||||
* you are able to clone repositories but are unable to push changes back
|
||||
(the error complains about the `GL_RC` environment variable not being set,
|
||||
and the `hooks/update` failing in some way).
|
||||
|
||||
Let us recap the message that appears on a successful run of the "easy-install"
|
||||
program; it looks something like this (with suitable values substituted for
|
||||
`<user>`, `<server>`, and `<port>`):
|
||||
|
||||
IMPORTANT NOTE -- PLEASE READ!!!
|
||||
*Your* URL for cloning any repo on this server will be
|
||||
gitolite:reponame.git
|
||||
|
||||
*Other* users you set up will have to use
|
||||
<user>@<server>:reponame.git
|
||||
However, if your server uses a non-standard ssh port, they should use
|
||||
ssh://<user>@<server>:<port>/reponame.git
|
||||
|
||||
If this is your first time installing gitolite, please also:
|
||||
tail -31 src/gl-easy-install
|
||||
for next steps.
|
||||
|
||||
The first error above happens if you use `git@server:reponame` instead of
|
||||
`gitolite:reponame`. All your repos are actually in a subdirectory pointed to
|
||||
by `$REPO_BASE` in the rc file (default: `repositories`). Gitolite internally
|
||||
prefixes this before calling the actual git command you invoked, but since
|
||||
you're bypassing gitolite completely, this prefixing does not happen, and so
|
||||
the repo is not found.
|
||||
|
||||
The second error happens if you use `git@server:repositories/reponame.git`
|
||||
(assuming default `$REPO_BASE` setting) -- that is, you used the full unix
|
||||
path. Since the "prefixing" mentioned above is not required, the shell finds
|
||||
the repo and clones ok. But when you push, gitolite's **update hook** kicks
|
||||
in, and fails to run because some of the environment variables it is
|
||||
expecting are not present.
|
||||
|
||||
<a name="_basic_ssh_troubleshooting_for_the_admin"></a>
|
||||
|
||||
#### basic ssh troubleshooting for the admin
|
||||
|
||||
Otherwise, run these checks:
|
||||
|
||||
1. `ssh git@server` should get you a command line *without* asking for a
|
||||
password.
|
||||
|
||||
If it asks you for a password, then your `id_rsa` keypair changed after
|
||||
you ran the easy install, or someone fiddled with the
|
||||
`~/.ssh/authorized_keys` file on the server.
|
||||
|
||||
If it prints the gitolite version and access info (see
|
||||
[doc/report-output.mkd][repout]), you managed to overwrite the `id_rsa`
|
||||
keypair with the `sitaram` keypair, or something equally weird.
|
||||
|
||||
2. `ssh gitolite info` should print some gitolite version and access info.
|
||||
If you get the output of the GNU info command instead, you probably reused
|
||||
your `id_rsa` keypair as your `sitaram` keypair, or overwrote the
|
||||
`sitaram` keypair with the `id_rsa` keypair.
|
||||
|
||||
There are many ways to fix this, depending on where and what the damage is.
|
||||
The most generic way (and therefore time-taking) is to re-install gitolite
|
||||
from scratch:
|
||||
|
||||
* make a backup of your gitolite-admin repo clone somewhere (basically your
|
||||
`keydir/*.pub` and your `conf/gitolite.conf`). If necessary get these
|
||||
files from the server's `~/.gitolite` directory.
|
||||
* log on to the server somehow (using some other account, using a password,
|
||||
su-ing in, etc) and delete `~/.ssh/authorized_keys`. Rename or move aside
|
||||
`~/.gitolite` so that also looks like it is missing.
|
||||
* back on your workstation, make sure you have 2 keypairs (`id_rsa` and
|
||||
`sitaram`, along with corresponding `.pub` files). Create them if needed.
|
||||
Also make sure they are *different* and not a copy of each other :-)
|
||||
* install gitolite normally:
|
||||
* run `ssh-copy-id -i ~/.ssh/id_rsa git@server` to get passwordless
|
||||
access to the server. (Mac users may have to do this step manually)
|
||||
* make sure `ssh git@server pwd` prints the `$HOME` of `git@server`
|
||||
**without** asking for a password. Do not proceed till this works.
|
||||
* run easy install again, (in my case: `cd gitolite-source;
|
||||
src/gl-easy-install -q git server sitaram`)
|
||||
* go to your gitolite-admin repo clone, and copy `conf/gitolite.conf` and
|
||||
`keydir/*.pub` from your backup to this directory
|
||||
* copy (be sure to overwrite!) `~/.ssh/sitaram.pub` also to keydir
|
||||
* now `git add keydir; git commit; git push -f`
|
||||
|
||||
That's a long sequence but it should work.
|
||||
|
||||
<a name="_windows_issues"></a>
|
||||
|
||||
### windows issues
|
||||
|
||||
On windows, I have only used msysgit, and the openssh that comes with it.
|
||||
Over time, I have grown to distrust putty/plink due to the number of people
|
||||
who seem to have trouble when those beasts are involved (I myself have never
|
||||
used them for any kind of git access). If you have unusual ssh problems that
|
||||
just don't seem to have any explanation, try removing all traces of
|
||||
putty/plink, including environment variables, etc., and then try again.
|
||||
|
||||
If you can offer an *authoritative* account of the complications involved, and
|
||||
how to resolve them and get things working, I'd be happy to credit you and
|
||||
include it, either directly here if it is short enough or just an external
|
||||
link, or in contrib/ if it's a longer piece of text.
|
||||
|
||||
<a name="_details"></a>
|
||||
|
||||
### details
|
||||
|
||||
Here's how it all hangs together.
|
||||
|
||||
<a name="_files_on_the_server"></a>
|
||||
|
||||
#### files on the server
|
||||
|
||||
* the authkeys file; this contains one line containing the pubkey of each
|
||||
user who is permitted to login without a password.
|
||||
|
||||
Pubkey lines that give shell access look like this:
|
||||
|
||||
ssh-rsa AAAAB3NzaC[snip]uPjrUiAUew== /home/sitaram/.ssh/id_rsa
|
||||
|
||||
On a typical server there will be only one or two of these lines.
|
||||
|
||||
Note that the last bit (`/home/sitaram/.ssh/id_rsa`) is purely a *comment*
|
||||
field and can be anything. Also, the actual lines are much longer, about
|
||||
400 characters; I snipped 'em in the middle, as you can see.
|
||||
|
||||
In contrast, pubkey lines that give access to git repos hosted by gitolite
|
||||
look like this:
|
||||
|
||||
command="[some path]src/gl-auth-command sitaram",[some restrictions] ssh-rsa AAAAB3NzaC[snip]s18OnB42oQ== sitaram@sita-lt
|
||||
|
||||
You will have many more of these lines -- one for every pubkey file in
|
||||
`keydir/` of your gitolite-admin repo, with the corresponding username in
|
||||
place of "sitaram" in the example above.
|
||||
|
||||
The "command=" at the beginning ensures that when someone with the
|
||||
corresponding private key logs in, they don't get a shell. Instead, the
|
||||
`gl-auth-command` program is run, and (in this example) is given the
|
||||
argument `sitaram`. This is how gitolite is invoked, (and is told the
|
||||
user logging in is "sitaram").
|
||||
|
||||
<a name="_files_on_client"></a>
|
||||
|
||||
#### files on client
|
||||
|
||||
* default keypair; used to get shell access to servers. You would have
|
||||
copied this pubkey to the gitolite server in order to log in without a
|
||||
password. (On Linux systems you may have used `ssh-copy-id` to do that).
|
||||
You would have done this *before* you ran the easy install script, because
|
||||
otherwise easy install won't run!
|
||||
|
||||
~/.ssh/id_rsa
|
||||
~/.ssh/id_rsa.pub
|
||||
|
||||
* gitolite keypair; the "sitaram" in this is the 3rd argument to the
|
||||
`src/gl-easy-install` command you ran; the easy install script does the
|
||||
rest
|
||||
|
||||
~/.ssh/sitaram
|
||||
~/.ssh/sitaram.pub
|
||||
|
||||
* config file; this file has an entry for gitolite access if you install
|
||||
usine the "from-client" method. (See above for example "host gitolite"
|
||||
para in the ssh config file).
|
||||
|
||||
This is needed because this is the only way to force git to use a
|
||||
non-default keypair (unlike command line ssh, which can be given the `-i
|
||||
~/.ssh/sitaram` flag to do so).
|
||||
|
||||
<a name="_some_other_tips_and_tricks"></a>
|
||||
|
||||
### some other tips and tricks
|
||||
|
||||
<a name="_giving_shell_access_to_gitolite_users"></a>
|
||||
|
||||
#### giving shell access to gitolite users
|
||||
|
||||
We've managed (thanks to an idea from Jesse Keating) to make it possible for a
|
||||
single key to allow both gitolite access *and* shell access.
|
||||
|
||||
This is done by copying the pubkey (to which you want to give shell access) to
|
||||
the server and running either
|
||||
|
||||
cd $HOME/.gitolite # assuming default $GL_ADMINDIR in ~/.gitolite.rc
|
||||
src/gl-tool shell-add ~/foo.pub
|
||||
|
||||
or
|
||||
|
||||
gl-tool shell-add ~/foo.pub
|
||||
|
||||
The first method is to be used if you used the **user-install** mode, while
|
||||
the second method is for the **system-install followed by user-setup** mode
|
||||
(see doc/1-INSTALL.mkd, section on "install methods", for more on this)
|
||||
|
||||
**IMPORTANT UPGRADE NOTE**: previous implementations of this feature were
|
||||
crap. There was no easy/elegant way to ensure that someone who had repo admin
|
||||
access would not manage to get himself shell access.
|
||||
|
||||
Giving someone shell access requires that you should have shell access in the
|
||||
first place, so the simplest way is to enable it from the server side only.
|
||||
|
||||
<a name="_losing_your_admin_key"></a>
|
||||
|
||||
#### losing your admin key
|
||||
|
||||
If you lost the admin key, and need to re-establish ownership of the
|
||||
gitolite-admin repository with a fresh key, take a look at the
|
||||
`src/gl-dont-panic` program. You will need shell access to the server of
|
||||
course. Run it without arguments to get instructions.
|
||||
|
||||
<a name="_simulating_ssh_copy_id"></a>
|
||||
|
||||
#### simulating ssh-copy-id
|
||||
|
||||
don't have `ssh-copy-id`? This is broadly what that command does, if you want
|
||||
to replicate it manually. The input is your pubkey, typically
|
||||
`~/.ssh/id_rsa.pub` from your client/workstation.
|
||||
|
||||
* it copies it to the server as some file
|
||||
|
||||
* it appends that file to `~/.ssh/authorized_keys` on the server
|
||||
(creating it if it doesn't already exist)
|
||||
|
||||
* it then makes sure that all these files/directories have go-w perms
|
||||
set (assuming user is "git"):
|
||||
|
||||
/home/git/.ssh/authorized_keys
|
||||
/home/git/.ssh
|
||||
/home/git
|
||||
|
||||
[Actually, `sshd` requires that even directories *above* `~` (`/`, `/home`,
|
||||
typically) also must be `go-w`, but that needs root. And typically
|
||||
they're already set that way anyway. (Or if they're not, you've got
|
||||
bigger problems than gitolite install not working!)]
|
||||
|
||||
[doc9gas]: http://github.com/sitaramc/gitolite/blob/pu/doc/gitolite-and-ssh.mkd
|
||||
[install]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd
|
||||
[o3]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd#methods
|
||||
[fc]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd#fc
|
||||
[urls]: http://github.com/sitaramc/gitolite/blob/pu/doc/1-INSTALL.mkd#URLs_for_gitolite_managed_repos
|
||||
[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd
|
||||
[transcript]: http://github.com/sitaramc/gitolite/blob/pu/doc/install-transcript.mkd
|
67
doc/uninstall.mkd
Normal file
67
doc/uninstall.mkd
Normal file
|
@ -0,0 +1,67 @@
|
|||
# uninstalling gitolite
|
||||
|
||||
Sometimes you might find gitolite is overkill -- you have only one user
|
||||
(yourself) pushing maybe. Or maybe gitolite is just not enough -- you want a
|
||||
web-based front end that users can use to manage their keys themselves, etc.,
|
||||
in which case you'd probably switch to [github][g1], [girocco][g2],
|
||||
[indefero][g3] or [gitorious][g4]. [Gerrit][g5] is quite nice too, if you
|
||||
want collaborative code review there's nothing like it. Either way, you'd
|
||||
like to uninstall gitolite.
|
||||
|
||||
[g1]: http://github.com
|
||||
[g2]: http://repo.or.cz/w/girocco.git
|
||||
[g3]: http://www.indefero.net/
|
||||
[g4]: http://gitorious.com/
|
||||
[g5]: http://code.google.com/p/gerrit/
|
||||
|
||||
Uninstalling gitolite is fairly easy, although it is manual. (We'll assume
|
||||
`$REPO_BASE` in the rc file was left at its default of `~/repositories`; if
|
||||
not, adjust accordingly):
|
||||
|
||||
**server side tasks**
|
||||
|
||||
* edit `~/.ssh/authorized_keys` and delete the `# gitolite start` and `#
|
||||
gitolite end` markers and all the lines between them. This will prevent
|
||||
any of your users from attempting a push while you are doing this.
|
||||
|
||||
If you are the only user, and/or *need* one or more of those keys to
|
||||
continue to access this account (like if one of them is your laptop or
|
||||
your home desktop etc.) then instead of deleting the line you can just
|
||||
delete everything upto but not including the words "ssh-rsa" or "ssh-dss".
|
||||
|
||||
* Now remove (or move aside or rename to something else if you're paranoid)
|
||||
the following files and directories.
|
||||
|
||||
~/.gitolite
|
||||
~/.gitolite.rc
|
||||
~/repositories/gitolite-admin.git
|
||||
|
||||
* You can remove all of `~/repositories` if you have not really started
|
||||
using gitolite properly yet; that's your choice.
|
||||
|
||||
If you *do* need to preserve the other repos and continue to use them,
|
||||
remove all the `update` hooks that git installs on each repository. The
|
||||
easiest way is:
|
||||
|
||||
find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f
|
||||
|
||||
but you can do it manually if you want to be careful.
|
||||
|
||||
**client side tasks**
|
||||
|
||||
* Any remote users that still have access must update their clone's remote
|
||||
URLs (edit `.git/config` in the repo) to prefix `repositories/` before the
|
||||
actual path used, in order for the remote to still work. This is because
|
||||
you'll now be accessing it through plain ssh, which means you have to give
|
||||
it the full path.
|
||||
|
||||
* Finally, you as the gitolite admin will probably have a host stanza for
|
||||
"gitolite" in your *client*'s `~/.ssh/config`. Find and delete lines that
|
||||
look like this:
|
||||
|
||||
host gitolite
|
||||
user git
|
||||
hostname your.server
|
||||
port 22
|
||||
identityfile ~/.ssh/your-gitolite-admin-username
|
||||
|
41
doc/who-uses-it.mkd
Normal file
41
doc/who-uses-it.mkd
Normal file
|
@ -0,0 +1,41 @@
|
|||
# who uses gitolite
|
||||
|
||||
> > If you're using gitolite and find it very useful in some way, I would
|
||||
> > love to describe your use of it or add a link to your own description
|
||||
> > of it here. Of course, you can anonymise it as much as you need to.
|
||||
|
||||
The **Fedora Project** controls access to over 10,000 package management
|
||||
repositories accessed by over 1,000 package maintainers [using
|
||||
gitolite][fedora]. This is probably the largest *confirmed* gitolite
|
||||
installation anywhere. The whole [big-config][bc] thing was initially done
|
||||
for them (their config file was so big that without the big-config changes
|
||||
gitolite would just run out of memory and die!).
|
||||
|
||||
[fedora]: http://lists.fedoraproject.org/pipermail/devel-announce/2010-July/000647.html
|
||||
[bc]: http://github.com/sitaramc/gitolite/blob/pu/doc/big-config.mkd
|
||||
|
||||
The **KDE project** is currently [testing][kdet] gitolite to see if it will
|
||||
suit their needs (in combination with redmine for issue tracking and
|
||||
reviewboard for code review), after an initial [review of alternatives][kdera]
|
||||
by a core group. Apart from the usual access control, the KDE folks will be
|
||||
using many of the "ad hoc repo creation" features enabled by wildrepos and the
|
||||
accompanying commands. Some of the changes to the "admin defined commands"
|
||||
were inspired by KDE's needs.
|
||||
|
||||
[kdet]: http://www.omat.nl/2010/07/07/move-to-git-the-progress-so-far/
|
||||
[kdera]: http://permalink.gmane.org/gmane.comp.kde.scm-interest/1437
|
||||
|
||||
**Prof. Hiren Patel** of the University of Waterloo is responsible for the
|
||||
existence of the fairly popular "[wildrepos][wild]" feature. The
|
||||
documentation was pretty much written with his use case in mind, but of course
|
||||
it turns out to be useful for a lot of people.
|
||||
|
||||
In fact, he surprised the heck out of me recently by saying that if it hadn't
|
||||
been for this feature, he might not have used git itself -- which is a pretty
|
||||
serious compliment if you think about the magnitude of the git project and my
|
||||
little one-man show!
|
||||
|
||||
He explains his use of it [here][hiren].
|
||||
|
||||
[wild]: http://github.com/sitaramc/gitolite/blob/pu/doc/wildcard-repositories.mkd
|
||||
[hiren]: http://ece.uwaterloo.ca/~hdpatel/uwhtml/?p=470
|
288
doc/wildcard-repositories.mkd
Normal file
288
doc/wildcard-repositories.mkd
Normal file
|
@ -0,0 +1,288 @@
|
|||
# repositories named with wildcards
|
||||
|
||||
***IMPORTANT NOTE***:
|
||||
|
||||
This feature may be somewhat "brittle" in terms of security. Creating
|
||||
repositories based on wild cards, giving "ownership" to the specific user who
|
||||
created it, allowing him/her to hand out R and RW permissions to other users
|
||||
to collaborate, all these are possible. And any of these could have a bug in
|
||||
it. I haven't found any yet, but that doesn't mean there aren't any.
|
||||
|
||||
----
|
||||
|
||||
In this document:
|
||||
|
||||
* <a href="#_quick_introduction">quick introduction</a>
|
||||
* <a href="#_rc_file_setting_required">rc file setting required</a>
|
||||
* <a href="#_examples_of_wildcard_repos">examples of wildcard repos</a>
|
||||
* <a href="#_wildcard_repos_with_creator_name_in_them">wildcard repos with creator name in them</a>
|
||||
* <a href="#_wildcard_repos_without_creator_name_in_them">wildcard repos without creator name in them</a>
|
||||
* <a href="#_side_note_line_anchored_regexes">side-note: line-anchored regexes</a>
|
||||
* <a href="#_contrast_with_refexes">contrast with refexes</a>
|
||||
* <a href="#_handing_out_rights_to_wildcard_matched_repos">handing out rights to wildcard-matched repos</a>
|
||||
* <a href="#_setting_a_gitweb_description_for_a_wildcard_matched_repo">setting a gitweb description for a wildcard-matched repo</a>
|
||||
* <a href="#_reporting">reporting</a>
|
||||
* <a href="#_how_it_actually_works">how it actually works</a>
|
||||
|
||||
----
|
||||
|
||||
This document is mostly "by example".
|
||||
|
||||
----
|
||||
|
||||
<a name="_quick_introduction"></a>
|
||||
|
||||
### quick introduction
|
||||
|
||||
The wildrepos feature allows you to specify access control rules using regular
|
||||
expression patterns, so you can have many actual repos being served by a
|
||||
single set of rules in the config file. The regex pattern can also include
|
||||
the word `CREATOR` in it, allowing you to parametrise the name of the user
|
||||
creating the repo. The examples below will make this clearer.
|
||||
|
||||
<a name="_rc_file_setting_required"></a>
|
||||
|
||||
### rc file setting required
|
||||
|
||||
This feature requires that you set `$GL_WILDREPOS` to "1" in `~/.gitolite.rc`
|
||||
on the server. Please search for that variable and see comments around it in
|
||||
`conf/example.gitolite.rc` for more information on this.
|
||||
|
||||
<a name="_examples_of_wildcard_repos"></a>
|
||||
|
||||
### examples of wildcard repos
|
||||
|
||||
As the introduction said, you can include the word `CREATOR` in the regex
|
||||
pattern, though it is not mandatory. We'll look at examples of both types of
|
||||
usage.
|
||||
|
||||
Which of these alternatives you choose depends on your needs, and the social
|
||||
aspects of your environment. Including the creator name in the pattern keeps
|
||||
users rigidly separated from each others repos, and is good for a largely
|
||||
autonomous collection of users with a high probability of repo name clashes.
|
||||
|
||||
Omitting the creator name from the pattern puts the repos in a common
|
||||
namespace, and is suitable for environments where it is not very important to
|
||||
keep track of who actually created the repo (except for granting access), but
|
||||
needs more communication / co-operation among the users to avoid repo name
|
||||
clashes.
|
||||
|
||||
<a name="_wildcard_repos_with_creator_name_in_them"></a>
|
||||
|
||||
#### wildcard repos with creator name in them
|
||||
|
||||
Here's an example snippet:
|
||||
|
||||
@prof = u1
|
||||
@TAs = u2 u3
|
||||
@students = u4 u5 u6
|
||||
|
||||
repo assignments/CREATOR/a[0-9][0-9]
|
||||
C = @students
|
||||
RW+ = CREATOR
|
||||
RW = WRITERS @TAs
|
||||
R = READERS @prof
|
||||
|
||||
For now, ignore the special usernames READERS and WRITERS, and just create a
|
||||
new repo, as user "u4" (a student):
|
||||
|
||||
$ git clone git@server:assignments/u4/a12
|
||||
Initialized empty Git repository in /home/sitaram/t/a12/.git/
|
||||
Initialized empty Git repository in /home/gitolite/repositories/assignments/u4/a12.git/
|
||||
warning: You appear to have cloned an empty repository.
|
||||
|
||||
Notice the *two* empty repo inits, and the order in which they occur ;-)
|
||||
|
||||
<a name="_wildcard_repos_without_creator_name_in_them"></a>
|
||||
|
||||
#### wildcard repos without creator name in them
|
||||
|
||||
Here's how the same example would look if you did not want the CREATOR's name
|
||||
to be part of the actual repo name.
|
||||
|
||||
repo assignments/a[0-9][0-9]
|
||||
C = @students
|
||||
RW+ = CREATOR
|
||||
RW = WRITERS @TAs
|
||||
R = READERS @prof
|
||||
|
||||
We haven't changed anything except the repo name pattern. This means that the
|
||||
first student that creates, say, `assignments/a12` becomes the owner.
|
||||
Mistakes (such as claiming a12 instead of a13) need to be rectified by an
|
||||
admin logging on to the back end, though it's not too difficult.
|
||||
|
||||
You could also repace the C line like this:
|
||||
|
||||
C = @TAs
|
||||
|
||||
and have a TA create the repos in advance.
|
||||
|
||||
In either case, they could then use the `setperms` feature to specify which
|
||||
users are "READERS" and which are "WRITERS". See later for details.
|
||||
|
||||
<a name="_side_note_line_anchored_regexes"></a>
|
||||
|
||||
### side-note: line-anchored regexes
|
||||
|
||||
A regex like
|
||||
|
||||
repo assignments/S[0-9]+/A[0-9]+
|
||||
|
||||
would match `assignments/S02/A37`. It will not match `assignments/S02/ABC`,
|
||||
or `assignments/S02/a37`, obviously.
|
||||
|
||||
But you may be surprised to find that it does not match even
|
||||
`assignments/S02/A37/B99`. This is because internally, gitolite
|
||||
*line-anchors* the given regex; so that regex actually becomes
|
||||
`^assignments/S[0-9]+/A[0-9]+$` -- notice the line beginning and ending
|
||||
metacharacters.
|
||||
|
||||
<a name="_contrast_with_refexes"></a>
|
||||
|
||||
#### contrast with refexes
|
||||
|
||||
Just for interest, note that this is in contrast to the refexes for the normal
|
||||
"branch" permissions, as described in `conf/example.conf` and elsewhere.
|
||||
These "refexes" are only anchored at the start; a pattern like
|
||||
`refs/heads/master` actually can match `refs/heads/master01/bar` as well, even
|
||||
if no one will actually push such a branch! You can anchor both sides if you
|
||||
really care, by using `master$` instead of `master`, but that is *not* the
|
||||
default for refexes.
|
||||
|
||||
<a name="_handing_out_rights_to_wildcard_matched_repos"></a>
|
||||
|
||||
### handing out rights to wildcard-matched repos
|
||||
|
||||
In the examples above, we saw two special "user" names: READERS and WRITERS.
|
||||
The permissions they have are controlled by the config file, but ***who is
|
||||
part of this list*** is controlled by the person who created the repository.
|
||||
|
||||
The use case is that, although our toy example has only 3 students, in reality
|
||||
there will be a few dozen, but each assignment will be worked on only by a
|
||||
handful from among those. This allows the creator to take ad hoc sets of
|
||||
users from among the actual users in the system, and place them into one of
|
||||
two categories (whose permissions are, in this example, R and RW
|
||||
respectively). In theory you could do the same thing by creating lots of
|
||||
little "assignment-NN" groups in the config file but that may be a little too
|
||||
cumbersome for non-secret environments.
|
||||
|
||||
Create a small text file that contains the permissions you desire:
|
||||
|
||||
$ cat > myperms
|
||||
R u5
|
||||
RW u6
|
||||
(hit ctrl-d here)
|
||||
|
||||
...and use the new "setperms" command to set permissions for your repo:
|
||||
|
||||
$ ssh git@server setperms assignments/u4/a12 < myperms
|
||||
New perms are:
|
||||
R u5
|
||||
RW u6
|
||||
|
||||
'setperms' will helpfully print what the new permissions are but you can also
|
||||
use 'getperms' to check:
|
||||
|
||||
$ ssh git@server getperms assignments/u4/a12
|
||||
R u5
|
||||
RW u6
|
||||
|
||||
The following points are important:
|
||||
|
||||
* note the syntax of the commands; it's not a "git" command, and there's no
|
||||
`:` like in a repo URL. The first space-separated word is R or RW, and
|
||||
the rest are simple usernames.
|
||||
|
||||
* whoever you specify as "R" will match the special user READERS. "RW" will
|
||||
match WRITERS.
|
||||
|
||||
<a name="_setting_a_gitweb_description_for_a_wildcard_matched_repo"></a>
|
||||
|
||||
### setting a gitweb description for a wildcard-matched repo
|
||||
|
||||
Similar to the getperms/setperms commands, there are the getdesc/setdesc
|
||||
commands, thanks to Teemu.
|
||||
|
||||
<a name="_reporting"></a>
|
||||
|
||||
### reporting
|
||||
|
||||
In order to see what repositories were created from a wildcard, use the
|
||||
"expand" command, described briefly in [doc/report-output.mkd][repout].
|
||||
|
||||
<a name="_how_it_actually_works"></a>
|
||||
|
||||
### how it actually works
|
||||
|
||||
This section tells you what is happening inside gitolite so you can understand
|
||||
this feature better. Let's use the config example at the beginning of this
|
||||
document:
|
||||
|
||||
repo assignments/CREATOR/a[0-9][0-9]
|
||||
C = @students
|
||||
RW+ = CREATOR
|
||||
RW = WRITERS @TAs
|
||||
R = READERS @prof
|
||||
|
||||
First we find the set of rules to apply. This involves replacing the special
|
||||
words CREATOR, WRITERS, and READERS with appropriate usernames to derive an
|
||||
"effective" ruleset for the repo in question.
|
||||
|
||||
For a **new** repo, replace the word CREATOR in all repo patterns and rules
|
||||
with the name of the invoking user.
|
||||
|
||||
> (Note: this is why you should never use `C = CREATOR`; it becomes `C =
|
||||
> invoking_user`! Unless you really want to allow *all* users to create
|
||||
> repos, you should restrict "C" perms to an actual user or set of users,
|
||||
> like in the examples in this document).
|
||||
|
||||
For an **existing** repo, do the same but replace with the name of the user
|
||||
who actually *created* the repo (this name is recorded in a special file in
|
||||
the repo directory when the repo is first created, so it is available).
|
||||
|
||||
Now find a repo pattern that matches the actual reponame being pushed -- this
|
||||
tells you which set of rules to apply. There can be multiple matches; if so,
|
||||
they will all be applied in the sequence they appear in the config file.
|
||||
|
||||
If the invoking user has been given "RW" permissions using `setperms`, all
|
||||
occurrences of the word WRITERS are replaced by the invoking username.
|
||||
Otherwise -- and this includes the "new repo" case, since you couldn't have
|
||||
run `setperms` on a non-existant repo -- they are replaced by "NOBODY".
|
||||
|
||||
(The same thing is done with "R" access and the word "READERS".)
|
||||
|
||||
At this point we have an effective ruleset, and the normal access rules (R,
|
||||
RW, etc) apply, with the addition that the invoking user needs "C" access to
|
||||
be able to create a repo.
|
||||
|
||||
> (Note: "C" rights do not automatically give the CREATOR any other rights;
|
||||
> they must be specifically given. `RW+ = CREATOR` is recommended in most
|
||||
> situations, as you can see in our example).
|
||||
|
||||
Assuming user "u4" trying to push-create a new repo called
|
||||
`assignments/u4/a23`, this is what the effective ruleset looks like (we're
|
||||
ignoring the "NOBODY" business):
|
||||
|
||||
repo assignments/u4/a23
|
||||
C = @students
|
||||
RW+ = u4
|
||||
RW = @TAs
|
||||
R = @prof
|
||||
|
||||
If u4 gives "RW" perms to u5 using `setperms`, and u5 tries to access that
|
||||
repo, the ruleset looks like:
|
||||
|
||||
repo assignments/u4/a23
|
||||
C = @students
|
||||
RW+ = u4
|
||||
RW = u5 @TAs
|
||||
R = @prof
|
||||
|
||||
I hope that helps.
|
||||
|
||||
----
|
||||
|
||||
Enjoy, and please use with care. This is pretty powerful stuff. As they say:
|
||||
if you break it, you get to keep both pieces :)
|
||||
|
||||
[repout]: http://github.com/sitaramc/gitolite/blob/pu/doc/report-output.mkd
|
0
hooks/common/gitolite-hooked
Normal file
0
hooks/common/gitolite-hooked
Normal file
21
hooks/common/post-receive.mirrorpush
Executable file
21
hooks/common/post-receive.mirrorpush
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
|
||||
# gitolite mirroring
|
||||
|
||||
# please see doc/mirroring.mkd for instructions on how to use this
|
||||
|
||||
if [ -n "$GL_SLAVES" ]
|
||||
then
|
||||
for mirror in $GL_SLAVES
|
||||
do
|
||||
if git push --mirror $mirror:$GL_REPO.git
|
||||
then
|
||||
:
|
||||
else
|
||||
ssh $mirror mkdir -p $GL_REPO.git
|
||||
ssh $mirror git init --bare $GL_REPO.git
|
||||
git push --mirror $mirror:$GL_REPO.git ||
|
||||
echo "WARNING: mirror push to $mirror failed"
|
||||
fi
|
||||
done
|
||||
fi >&2
|
116
hooks/common/update
Executable file
116
hooks/common/update
Executable file
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# === update ===
|
||||
# this is gitolite's update hook
|
||||
|
||||
# part of the gitolite (GL) suite
|
||||
|
||||
# how run: via git, being copied as .git/hooks/update in every repo
|
||||
# when: every push
|
||||
# input:
|
||||
# - see man githooks for STDIN
|
||||
# - uses the compiled config file to get permissions info
|
||||
# output: based on permissions etc., exit 0 or 1
|
||||
# security:
|
||||
# - none
|
||||
|
||||
# robustness:
|
||||
|
||||
# other notes:
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# common definitions
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
our ($GL_CONF_COMPILED, $UPDATE_CHAINS_TO, $GL_PERFLOGT);
|
||||
our %repos;
|
||||
|
||||
# people with shell access should be allowed to bypass the update hook, simply
|
||||
# by setting an env var that the ssh "front door" will never set
|
||||
exit 0 if exists $ENV{GL_BYPASS_UPDATE_HOOK};
|
||||
|
||||
# we should already have the GL_RC env var set when we enter this hook
|
||||
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
|
||||
|
||||
require "$ENV{GL_BINDIR}/gitolite.pm";
|
||||
|
||||
my ($perm, $creator, $wild) = &repo_rights($ENV{GL_REPO});
|
||||
my $reported_repo = $ENV{GL_REPO} . ( $ENV{GL_REPOPATT} ? " ($ENV{GL_REPOPATT})" : "" );
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# start...
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
my @saved_ARGV = @ARGV; # for chaining to another update hook at the end
|
||||
|
||||
my $ref = shift;
|
||||
my $oldsha = shift;
|
||||
my $newsha = shift;
|
||||
my $merge_base = '0' x 40;
|
||||
# compute a merge-base if both SHAs are non-0, else leave it as '0'x40
|
||||
# (i.e., for branch create or delete, merge_base == '0'x40)
|
||||
chomp($merge_base = `git merge-base $oldsha $newsha`)
|
||||
unless $oldsha eq '0' x 40
|
||||
or $newsha eq '0' x 40;
|
||||
|
||||
# att_acc == attempted access -- what are you trying to do? (is it 'W' or '+'?)
|
||||
my $att_acc = 'W';
|
||||
# rewriting a tag is considered a rewind, in terms of permissions
|
||||
$att_acc = '+' if $ref =~ m(refs/tags/) and $oldsha ne ('0' x 40);
|
||||
# non-ff push to ref
|
||||
# notice that ref delete looks like a rewind, as it should
|
||||
$att_acc = '+' if $oldsha ne $merge_base;
|
||||
|
||||
# were any 'D' perms specified? If they were, it means we have to separate
|
||||
# deletes from rewinds, so if the new sha is all 0's, change the '+' to a 'D'
|
||||
$att_acc = 'D' if ( $repos{$ENV{GL_REPO}}{DELETE_IS_D} or $repos{'@all'}{DELETE_IS_D} ) and $newsha eq '0' x 40;
|
||||
# similarly C for create a branch
|
||||
$att_acc = 'C' if ( $repos{$ENV{GL_REPO}}{CREATE_IS_C} or $repos{'@all'}{CREATE_IS_C} ) and $oldsha eq '0' x 40;
|
||||
|
||||
my @allowed_refs;
|
||||
# @all repos: see comments in similar code in check_access
|
||||
push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{$ENV{GL_USER}} || [] };
|
||||
push @allowed_refs, @ { $repos{'@all'} {$ENV{GL_USER}} || [] };
|
||||
push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] };
|
||||
|
||||
# prepare the list of refs to be checked
|
||||
|
||||
# previously, we just checked $ref -- the ref being updated, which is passed
|
||||
# to us by git (see man githooks). Now we also have to treat each NAME being
|
||||
# updated as a potential "ref" and check that, if NAME-based restrictions have
|
||||
# been specified
|
||||
|
||||
my @refs = ($ref); # the first ref to check is the real one
|
||||
# because making it work screws up efficiency like no tomorrow...
|
||||
if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) {
|
||||
# this is special to git -- the hash of an empty tree
|
||||
my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
||||
# well they're not really "trees" but $empty is indeed the empty tree so
|
||||
# we can just pretend $oldsha/$newsha are also trees, and anyway 'git
|
||||
# diff' only wants trees
|
||||
my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha;
|
||||
my $newtree = $newsha eq '0' x 40 ? $empty : $newsha;
|
||||
push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`;
|
||||
}
|
||||
|
||||
# and in this version, we have many "refs" to check. The one we print in the
|
||||
# log is the *first* one (which is a *real* ref, like refs/heads/master),
|
||||
# while all the rest (if they exist) are like NAME/something. So we do the
|
||||
# first one separately to capture it, then run the rest (if any)
|
||||
my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $att_acc);
|
||||
&check_ref (\@allowed_refs, $ENV{GL_REPO}, $_ , $att_acc) for @refs;
|
||||
|
||||
# if we returned at all, all the checks succeeded, so we log the action and exit 0
|
||||
|
||||
&log_it("", "$att_acc\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) .
|
||||
"\t$reported_repo\t$ref\t$log_refex");
|
||||
|
||||
# now chain to the local admin defined update hook, if present
|
||||
$UPDATE_CHAINS_TO ||= 'hooks/update.secondary';
|
||||
exec $UPDATE_CHAINS_TO, @saved_ARGV
|
||||
if -f $UPDATE_CHAINS_TO or -l $UPDATE_CHAINS_TO;
|
||||
|
||||
exit 0;
|
28
hooks/gitolite-admin/post-update
Executable file
28
hooks/gitolite-admin/post-update
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
|
||||
# ensure that the admin is not sneaking in src/ and hooks/ :)
|
||||
GIT_WORK_TREE=$GL_ADMINDIR git ls-tree --name-only master |
|
||||
perl -lne 'exit 1 if /^(src|hooks)$/' || {
|
||||
echo "*** ERROR ***" >&2
|
||||
echo "no files/dirs called 'src' or 'hooks' are allowed, sorry" >&2
|
||||
echo "until those files are deleted, the post-update hook will not run" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# checkout the master branch to $GL_ADMINDIR
|
||||
# (the GL_ADMINDIR env var would have been set by gl-auth-command)
|
||||
GIT_WORK_TREE=$GL_ADMINDIR git checkout -f master
|
||||
|
||||
od=$PWD
|
||||
cd $GL_ADMINDIR
|
||||
$GL_BINDIR/gl-compile-conf
|
||||
|
||||
cd $od
|
||||
|
||||
ADMIN_POST_UPDATE_CHAINS_TO=` cd $HOME;perl -e 'do ".gitolite.rc"; print $ADMIN_POST_UPDATE_CHAINS_TO'`
|
||||
[ -n "$ADMIN_POST_UPDATE_CHAINS_TO" ] || ADMIN_POST_UPDATE_CHAINS_TO=hooks/post-update.secondary
|
||||
|
||||
if [ -f $ADMIN_POST_UPDATE_CHAINS_TO ] || [ -L $ADMIN_POST_UPDATE_CHAINS_TO ]
|
||||
then
|
||||
exec $ADMIN_POST_UPDATE_CHAINS_TO "$@"
|
||||
fi
|
77
install
77
install
|
@ -1,77 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# Clearly you don't need a program to make one measly symlink, but the git
|
||||
# describe command involved in generating the VERSION string is a bit fiddly.
|
||||
|
||||
use Getopt::Long;
|
||||
use FindBin;
|
||||
|
||||
# meant to be run from the root of the gitolite tree, one level above 'src'
|
||||
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin . "/src"; }
|
||||
BEGIN { $ENV{GL_LIBDIR} = "$ENV{GL_BINDIR}/lib"; }
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Common;
|
||||
|
||||
=for usage
|
||||
Usage (from gitolite clone directory):
|
||||
|
||||
./install
|
||||
to run gitolite using an absolute or relative path, for example
|
||||
'src/gitolite' or '/full/path/to/this/dir/src/gitolite'
|
||||
|
||||
./install -ln [<dir>]
|
||||
to symlink just the gitolite executable to some <dir> that is in
|
||||
$PATH. <dir> defaults to $HOME/bin if <dir> not specified. <dir> is
|
||||
assumed to exist; gitolite will not create it.
|
||||
|
||||
Please provide a full path, not a relative path.
|
||||
|
||||
./install -to <dir>
|
||||
to copy the entire 'src' directory to <dir>. If <dir> is not in
|
||||
$PATH, use the full path to run gitolite commands.
|
||||
|
||||
Please provide a full path, not a relative path.
|
||||
|
||||
Simplest use, if $HOME/bin exists and is in $PATH, is:
|
||||
|
||||
git clone git://github.com/sitaramc/gitolite
|
||||
gitolite/install -ln
|
||||
|
||||
# now run setup
|
||||
gitolite setup -pk /path/to/YourName.pub
|
||||
=cut
|
||||
|
||||
my ( $to, $ln, $help, $quiet );
|
||||
|
||||
GetOptions(
|
||||
'to=s' => \$to,
|
||||
'ln:s' => \$ln,
|
||||
'help|h' => \$help,
|
||||
'quiet|q' => \$quiet,
|
||||
);
|
||||
usage() if $to and $ln or $help;
|
||||
$ln = "$ENV{HOME}/bin" if defined($ln) and not $ln;
|
||||
for my $d ($ln, $to) {
|
||||
if ($d and not -d $d) {
|
||||
print STDERR "FATAL: '$d' does not exist.\n";
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
chdir($ENV{GL_BINDIR});
|
||||
my $version = `git describe --tags --long --dirty=-dt`;
|
||||
|
||||
if ($to) {
|
||||
_mkdir($to);
|
||||
system("cp -a * $to");
|
||||
_print( "$to/VERSION", $version );
|
||||
} elsif ($ln) {
|
||||
ln_sf( $ENV{GL_BINDIR}, "gitolite", $ln );
|
||||
_print( "VERSION", $version );
|
||||
} else {
|
||||
say "use the following full path for gitolite:";
|
||||
say "\t$ENV{GL_BINDIR}/gitolite";
|
||||
_print( "VERSION", $version );
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# gitolite VREF to count number of changed/new files in a push
|
||||
|
||||
# see gitolite docs for what the first 7 arguments mean
|
||||
|
||||
# inputs:
|
||||
# arg-8 is a number
|
||||
# arg-9 is optional, and can be "NEWFILES"
|
||||
# outputs (STDOUT)
|
||||
# arg-7 if the number of changed (or new, if arg-9 supplied) files is > arg-8
|
||||
# otherwise nothing
|
||||
# exit status:
|
||||
# always 0
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
[ -z "$8" ] && die "not meant to be run manually"
|
||||
|
||||
newsha=$3
|
||||
oldtree=$4
|
||||
newtree=$5
|
||||
refex=$7
|
||||
|
||||
max=$8
|
||||
|
||||
nf=
|
||||
[ "$9" = "NEWFILES" ] && nf='--diff-filter=A'
|
||||
# NO_SIGNOFF implies NEWFILES
|
||||
[ "$9" = "NO_SIGNOFF" ] && nf='--diff-filter=A'
|
||||
|
||||
# count files against all the other commits in the system not just $oldsha
|
||||
# (why? consider what is $oldtree when you create a new branch, or what is
|
||||
# $oldsha when you update an old feature branch from master and then push it
|
||||
count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | perl -ne '}{print "$."'`
|
||||
|
||||
[ $count -gt $max ] && {
|
||||
# count has been exceeded. If $9 was NO_SIGNOFF there's still a chance
|
||||
# for redemption -- if the top commit has a proper signed-off by line
|
||||
[ "$9" = "NO_SIGNOFF" ] && {
|
||||
author_email=$(git log --format=%ae -1 $newsha)
|
||||
git cat-file -p $newsha |
|
||||
egrep -i >/dev/null "^ *$count +new +files +signed-off by: *$author_email *$" && exit 0
|
||||
echo $refex top commit message should include the text \'$count new files signed-off by: $author_email\'
|
||||
exit 0
|
||||
}
|
||||
echo -n $refex "(too many "
|
||||
[ -n "$nf" ] && echo -n "new " || echo -n "changed "
|
||||
echo "files in this push)"
|
||||
}
|
||||
|
||||
exit 0
|
|
@ -1,66 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# gitolite VREF to check if all *new* commits have author == pusher
|
||||
|
||||
# THIS IS NOT READY TO USE AS IS
|
||||
# ------------------------------
|
||||
# you MUST change the 'email_ok()' sub to suit *YOUR* site's
|
||||
# gitolite username -> author email mapping!
|
||||
|
||||
# See bottom of the program for important philosophical notes.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# mapping between gitolite userid and correct email address is encapsulated in
|
||||
# this subroutine; change as you like
|
||||
sub email_ok {
|
||||
my ($author_email) = shift;
|
||||
my $expected_email = "$ENV{GL_USER}\@atc.tcs.com";
|
||||
return $author_email eq $expected_email;
|
||||
}
|
||||
|
||||
my ( $ref, $old, $new ) = @ARGV;
|
||||
for my $rev (`git log --format="%ae\t%h\t%s" $new --not --all`) {
|
||||
chomp($rev);
|
||||
my ( $author_email, $hash, $subject ) = split /\t/, $rev;
|
||||
|
||||
# again, we use the trick that a vref can just choose to die instead of
|
||||
# passing back a vref, having it checked, etc., if it's more convenient
|
||||
die "$ENV{GL_USER}, you can't push $hash authored by $author_email\n" . "\t(subject of commit was $subject)\n"
|
||||
unless email_ok($author_email);
|
||||
}
|
||||
|
||||
exit 0;
|
||||
|
||||
__END__
|
||||
|
||||
The following discussion is for people who want to enforce this check on ALL
|
||||
their developers (i.e., not just the newbies).
|
||||
|
||||
Doing this breaks the "D" in "DVCS", forcing all your developers to work to a
|
||||
centralised model as far as pushes are concerned. It prevents amending
|
||||
someone else's commit and pushing (this includes rebasing, cherry-picking, and
|
||||
so on, which are all impossible now). It also makes *any* off-line
|
||||
collabaration between two developers useless, because neither of them can push
|
||||
the result to the server.
|
||||
|
||||
PHBs should note that validating the committer ID is NOT the same as reviewing
|
||||
the code and running QA/tests on it. If you're not reviewing/QA-ing the code,
|
||||
it's probably worthless anyway. Conversely, if you *are* going to review the
|
||||
code and run QA/tests anyway, then you don't really need to validate the
|
||||
author email!
|
||||
|
||||
In a DVCS, if you *pushed* a series of commits, you have -- in some sense --
|
||||
signed off on them. The most formal way to "sign" a series is to tack on and
|
||||
push a gpg-signed tag, although most people don't go that far. Gitolite's log
|
||||
files are designed to preserve that accountability to *some* extent, though;
|
||||
see contrib/adc/who-pushed for an admin defined command that quickly and
|
||||
easily tells you who *pushed* a particular commit.
|
||||
|
||||
Anyway, the point is that the only purpose of this script is to
|
||||
|
||||
* pander to someone who still has not grokked *D*VCS
|
||||
OR
|
||||
* tick off an item in some stupid PHB's checklist
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# gitolite VREF to find autogenerated files
|
||||
|
||||
# *completely* site specific; use it as an illustration of what can be done
|
||||
# with gitolite VREFs if you wish
|
||||
|
||||
# see gitolite docs for what the first 7 arguments mean
|
||||
|
||||
# inputs:
|
||||
# arg-8 is currently only one possible value: AUTOGENERATED
|
||||
# outputs (STDOUT)
|
||||
# arg-7 if any files changed in the push look like they were autogenerated
|
||||
# otherwise nothing
|
||||
# exit status:
|
||||
# always 0
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
[ -z "$8" ] && die "not meant to be run manually"
|
||||
|
||||
newsha=$3
|
||||
oldtree=$4
|
||||
newtree=$5
|
||||
refex=$7
|
||||
|
||||
option=$8
|
||||
|
||||
[ "$option" = "AUTOGENERATED" ] && {
|
||||
# currently we only look for ".java" programs with the string "Generated
|
||||
# by the protocol buffer compiler. DO NOT EDIT" in them.
|
||||
|
||||
git log --name-only $nf --format=%n $newtree --not --all |
|
||||
grep . |
|
||||
sort -u |
|
||||
grep '\.java$' |
|
||||
while read fn
|
||||
do
|
||||
git show "$newtree:$fn" | egrep >/dev/null \
|
||||
'Generated by the protocol buffer compiler. +DO NOT EDIT' ||
|
||||
continue
|
||||
|
||||
echo $refex
|
||||
exit 0
|
||||
done
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# gitolite VREF to check max size of new binary files
|
||||
|
||||
# see gitolite docs for what the first 7 arguments mean
|
||||
|
||||
# inputs:
|
||||
# arg-8 is a number
|
||||
# outputs (STDOUT)
|
||||
# arg-7 if any new binary files exist that are greater in size than arg-8
|
||||
# *and* there is no "signed-off by" line for such a file in the top commit
|
||||
# message.
|
||||
#
|
||||
# Otherwise nothing
|
||||
# exit status:
|
||||
# always 0
|
||||
|
||||
die "not meant to be run manually" unless $ARGV[7];
|
||||
|
||||
my ( $newsha, $oldtree, $newtree, $refex, $max ) = @ARGV[ 2, 3, 4, 6, 7 ];
|
||||
|
||||
# / (.*) +\| Bin 0 -> (\d+) bytes/
|
||||
|
||||
chomp( my $author_email = `git log --format=%ae -1 $newsha` );
|
||||
my $msg = `git cat-file -p $newsha`;
|
||||
$msg =~ s/\t/ /g; # makes our regexes simpler
|
||||
|
||||
for my $newbin (`git diff --stat=999,999 $oldtree $newtree | grep Bin.0.-`) {
|
||||
next unless $newbin =~ /^ (.*) +\| +Bin 0 -> (\d+) bytes/;
|
||||
my ( $f, $s ) = ( $1, $2 );
|
||||
next if $s <= $max;
|
||||
|
||||
next if $msg =~ /^ *$f +signed-off by: *$author_email *$/mi;
|
||||
|
||||
print "$refex $f is larger than $max";
|
||||
}
|
||||
|
||||
exit 0
|
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# gitolite VREF to check if there are any merge commits in the current push.
|
||||
|
||||
# THIS IS DEMO CODE; please read all comments below as well as
|
||||
# doc/vref.mkd before trying to use this.
|
||||
|
||||
# usage in conf/gitolite.conf goes like this:
|
||||
|
||||
# - VREF/MERGE_CHECK/master = @all
|
||||
# # reject only if the merge commit is being pushed to the master branch
|
||||
# - VREF/MERGE_CHECK = @all
|
||||
# # reject merge commits to any branch
|
||||
|
||||
my $ref = $ARGV[0];
|
||||
my $oldsha = $ARGV[1];
|
||||
my $newsha = $ARGV[2];
|
||||
my $refex = $ARGV[6];
|
||||
|
||||
# The following code duplicates some code from parse_conf_line() and some from
|
||||
# check_ref(). This duplication is the only thing that is preventing me from
|
||||
# removing the "M" permission code from 'core' gitolite and using this
|
||||
# instead. However, it does demonstrate how you would do this if you had to
|
||||
# create any other similar features, for example someone wanted "no non-merge
|
||||
# first-parent", which is far too specific for me to add to 'core'.
|
||||
|
||||
# -- begin duplication --
|
||||
my $branch_refex = $ARGV[7] || '';
|
||||
if ($branch_refex) {
|
||||
$branch_refex =~ m(^refs/) or $branch_refex =~ s(^)(refs/heads/);
|
||||
} else {
|
||||
$branch_refex = 'refs/.*';
|
||||
}
|
||||
exit 0 unless $ref =~ /^$branch_refex/;
|
||||
# -- end duplication --
|
||||
|
||||
# we can't run this check for tag creation or new branch creation, because
|
||||
# 'git log' does not deal well with $oldsha = '0' x 40.
|
||||
if ( $oldsha eq "0" x 40 or $newsha eq "0" x 40 ) {
|
||||
print STDERR "ref create/delete ignored for purposes of merge-check\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $ret = `git rev-list -n 1 --merges $oldsha..$newsha`;
|
||||
print "$refex FATAL: merge commits not allowed\n" if $ret =~ /./;
|
||||
|
||||
exit 0;
|
|
@ -1,80 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# gitolite VREF to count votes before allowing pushes to certain branches.
|
||||
|
||||
# This approximates gerrit's voting (but it is SHA based; I believe Gerrit is
|
||||
# more "changeset" based). Here's how it works:
|
||||
|
||||
# - A normal developer "bob" proposes changes to master by pushing a commit to
|
||||
# "pers/bob/master", then informs the voting members by email.
|
||||
|
||||
# - Some or all of the voting members fetch and examine the commit. If they
|
||||
# approve, they "vote" for the commit like so. For example, say voting
|
||||
# member "alice" fetched bob's proposed commit into "bob-master" on her
|
||||
# clone, then tested or reviewed it. She would approve it by running:
|
||||
# git push origin bob-master:votes/alice/master
|
||||
|
||||
# - Once enough votes have been tallied (hopefully there is normal team
|
||||
# communication that says "hey I approved your commit", or it can be checked
|
||||
# by 'git ls-remote origin' anyway), Bob, or any developer, can push the
|
||||
# same commit (same SHA) to master and the push will succeed.
|
||||
|
||||
# - Finally, a "trusted" developer can push a commit to master without
|
||||
# worrying about the voting restriction at all.
|
||||
|
||||
# The config for this example would look like this:
|
||||
|
||||
# repo foo
|
||||
# # allow personal branches (to submit proposed changes)
|
||||
# RW+ pers/USER/ = @devs
|
||||
# - pers/ = @all
|
||||
#
|
||||
# # allow only voters to vote
|
||||
# RW+ votes/USER/ = @voters
|
||||
# - votes/ = @all
|
||||
#
|
||||
# # normal access rules go here; should allow *someone* to push master
|
||||
# RW+ = @devs
|
||||
#
|
||||
# # 2 votes required to push master, but trusted devs don't have this restriction
|
||||
# RW+ VREF/VOTES/2/master = @trusted-devs
|
||||
# - VREF/VOTES/2/master = @devs
|
||||
|
||||
# Note: "2 votes required to push master" means at least 2 refs matching
|
||||
# "votes/*/master" have the same SHA as the one currently being pushed.
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# see gitolite docs for what the first 7 arguments mean
|
||||
|
||||
# inputs:
|
||||
# arg-8 is a number; see below
|
||||
# arg-9 is a simple branch name (i.e., "master", etc). Currently this code
|
||||
# does NOT do vote counting for branch names with more than one component
|
||||
# (like foo/bar).
|
||||
# outputs (STDOUT)
|
||||
# nothing
|
||||
# exit status:
|
||||
# always 0
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
[ -z "$8" ] && die "not meant to be run manually"
|
||||
|
||||
ref=$1
|
||||
newsha=$3
|
||||
refex=$7
|
||||
votes_needed=$8
|
||||
branch=$9
|
||||
|
||||
# nothing to do if the branch being pushed is not "master" (using our example)
|
||||
[ "$ref" = "refs/heads/$branch" ] || exit 0
|
||||
|
||||
# find how many votes have come in
|
||||
votes=`git for-each-ref refs/heads/votes/*/$branch | grep -c $newsha`
|
||||
|
||||
# send back a vref if we don't have the minimum votes needed. For trusted
|
||||
# developers this will invoke the RW+ rule and pass anyway, but for others it
|
||||
# will invoke the "-" rule and fail.
|
||||
[ $votes -ge $votes_needed ] || echo $refex "require at least $votes_needed votes to push $branch"
|
||||
|
||||
exit 0
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Common;
|
||||
|
||||
# gitolite VREF to lock and unlock (binary) files. Requires companion command
|
||||
# 'lock' to be enabled; see doc/locking.mkd for details.
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# see gitolite docs for what the first 7 arguments mean
|
||||
|
||||
die "not meant to be run manually" unless $ARGV[6];
|
||||
|
||||
my $ff = "$ENV{GL_REPO_BASE}/$ENV{GL_REPO}.git/gl-locks";
|
||||
exit 0 unless -f $ff;
|
||||
|
||||
our %locks;
|
||||
my $t = slurp($ff);
|
||||
eval $t;
|
||||
_die "do '$ff' failed with '$@', contact your administrator" if $@;
|
||||
|
||||
my ( $oldtree, $newtree, $refex ) = @ARGV[ 3, 4, 6 ];
|
||||
|
||||
for my $file (`git diff --name-only $oldtree $newtree` ) {
|
||||
chomp($file);
|
||||
|
||||
if ($locks{$file} and $locks{$file}{USER} ne $ENV{GL_USER}) {
|
||||
print "$refex '$file' locked by '$locks{$file}{USER}'";
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
exit 0
|
|
@ -1,41 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# push updated branches back to the "main" repo.
|
||||
|
||||
# This must be run as the *last* VREF, though it doesn't matter what
|
||||
# permission you give to it
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
|
||||
repo=$GL_REPO
|
||||
user=$GL_USER
|
||||
ref=$1 # we're running like an update hook
|
||||
old=$2
|
||||
new=$3
|
||||
|
||||
# never send any STDOUT back, to avoid looking like a ref. If we fail, git
|
||||
# will catch it by our exit code
|
||||
exec >&2
|
||||
|
||||
main=`git config --file $GL_REPO_BASE/$repo.git/config --get gitolite.partialCopyOf`;
|
||||
[ -z "$main" ] && exit 0
|
||||
|
||||
rand=$$
|
||||
export GL_BYPASS_ACCESS_CHECKS=1
|
||||
|
||||
if [ "$new" = "0000000000000000000000000000000000000000" ]
|
||||
then
|
||||
# special case for deleting a ref (this is why it is important to put this
|
||||
# VREF as the last one; if we got this far he is allowed to delete it)
|
||||
git push -f $GL_REPO_BASE/$main.git :$ref || die "FATAL: failed to delete $ref"
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git push -f $GL_REPO_BASE/$main.git $new:refs/partial/br-$rand || die "FATAL: failed to send $new"
|
||||
|
||||
cd $GL_REPO_BASE/$main.git
|
||||
git update-ref -d refs/partial/br-$rand
|
||||
git update-ref $ref $new $old || die "FATAL: update-ref for $ref failed"
|
||||
|
||||
exit 0
|
|
@ -1,75 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $rule = $ARGV[7];
|
||||
die "\n\nFATAL: GL_REFEX_EXPR_ doesn't exist\n(your admin probably forgot the rc file change needed for this to work)\n\n"
|
||||
unless exists $ENV{"GL_REFEX_EXPR_" . $rule};
|
||||
my $res = $ENV{"GL_REFEX_EXPR_" . $rule} || 0;
|
||||
print "$ARGV[6] ($res)\n" if $res;
|
||||
|
||||
exit 0;
|
||||
|
||||
__END__
|
||||
|
||||
Documentation for the refex-expression evaluation feature
|
||||
|
||||
First, make sure you have both the VREF and the trigger scripts
|
||||
(src/VREF/refex-expr and src/lib/Gitolite/Triggers/RefexExpr.pm)
|
||||
|
||||
Next, add this to the ACCESS_2 list in the rc file:
|
||||
|
||||
'RefexExpr::access_2',
|
||||
|
||||
For the rest, we'll use this example:
|
||||
|
||||
* user u1 can push foo to some other branch, and anything else to the master
|
||||
branch, but not foo to the master branch
|
||||
|
||||
* user u2 is allowed to push either 'doc/' or 'src/' but not both
|
||||
|
||||
Here's the conf file extract:
|
||||
|
||||
repo testing
|
||||
RW+ master = u1 # line 1
|
||||
RW+ = @all # line 2
|
||||
|
||||
RW+ VREF/NAME/foo = u1
|
||||
RW+ VREF/NAME/doc/ = u2
|
||||
RW+ VREF/NAME/src/ = u2
|
||||
|
||||
# set up 2 refex expressions, named e1, e2
|
||||
option refex-expr.e1 = master and VREF/NAME/foo
|
||||
option refex-expr.e2 = VREF/NAME/doc/ and VREF/NAME/src/
|
||||
|
||||
# now deny users if the corresponding expression is true
|
||||
- VREF/refex-expr/e1 = u1
|
||||
- VREF/refex-expr/e2 = u2
|
||||
|
||||
Here are some IMPORTANT notes:
|
||||
|
||||
* You MUST place VREF/refex-expr rules at the end. (Only 'partial-copy', if
|
||||
you use it, must come later).
|
||||
|
||||
* You MUST explicitly permit the refexes used in your refex expressions. If
|
||||
you have more generic rules, the specific ones must come first.
|
||||
|
||||
For example, without line 1, the refex recorded for user u1 will come from
|
||||
line 2, (so it will be 'refs/.*'), and 'master' in the refex expressions
|
||||
will never have a true value.
|
||||
|
||||
* (corollary) make sure you use the exact same refex in the expression as
|
||||
you did on the original rule line. E.g., a missing slash at the end will
|
||||
mess things up.
|
||||
|
||||
* You can use any logical expression using refexes as operands and using
|
||||
these operators:
|
||||
|
||||
and not xor or
|
||||
|
||||
Parens are not allowed.
|
||||
|
||||
If a refex has passed, it will have a 'true' value, else it will be false.
|
||||
|
||||
The result of the evaluation, after these substitutions, will be the
|
||||
result of the refex-expr VREF.
|
131
src/commands/D
131
src/commands/D
|
@ -1,131 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# ADMINISTRATOR NOTES:
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# - set TRASH_CAN in the rc if you don't like the default. It should be
|
||||
# relative to GL_REPO_BASE or an absolute value. It should also be on the
|
||||
# same filesystem as GL_REPO_BASE, otherwise the 'mv' will take too long.
|
||||
|
||||
# - you could set TRASH_SUFFIX also but I recomend you leave it as it is
|
||||
|
||||
# - run a cron job to delete old repos based on age (the TRASH_SUFFIX has a
|
||||
# timestamp); your choice how/how often you do that
|
||||
|
||||
# - you can completely disable the 'rm' command by setting an rc variable
|
||||
# called D_DISABLE_RM to "1".
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Usage: ssh git@host D <subcommand> <argument>
|
||||
#
|
||||
# The whimsically named "D" command deletes repos ("D" is a counterpart to the
|
||||
# "C" permission which lets you create repos. Which also means that, just
|
||||
# like "C", it only works for wild repos).
|
||||
#
|
||||
# There are two kinds of deletions: 'rm' removes a repo completely, while
|
||||
# 'trash' moves it to a trashcan which can be recovered later (upto a time
|
||||
# limit that your admin will tell you).
|
||||
#
|
||||
# The 'rm', 'lock', and 'unlock' subcommands:
|
||||
# Initially, all repos are "locked" against 'rm'. The correct sequence is
|
||||
# ssh git@host D unlock repo
|
||||
# ssh git@host D rm repo
|
||||
# Since the initial condition is always locked, the "lock" command is
|
||||
# rarely used but it is there if you want it.
|
||||
#
|
||||
# The 'trash', 'list-trash', and 'restore' subcommands:
|
||||
# You can 'trash' a repo, which moves it to a special place:
|
||||
# ssh git@host D trash repo
|
||||
# You can then 'list-trash'
|
||||
# ssh git@host D list-trash
|
||||
# which prints something like
|
||||
# repo/2012-04-11_05:58:51
|
||||
# allowing you to restore by saying
|
||||
# ssh git@host D restore repo/2012-04-11_05:58:51
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
|
||||
[ -z "$1" ] && usage
|
||||
[ "$1" = "-h" ] && usage
|
||||
[ "$1" != "list-trash" ] && [ -z "$2" ] && usage
|
||||
[ -z "$GL_USER" ] && die GL_USER not set
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
cmd=$1
|
||||
repo=$2
|
||||
# ----------------------------------------------------------------------
|
||||
RB=`gitolite query-rc GL_REPO_BASE`; cd $RB
|
||||
TRASH_CAN=`gitolite query-rc TRASH_CAN`; tcan=Trash; TRASH_CAN=${TRASH_CAN:-$tcan}
|
||||
TRASH_SUFFIX=`gitolite query-rc TRASH_SUFFIX`; tsuf=`date +%Y-%m-%d_%H:%M:%S`; TRASH_SUFFIX=${TRASH_SUFFIX:-$tsuf}
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
owner_or_die() {
|
||||
gitolite creator "$repo" $GL_USER || die You are not authorised
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
if [ "$cmd" = "rm" ]
|
||||
then
|
||||
|
||||
gitolite query-rc -q D_DISABLE_RM && die "sorry, 'unlock' and 'rm' are disabled"
|
||||
|
||||
owner_or_die
|
||||
[ -f $repo.git/gl-rm-ok ] || die "'$repo' is locked!"
|
||||
rm -rf $repo.git
|
||||
echo "'$repo' is now gone!"
|
||||
|
||||
elif [ "$cmd" = "lock" ]
|
||||
then
|
||||
|
||||
owner_or_die
|
||||
rm -f $repo.git/gl-rm-ok
|
||||
echo "'$repo' is now locked"
|
||||
|
||||
elif [ "$cmd" = "unlock" ]
|
||||
then
|
||||
|
||||
gitolite query-rc -q D_DISABLE_RM && die "sorry, 'unlock' and 'rm' are disabled"
|
||||
|
||||
owner_or_die
|
||||
touch $repo.git/gl-rm-ok
|
||||
echo "'$repo' is now unlocked"
|
||||
|
||||
elif [ "$cmd" = "trash" ]
|
||||
then
|
||||
|
||||
owner_or_die
|
||||
mkdir -p $TRASH_CAN/$repo 2>/dev/null || die "failed creating directory in trashcan"
|
||||
[ -d $TRASH_CAN/$repo/$TRASH_SUFFIX ] && die "try again in a few seconds..."
|
||||
mv $repo.git $TRASH_CAN/$repo/$TRASH_SUFFIX
|
||||
echo "'$repo' moved to trashcan"
|
||||
|
||||
elif [ "$cmd" = "list-trash" ]
|
||||
then
|
||||
|
||||
cd $TRASH_CAN 2>/dev/null || exit 0
|
||||
find . -name gl-creator | sort | while read t
|
||||
do
|
||||
owner=
|
||||
owner=`cat "$t"`
|
||||
[ "$owner" = "$GL_USER" ] && dirname $t
|
||||
done | cut -c3-
|
||||
|
||||
elif [ "$cmd" = "restore" ]
|
||||
then
|
||||
|
||||
owner=
|
||||
owner=`cat $TRASH_CAN/$repo/gl-creator 2>/dev/null`
|
||||
[ "$owner" = "$GL_USER" ] || die "'$repo' is not yours!"
|
||||
|
||||
cd $TRASH_CAN
|
||||
realrepo=`dirname $repo`
|
||||
[ -d $RB/$realrepo.git ] && die "'$realrepo' already exists"
|
||||
mv $repo $RB/$realrepo.git
|
||||
echo "'$repo' restored to '$realrepo'"
|
||||
|
||||
else
|
||||
die "unknown subcommand '$cmd'"
|
||||
fi
|
|
@ -1,74 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage: gitolite access [-q] <repo> <user> <perm> <ref>
|
||||
|
||||
Print access rights for arguments given. The string printed has the word
|
||||
DENIED in it if access was denied. With '-q', returns only an exit code
|
||||
(shell truth, not perl truth -- 0 is success).
|
||||
|
||||
- repo: mandatory
|
||||
- user: mandatory
|
||||
- perm: defauts to '+'. Valid values: R, W, +, C, D, M
|
||||
- ref: defauts to 'any'. See notes below
|
||||
|
||||
Notes:
|
||||
- ref: Any fully qualified ref ('refs/heads/master', not 'master') is fine.
|
||||
The 'any' ref is special -- it ignores deny rules (see docs for what this
|
||||
means and exceptions).
|
||||
|
||||
For each case where access is not denied, one line is printed like this:
|
||||
|
||||
reponame<tab>username<tab>access rights
|
||||
|
||||
This is orders of magnitude faster than running the command multiple times;
|
||||
you'll notice if you have more than a hundred or so repos.
|
||||
|
||||
Advanced uses: see src/triggers/post-compile/update-git-daemon-access-list for
|
||||
a good example.
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
my $quiet = 0;
|
||||
if ( $ARGV[0] eq '-q' ) { $quiet = 1; shift @ARGV; }
|
||||
|
||||
my ( $repo, $user, $aa, $ref ) = @ARGV;
|
||||
$aa ||= '+';
|
||||
$ref ||= 'any';
|
||||
_die "invalid perm" if not( $aa and $aa =~ /^(R|W|\+|C|D|M|\^C)$/ );
|
||||
_die "invalid ref name" if not( $ref and $ref =~ $REPONAME_PATT );
|
||||
|
||||
my $ret = '';
|
||||
|
||||
if ( $repo ne '%' and $user ne '%' ) {
|
||||
# single repo, single user; no STDIN
|
||||
$ret = access( $repo, $user, $aa, $ref );
|
||||
|
||||
if ( $ret =~ /DENIED/ ) {
|
||||
print "$ret\n" unless $quiet;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print "$ret\n" unless $quiet;
|
||||
exit 0;
|
||||
}
|
||||
|
||||
$repo = '' if $repo eq '%';
|
||||
$user = '' if $user eq '%';
|
||||
|
||||
_die "'-q' doesn't go with using a pipe" if $quiet;
|
||||
@ARGV = ();
|
||||
while (<>) {
|
||||
my @in = split;
|
||||
my $r = $repo || shift @in;
|
||||
my $u = $user || shift @in;
|
||||
$ret = access( $r, $u, $aa, $ref );
|
||||
print "$r\t$u\t$ret\n";
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Usage: ssh git@host create <repo>
|
||||
#
|
||||
# Create wild repo.
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
|
||||
[ -z "$1" ] && usage
|
||||
[ -z "$2" ] || usage
|
||||
[ "$1" = "-h" ] && usage
|
||||
[ -z "$GL_USER" ] && die GL_USER not set
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
exec $GL_BINDIR/commands/perms -c "$@" < /dev/null
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage: gitolite creator [-n] <reponame> [<username>]
|
||||
|
||||
Print the creator name for the repo. A '-n' suppresses the newline.
|
||||
|
||||
When an optional username is supplied, it checks if the user is the creator of
|
||||
the repo and returns an exit code (shell truth, 0 for success) instead of
|
||||
printing anything, which makes it possible to do this in shell:
|
||||
|
||||
if gitolite creator someRepo someUser
|
||||
then
|
||||
...
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
my $nl = "\n";
|
||||
if ( $ARGV[0] eq '-n' ) {
|
||||
$nl = '';
|
||||
shift;
|
||||
}
|
||||
my $repo = shift;
|
||||
my $user = shift || '';
|
||||
|
||||
my $creator = '';
|
||||
$creator = creator($repo) if not repo_missing($repo);
|
||||
if ($user) {
|
||||
exit 0 if $creator eq $user;
|
||||
exit 1;
|
||||
}
|
||||
return ( $creator eq $user ) if $user;
|
||||
print "$creator$nl";
|
|
@ -1,43 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Usage: ssh git@host desc <repo>
|
||||
# ssh git@host desc <repo> <description string>
|
||||
#
|
||||
# Show or set description for user-created ("wild") repo.
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
|
||||
[ -z "$1" ] && usage
|
||||
[ "$1" = "-h" ] && usage
|
||||
[ -z "$GL_USER" ] && die GL_USER not set
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
repo=$1; shift
|
||||
|
||||
# this shell script takes arguments that are completely under the user's
|
||||
# control, so make sure you quote those suckers!
|
||||
|
||||
# kernel.org needs 'desc' to be available to people who have "RW" or above,
|
||||
# not just the "creator". In fact they need it for non-wild repos so there
|
||||
# *is* no creator.
|
||||
if gitolite query-rc -q WRITER_CAN_UPDATE_DESC
|
||||
then
|
||||
gitolite access -q "$repo" $GL_USER W any || die You are not authorised
|
||||
else
|
||||
gitolite creator "$repo" $GL_USER || die You are not authorised
|
||||
fi
|
||||
|
||||
# if it passes, $repo is a valid repo name so it is known to contain only sane
|
||||
# characters. This is because 'gitolite creator' return true only if there
|
||||
# *is* a repo of that name and it has a gl-creator file that contains the same
|
||||
# text as $GL_USER.
|
||||
|
||||
descfile=`gitolite query-rc GL_REPO_BASE`/"$repo".git/description
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
[ -r "$descfile" ] && cat "$descfile"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "$*" > "$descfile"
|
|
@ -1,59 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Usage: ssh git@host fork <repo1> <repo2>
|
||||
#
|
||||
# Forks repo1 to repo2. You must have read permissions on repo1, and create
|
||||
# ("C") permissions for repo2, which of course must not exist.
|
||||
#
|
||||
# A fork is functionally the same as cloning repo1 to a client and pushing it
|
||||
# to a new repo2. It's just a little more efficient, not just in network
|
||||
# traffic but because it uses git clone's "-l" option to share the object
|
||||
# store also, so it is likely to be almost instantaneous, regardless of how
|
||||
# big the repo actually is.
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
|
||||
[ -z "$1" ] && usage
|
||||
[ "$1" = "-h" ] && usage
|
||||
[ -z "$GL_USER" ] && die GL_USER not set
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
from=$1; shift
|
||||
to=$1; shift
|
||||
[ -z "$to" ] && usage
|
||||
|
||||
gitolite access -q "$from" $GL_USER R any || die "'$from' does not exist or you are not allowed to read it"
|
||||
gitolite access -q "$to" $GL_USER ^C any || die "'$to' already exists or you are not allowed to create it"
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# IMPORTANT NOTE: checking whether someone can create a repo is done as above.
|
||||
# However, make sure that the env var GL_USER is set, and that too to the same
|
||||
# value as arg-2 of the access command), otherwise it won't work.
|
||||
|
||||
# Ideally, you'll leave such code to me. There's a reason ^C is not listed in
|
||||
# the help message for 'gitolite access'.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# clone $from to $to
|
||||
git clone --bare -l $GL_REPO_BASE/$from.git $GL_REPO_BASE/$to.git
|
||||
[ $? -ne 0 ] && exit 1
|
||||
|
||||
echo "$from forked to $to" >&2
|
||||
|
||||
# fix up creator, default role permissions (gl-perms), and hooks
|
||||
cd $GL_REPO_BASE/$to.git
|
||||
echo $GL_USER > gl-creator
|
||||
|
||||
touch gl-perms
|
||||
if gitolite query-rc -q DEFAULT_ROLE_PERMS
|
||||
then
|
||||
gitolite query-rc DEFAULT_ROLE_PERMS > gl-perms
|
||||
fi
|
||||
|
||||
ln -sf `gitolite query-rc GL_ADMIN_BASE`/hooks/common/* hooks
|
||||
|
||||
# record where you came from
|
||||
echo "$from" > gl-forked-from
|
||||
|
||||
# trigger post_create
|
||||
gitolite trigger POST_CREATE $to $GL_USER fork
|
|
@ -1,86 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Getopt::Long;
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage: gitolite git-config [-n] [-q] [-r] <repo> <key|pattern>
|
||||
|
||||
Print git config keys and values for the given repo. The key is either a full
|
||||
key, or, if '-r' is supplied, a regex that is applied to all available keys.
|
||||
|
||||
-q exit code only (shell truth; 0 is success)
|
||||
-n suppress trailing newline when used as key (not pattern)
|
||||
-r treat key as regex pattern (unanchored)
|
||||
|
||||
Examples:
|
||||
gitolite git-config repo gitweb.owner
|
||||
gitolite git-config -q repo gitweb.owner
|
||||
gitolite git-config -r repo gitweb
|
||||
|
||||
When the key is treated as a pattern, prints:
|
||||
|
||||
reponame<tab>key<tab>value<newline>
|
||||
|
||||
Otherwise the output is just the value.
|
||||
|
||||
Finally, see the advanced use section of 'gitolite access -h' -- you can do
|
||||
something similar here also:
|
||||
|
||||
gitolite list-phy-repos | gitolite git-config -r % gitweb\\. | cut -f1 > ~/projects.list
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV;
|
||||
|
||||
my ( $help, $nonl, $quiet, $regex ) = (0) x 4;
|
||||
GetOptions(
|
||||
'n' => \$nonl,
|
||||
'q' => \$quiet,
|
||||
'r' => \$regex,
|
||||
'h' => \$help,
|
||||
) or usage();
|
||||
|
||||
my ( $repo, $key ) = @ARGV;
|
||||
usage() unless $key;
|
||||
|
||||
my $ret = '';
|
||||
|
||||
if ( $repo ne '%' and $key ne '%' ) {
|
||||
# single repo, single key; no STDIN
|
||||
$key = "^\Q$key\E\$" unless $regex;
|
||||
|
||||
$ret = git_config( $repo, $key );
|
||||
|
||||
# if the key is not a regex, it should match at most one item
|
||||
_die "found more than one entry for '$key'" if not $regex and scalar( keys %$ret ) > 1;
|
||||
|
||||
# unlike access, there's nothing to print if we don't find any matching keys
|
||||
exit 1 unless %$ret;
|
||||
|
||||
if ($regex) {
|
||||
map { print "$repo\t$_\t" . $ret->{$_} . "\n" } sort keys %$ret unless $quiet;
|
||||
} else {
|
||||
map { print $ret->{$_} . ( $nonl ? "" : "\n" ) } sort keys %$ret unless $quiet;
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
$repo = '' if $repo eq '%';
|
||||
$key = '' if $key eq '%';
|
||||
|
||||
_die "'-q' doesn't go with using a pipe" if $quiet;
|
||||
@ARGV = ();
|
||||
while (<>) {
|
||||
my @in = split;
|
||||
my $r = $repo || shift @in;
|
||||
my $k = $key || shift @in;
|
||||
$k = "^\Q$k\E\$" unless $regex;
|
||||
$ret = git_config( $r, $k );
|
||||
next unless %$ret;
|
||||
map { print "$r\t$_\t" . $ret->{$_} . "\n" } sort keys %$ret;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
=for usage
|
||||
Usage: ssh git@host help # via ssh
|
||||
gitolite help # directly on server command line
|
||||
|
||||
Prints a list of custom commands available at this gitolite installation.
|
||||
|
||||
Each command has its own help, accessed by passing it '-h' again.
|
||||
=cut
|
||||
|
||||
usage() if @ARGV;
|
||||
|
||||
my $user = $ENV{GL_USER} || '';
|
||||
print "hello" . ( $user ? " $user" : "" ) . ", this is gitolite3 " . version() . " on git " . substr( `git --version`, 12 ) . "\n";
|
||||
|
||||
print "list of " . ( $user ? "remote" : "gitolite" ) . " commands available:\n\n";
|
||||
|
||||
my %list = (list_x( $ENV{GL_BINDIR}), list_x($rc{LOCAL_CODE} || ''));
|
||||
for (sort keys %list) {
|
||||
print "\t$list{$_}" if $ENV{D};
|
||||
print "\t$_\n" if not $user or $rc{COMMANDS}{$_};
|
||||
}
|
||||
|
||||
print "\n";
|
||||
|
||||
exit 0;
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
sub list_x {
|
||||
my $d = shift;
|
||||
return unless $d;
|
||||
return unless -d "$d/commands";
|
||||
_chdir "$d/commands";
|
||||
return map { $_ => $d } grep { -x $_ } map { chomp; s(^./)(); $_ } `find . -type f -o -type l|sort`;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
=for usage
|
||||
Usage: ssh git@host htpasswd
|
||||
|
||||
Sets your htpasswd, assuming your admin has enabled it.
|
||||
|
||||
(Admins: You need to add HTPASSWD_FILE to the rc file, pointing to an
|
||||
existing, writable, but possibly an initially empty, file, as well as adding
|
||||
an entry for 'htpasswd' to the COMMANDS hash).
|
||||
=cut
|
||||
|
||||
# usage and sanity checks
|
||||
usage() if @ARGV and $ARGV[0] eq '-h';
|
||||
$ENV{GL_USER} or _die "GL_USER not set";
|
||||
my $htpasswd_file = $rc{HTPASSWD_FILE} || '';
|
||||
die "htpasswd not enabled\n" unless $htpasswd_file;
|
||||
die "$htpasswd_file doesn't exist or is not writable\n" unless -w $htpasswd_file;
|
||||
|
||||
# prompt
|
||||
$|++;
|
||||
print <<EOFhtp;
|
||||
Please type in your new htpasswd at the prompt. You only have to type it once.
|
||||
|
||||
NOTE THAT THE PASSWORD WILL BE ECHOED, so please make sure no one is
|
||||
shoulder-surfing, and make sure you clear your screen as well as scrollback
|
||||
history after you're done (or close your terminal instance).
|
||||
|
||||
EOFhtp
|
||||
print "new htpasswd: ";
|
||||
|
||||
# get the password and run htpasswd
|
||||
my $password = <>;
|
||||
$password =~ s/[\n\r]*$//;
|
||||
die "empty passwords are not allowed\n" unless $password;
|
||||
my $res = system("htpasswd", "-mb", $htpasswd_file, $ENV{GL_USER}, $password);
|
||||
die "htpasswd command seems to have failed with return code: $res.\n" if $res;
|
|
@ -1,107 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Getopt::Long;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for args
|
||||
Usage: gitolite info [-lc] [-ld] [<repo name pattern>]
|
||||
|
||||
List all existing repos you can access, as well as repo name patterns you can
|
||||
create repos from (if any).
|
||||
|
||||
'-lc' lists creators as an additional field at the end.
|
||||
'-ld' lists description as an additional field at the end.
|
||||
|
||||
The optional pattern is an unanchored regex that will limit the repos
|
||||
searched, in both cases. It might speed up things a little if you have more
|
||||
than a few thousand repos.
|
||||
=cut
|
||||
|
||||
# these two are globals
|
||||
my ( $lc, $ld, $patt ) = args();
|
||||
|
||||
print_version();
|
||||
|
||||
print_patterns(); # repos he can create for himself
|
||||
print_phy_repos(); # repos already created
|
||||
print "\n$rc{SITE_INFO}\n" if $rc{SITE_INFO};
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub args {
|
||||
my ( $lc, $ld, $patt ) = ( '', '', '' );
|
||||
my $help = '';
|
||||
|
||||
GetOptions(
|
||||
'lc' => \$lc,
|
||||
'ld' => \$ld,
|
||||
'h' => \$help,
|
||||
) or usage();
|
||||
|
||||
usage() if @ARGV > 1 or $help;
|
||||
$patt = shift @ARGV || '.';
|
||||
|
||||
return ( $lc, $ld, $patt );
|
||||
}
|
||||
|
||||
sub print_version {
|
||||
chomp( my $hn = `hostname -s 2>/dev/null || hostname` );
|
||||
my $gv = substr( `git --version`, 12 );
|
||||
$ENV{GL_USER} or _die "GL_USER not set";
|
||||
print "hello $ENV{GL_USER}, this is " . ($ENV{USER} || "httpd") . "\@$hn running gitolite3 " . version() . " on git $gv\n";
|
||||
}
|
||||
|
||||
sub print_patterns {
|
||||
my ( $repos, @aa );
|
||||
|
||||
# find repo patterns only, call them with ^C flag included
|
||||
@$repos = grep { !/$REPONAME_PATT/ } @{ lister_dispatch('list-repos')->() };
|
||||
@aa = qw(R W ^C);
|
||||
listem( $repos, '', '', @aa );
|
||||
# but squelch the 'lc' and 'ld' flags for these
|
||||
}
|
||||
|
||||
sub print_phy_repos {
|
||||
my ( $repos, @aa );
|
||||
|
||||
# now get the actual repos and get R or W only
|
||||
_chdir( $rc{GL_REPO_BASE} );
|
||||
$repos = list_phy_repos(1);
|
||||
@aa = qw(R W);
|
||||
listem( $repos, $lc, $ld, @aa );
|
||||
}
|
||||
|
||||
sub listem {
|
||||
my ( $repos, $lc, $ld, @aa ) = @_;
|
||||
my $creator = '';
|
||||
for my $repo (@$repos) {
|
||||
next unless $repo =~ /$patt/;
|
||||
my $perm = '';
|
||||
$creator = creator($repo) if $lc;
|
||||
|
||||
my $desc = '';
|
||||
for my $d ("$ENV{GL_REPO_BASE}/$repo.git/description") {
|
||||
next unless $ld and -r $d;
|
||||
$desc = slurp($d);
|
||||
chomp($desc);
|
||||
}
|
||||
|
||||
for my $aa (@aa) {
|
||||
my $ret = access( $repo, $ENV{GL_USER}, $aa, 'any' );
|
||||
$perm .= ( $ret =~ /DENIED/ ? " " : " $aa" );
|
||||
}
|
||||
$perm =~ s/\^//;
|
||||
next unless $perm =~ /\S/;
|
||||
print "$perm\t$repo";
|
||||
print "\t$creator" if $lc;
|
||||
print "\t$desc" if $ld;
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage: gitolite list-dangling-repos
|
||||
|
||||
List all existing repos that no one can access remotely any more. They could
|
||||
be normal repos that were taken out of "repo" statements in the conf file, or
|
||||
wildcard repos whose matching "wild" pattern was taken out or changed so it no
|
||||
longer matches.
|
||||
|
||||
I would advise caution if you use this as a basis for deleting repos from the
|
||||
file system. A bug in this program could cause you to lose important data!
|
||||
=cut
|
||||
|
||||
usage() if @ARGV and $ARGV[0] eq '-h';
|
||||
|
||||
# get the two lists we need. %repos is the list of repos in "repo" statements
|
||||
# in the conf file. %phy_repos is the list of actual repos on disk. Our job
|
||||
# is to cull %phy_repos of all keys that have a matching key in %repos, where
|
||||
# "matching" means "string equal" or "regex match".
|
||||
my %repos = map { chomp; $_ => 1 } `gitolite list-repos`;
|
||||
for my $r ( grep /^@/, keys %repos ) {
|
||||
map { chomp; $repos{$_} = 1; } `gitolite list-members $r`;
|
||||
}
|
||||
my %phy_repos = map { chomp; $_ => 1 } `gitolite list-phy-repos`;
|
||||
|
||||
# Remove exact matches. But for repo names like "gtk+", you could have
|
||||
# collapsed this into the next step (the regex match).
|
||||
for my $pr (keys %phy_repos) {
|
||||
next unless exists $repos{$pr};
|
||||
delete $repos{$pr};
|
||||
delete $phy_repos{$pr};
|
||||
}
|
||||
|
||||
# Remove regex matches.
|
||||
for my $pr (keys %phy_repos) {
|
||||
my $matched = 0;
|
||||
my $pr2 = Gitolite::Conf::Load::generic_name($pr);
|
||||
for my $r (keys %repos) {
|
||||
if ($pr =~ /^$r$/ or $pr2 =~ /^$r$/) {
|
||||
$matched = 1;
|
||||
next;
|
||||
}
|
||||
}
|
||||
delete $phy_repos{$pr} if $matched;
|
||||
}
|
||||
|
||||
# what's left in %phy_repos are dangling repos.
|
||||
print join("\n", sort keys %phy_repos), "\n";
|
|
@ -1,124 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Getopt::Long;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
# gitolite command to lock and unlock (binary) files and deal with locks.
|
||||
|
||||
=for usage
|
||||
Usage: ssh git@host lock -l <repo> <file> # lock a file
|
||||
ssh git@host lock -u <repo> <file> # unlock a file
|
||||
ssh git@host lock --break <repo> <file> # break someone else's lock
|
||||
ssh git@host lock -ls <repo> # list locked files for repo
|
||||
|
||||
See doc/locking.mkd for other details.
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
$ENV{GL_USER} or _die "GL_USER not set";
|
||||
|
||||
my $op = '';
|
||||
$op = 'lock' if $ARGV[0] eq '-l';
|
||||
$op = 'unlock' if $ARGV[0] eq '-u';
|
||||
$op = 'break' if $ARGV[0] eq '--break';
|
||||
$op = 'list' if $ARGV[0] eq '-ls';
|
||||
usage() if not $op;
|
||||
shift;
|
||||
|
||||
my $repo = shift;
|
||||
_die "You are not authorised" if access( $repo, $ENV{GL_USER}, 'W', 'any' ) =~ /DENIED/;
|
||||
_die "You are not authorised" if $op eq 'break' and access( $repo, $ENV{GL_USER}, '+', 'any' ) =~ /DENIED/;
|
||||
|
||||
my $file = shift || '';
|
||||
usage() if $op ne 'list' and not $file;
|
||||
|
||||
_chdir( $ENV{GL_REPO_BASE} );
|
||||
_chdir("$repo.git");
|
||||
|
||||
my $ff = "gl-locks";
|
||||
|
||||
if ( $op eq 'lock' ) {
|
||||
f_lock( $repo, $file );
|
||||
} elsif ( $op eq 'unlock' ) {
|
||||
f_unlock( $repo, $file );
|
||||
} elsif ( $op eq 'break' ) {
|
||||
f_break( $repo, $file );
|
||||
} elsif ( $op eq 'list' ) {
|
||||
f_list($repo);
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# everything below assumes we have already chdir'd to "$repo.git". Also, $ff
|
||||
# is used as a global.
|
||||
|
||||
sub f_lock {
|
||||
my ( $repo, $file ) = @_;
|
||||
|
||||
my %locks = get_locks();
|
||||
_die "'$file' locked by '$locks{$file}{USER}' since " . localtime( $locks{$file}{TIME} ) if $locks{$file}{USER};
|
||||
$locks{$file}{USER} = $ENV{GL_USER};
|
||||
$locks{$file}{TIME} = time;
|
||||
put_locks(%locks);
|
||||
}
|
||||
|
||||
sub f_unlock {
|
||||
my ( $repo, $file ) = @_;
|
||||
|
||||
my %locks = get_locks();
|
||||
_die "'$file' not locked by '$ENV{GL_USER}'" if ( $locks{$file}{USER} || '' ) ne $ENV{GL_USER};
|
||||
delete $locks{$file};
|
||||
put_locks(%locks);
|
||||
}
|
||||
|
||||
sub f_break {
|
||||
my ( $repo, $file ) = @_;
|
||||
|
||||
my %locks = get_locks();
|
||||
_die "'$file' was not locked" unless $locks{$file};
|
||||
push @{ $locks{BREAKS} }, time . " $ENV{GL_USER} $locks{$file}{USER} $locks{$file}{TIME} $file";
|
||||
delete $locks{$file};
|
||||
put_locks(%locks);
|
||||
}
|
||||
|
||||
sub f_list {
|
||||
my $repo = shift;
|
||||
|
||||
my %locks = get_locks();
|
||||
print "\n# locks held:\n\n";
|
||||
map { print "$locks{$_}{USER}\t$_\t(" . scalar(localtime($locks{$_}{TIME})) . ")\n" } grep { $_ ne 'BREAKS' } sort keys %locks;
|
||||
print "\n# locks broken:\n\n";
|
||||
for my $b ( @{ $locks{BREAKS} } ) {
|
||||
my ( $when, $who, $whose, $how_old, $what ) = split ' ', $b;
|
||||
print "$who\t$what\t(" . scalar( localtime($when) ) . ")\t(locked by $whose at " . scalar( localtime($how_old) ) . ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub get_locks {
|
||||
if ( -f $ff ) {
|
||||
our %locks;
|
||||
|
||||
my $t = slurp($ff);
|
||||
eval $t;
|
||||
_die "do '$ff' failed with '$@', contact your administrator" if $@;
|
||||
|
||||
return %locks;
|
||||
}
|
||||
return ();
|
||||
}
|
||||
|
||||
sub put_locks {
|
||||
my %locks = @_;
|
||||
|
||||
use Data::Dumper;
|
||||
$Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
|
||||
my $dumped_data = Data::Dumper->Dump( [ \%locks ], [qw(*locks)] );
|
||||
_print( $ff, $dumped_data );
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $tid;
|
||||
|
||||
BEGIN {
|
||||
$tid = $ENV{GL_TID} || 0;
|
||||
delete $ENV{GL_TID};
|
||||
}
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage 1: gitolite mirror push <slave> <repo>
|
||||
Usage 2: ssh git@master-server mirror push <slave> <repo>
|
||||
|
||||
Forces a push of one repo to one slave.
|
||||
|
||||
Usage 1 is directly on the master server. Nothing is checked; if the slave
|
||||
accepts it, the push happens, even if the slave is not in any slaves
|
||||
option. This is how you do delayed or lagged pushes to servers that do not
|
||||
need real-time updates or have bandwidth/connectivity issues.
|
||||
|
||||
Usage 2 can be initiated by *any* user who has *any* gitolite access to the
|
||||
master server, but it checks that the slave is in one of the slaves options
|
||||
before doing the push.
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
|
||||
_die "HOSTNAME not set" if not $rc{HOSTNAME};
|
||||
|
||||
my ( $cmd, $host, $repo ) = @ARGV;
|
||||
usage() if not $repo;
|
||||
|
||||
if ( $cmd eq 'push' ) {
|
||||
valid_slave( $host, $repo ) if exists $ENV{GL_USER};
|
||||
# will die if host not in slaves for repo
|
||||
|
||||
trace( 1, "TID=$tid host=$host repo=$repo", "gitolite mirror push started" );
|
||||
_chdir( $rc{GL_REPO_BASE} );
|
||||
_chdir("$repo.git");
|
||||
|
||||
if (-f "gl-creator") {
|
||||
# try to propagate the wild repo, including creator name and gl-perms
|
||||
my $creator = `cat gl-creator`; chomp($creator);
|
||||
trace(1, `cat gl-perms 2>/dev/null | ssh $host CREATOR=$creator perms -c \\'$repo\\' 2>/dev/null`);
|
||||
}
|
||||
|
||||
my $errors = 0;
|
||||
for (`git push --mirror $host:$repo 2>&1`) {
|
||||
$errors = 1 if $?;
|
||||
print STDERR "$_" if -t STDERR or exists $ENV{GL_USER};
|
||||
chomp;
|
||||
if (/FATAL/) {
|
||||
$errors = 1;
|
||||
gl_log( 'mirror', $_ );
|
||||
} else {
|
||||
trace( 1, "mirror: $_" );
|
||||
}
|
||||
}
|
||||
exit $errors;
|
||||
}
|
||||
|
||||
sub valid_slave {
|
||||
my ( $host, $repo ) = @_;
|
||||
_die "invalid repo '$repo'" unless $repo =~ $REPONAME_PATT;
|
||||
|
||||
my $ref = git_config( $repo, "^gitolite-options\\.mirror\\.slaves.*" );
|
||||
my %list = map { $_ => 1 } map { split } values %$ref;
|
||||
|
||||
_die "'$host' not a valid slave for '$repo'" unless $list{$host};
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage: ssh git@host perms -l <repo>
|
||||
ssh git@host perms <repo> - <rolename> <username>
|
||||
ssh git@host perms <repo> + <rolename> <username>
|
||||
|
||||
List or set permissions for user-created ("wild") repo. The first usage shown
|
||||
will list the current contents of the permissions file. The other two will
|
||||
change permissions, adding or removing a user from a role.
|
||||
|
||||
Examples:
|
||||
ssh git@host perms foo + READERS user1
|
||||
ssh git@host perms foo + READERS user2
|
||||
ssh git@host perms foo + READERS user3
|
||||
|
||||
----
|
||||
There is also a batch mode useful for scripting and bulk loading. Do not
|
||||
combine this with the +/- mode above. This mode also accepts an optional "-c"
|
||||
flag to create the repo if it does not already exist (assuming $GL_USER has
|
||||
permissions to create it).
|
||||
|
||||
Examples:
|
||||
cat copy-of-backed-up-gl-perms | ssh git@host perms <repo>
|
||||
cat copy-of-backed-up-gl-perms | ssh git@host perms -c <repo>
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
|
||||
$ENV{GL_USER} or _die "GL_USER not set";
|
||||
|
||||
my $list = 0;
|
||||
if ( $ARGV[0] eq '-l' ) {
|
||||
$list++;
|
||||
shift;
|
||||
getperms(@ARGV); # doesn't return
|
||||
}
|
||||
|
||||
my $generic_error = "repo does not exist, or you are not authorised";
|
||||
|
||||
# auto-create the repo if -c passed and repo doesn't exist
|
||||
if ( $ARGV[0] eq '-c' ) {
|
||||
shift;
|
||||
my $repo = $ARGV[0] or usage();
|
||||
_die "invalid repo '$repo'" unless $repo =~ $REPONAME_PATT;
|
||||
|
||||
if (not -d "$rc{GL_REPO_BASE}/$repo.git") {
|
||||
my $ret = access( $repo, $ENV{GL_USER}, '^C', 'any' );
|
||||
_die $generic_error if $ret =~ /DENIED/;
|
||||
|
||||
require Gitolite::Conf::Store;
|
||||
Gitolite::Conf::Store->import;
|
||||
new_wild_repo( $repo, $ENV{GL_USER}, 'perms-c' );
|
||||
gl_log( 'create', $repo, $ENV{GL_USER}, 'perms-c' );
|
||||
}
|
||||
}
|
||||
|
||||
my $repo = shift;
|
||||
setperms(@ARGV);
|
||||
_system( "gitolite", "trigger", "POST_CREATE", $repo, $ENV{GL_USER}, 'perms' );
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub getperms {
|
||||
my $repo = shift;
|
||||
_die $generic_error if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
|
||||
my $pf = "$rc{GL_REPO_BASE}/$repo.git/gl-perms";
|
||||
|
||||
print slurp($pf) if -f $pf;
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub setperms {
|
||||
_die $generic_error if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
|
||||
my $pf = "$rc{GL_REPO_BASE}/$repo.git/gl-perms";
|
||||
|
||||
if ( not @_ ) {
|
||||
# legacy mode; pipe data in
|
||||
print STDERR "'batch' mode started, waiting for input (run with '-h' for details).\n";
|
||||
print STDERR "Please hit Ctrl-C if you did not intend to do this.\n";
|
||||
@ARGV = ();
|
||||
my @a;
|
||||
for (<>) {
|
||||
_die "Invalid role '$1'; check the rc file" if /(\S+)/ and not $rc{ROLES}{$1};
|
||||
push @a, $_;
|
||||
}
|
||||
print STDERR "\n"; # make sure Ctrl-C gets caught
|
||||
_print( $pf, @a );
|
||||
return;
|
||||
}
|
||||
|
||||
_die "Invalid syntax. Please re-run with '-h' for detailed usage" if @_ != 3;
|
||||
my ( $op, $role, $user ) = @_;
|
||||
_die "Invalid syntax. Please re-run with '-h' for detailed usage" if $op ne '+' and $op ne '-';
|
||||
_die "Invalid role '$role'; check the rc file" if not $rc{ROLES}{$role};
|
||||
_die "Invalid user '$user'" if not $user =~ $USERNAME_PATT;
|
||||
|
||||
my $text = '';
|
||||
my @text = slurp($pf) if -f $pf;
|
||||
|
||||
my $present = grep { $_ eq "$role $user\n" } @text;
|
||||
|
||||
if ( $op eq '-' ) {
|
||||
if ( not $present ) {
|
||||
_warn "'$role $user' was not present in file";
|
||||
} else {
|
||||
@text = grep { $_ ne "$role $user\n" } @text;
|
||||
_print( $pf, @text );
|
||||
}
|
||||
} else {
|
||||
if ($present) {
|
||||
_warn "'$role $user' already present in file";
|
||||
} else {
|
||||
push @text, "$role $user\n";
|
||||
@text = sort @text;
|
||||
_print( $pf, @text );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
|
||||
print glrc('default-text');
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
export GL_BYPASS_ACCESS_CHECKS=1
|
||||
|
||||
git push "$@"
|
|
@ -1,149 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Easy;
|
||||
|
||||
=for admins
|
||||
|
||||
BUNDLE SUPPORT
|
||||
|
||||
(1) For each repo in gitolite.conf for which you want bundle support (or
|
||||
'@all', if you wish), add the following line:
|
||||
|
||||
option bundle = 1
|
||||
|
||||
Or you can say:
|
||||
|
||||
option bundle.ttl = <number>
|
||||
|
||||
A bundle file that is more than <number> seconds old (default value
|
||||
86400, i.e., 1 day) is recreated on the next bundle request. Increase
|
||||
this if your repo is not terribly active.
|
||||
|
||||
Note: a bundle file is also deleted and recreated if it contains a ref
|
||||
that was then either deleted or rewound in the repo. This is checked
|
||||
on every invocation.
|
||||
|
||||
(2) Add 'rsync' to the COMMANDS list in the rc file
|
||||
|
||||
|
||||
GENERIC RSYNC SUPPORT
|
||||
|
||||
TBD
|
||||
|
||||
=cut
|
||||
|
||||
=for usage
|
||||
rsync helper for gitolite
|
||||
|
||||
BUNDLE SUPPORT
|
||||
|
||||
Admins: see src/commands/rsync for setup instructions
|
||||
|
||||
Users:
|
||||
rsync -P git@host:repo.bundle .
|
||||
# downloads a file called "<basename of repo>.bundle"; repeat as
|
||||
# needed till the whole thing is downloaded
|
||||
git clone repo.bundle repo
|
||||
cd repo
|
||||
git remote set-url origin git@host:repo
|
||||
git fetch origin # and maybe git pull, etc. to freshen the clone
|
||||
|
||||
GENERIC RSYNC SUPPORT
|
||||
|
||||
TBD
|
||||
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
|
||||
# rsync driver program. Several things can be done later, but for now it
|
||||
# drives just the 'bundle' transfer.
|
||||
|
||||
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^rsync --server --sender (-[-\w=.]+ )+\. (\S+)\.bundle$/ ) {
|
||||
|
||||
my $repo = $2;
|
||||
$repo =~ s/\.git$//;
|
||||
|
||||
# all errors have the same message to avoid leaking info
|
||||
can_read($repo) or _die "you are not authorised";
|
||||
my %config = config( $repo, "gitolite-options.bundle" ) or _die "you are not authorised";
|
||||
|
||||
my $ttl = $config{'gitolite-options.bundle.ttl'} || 86400; # in seconds (default 1 day)
|
||||
|
||||
my $bundle = bundle_create( $repo, $ttl );
|
||||
|
||||
$ENV{SSH_ORIGINAL_COMMAND} =~ s( \S+\.bundle)( $bundle);
|
||||
trace( 1, "rsync bundle", $ENV{SSH_ORIGINAL_COMMAND} );
|
||||
Gitolite::Common::_system( split ' ', $ENV{SSH_ORIGINAL_COMMAND} );
|
||||
exit 0;
|
||||
}
|
||||
|
||||
_warn "invalid rsync command '$ENV{SSH_ORIGINAL_COMMAND}'";
|
||||
usage();
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# helpers
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub bundle_create {
|
||||
my ( $repo, $ttl ) = @_;
|
||||
my $bundle = "$repo.bundle";
|
||||
$bundle =~ s(.*/)();
|
||||
my $recreate = 0;
|
||||
|
||||
my ( %b, %r );
|
||||
if ( -f $bundle ) {
|
||||
%b = map { chomp; reverse split; } `git ls-remote --heads --tags $bundle`;
|
||||
%r = map { chomp; reverse split; } `git ls-remote --heads --tags .`;
|
||||
|
||||
for my $ref ( sort keys %b ) {
|
||||
|
||||
my $mtime = ( stat $bundle )[9];
|
||||
if ( time() - $mtime > $ttl ) {
|
||||
trace( 1, "bundle too old" );
|
||||
$recreate++;
|
||||
last;
|
||||
}
|
||||
|
||||
if ( not $r{$ref} ) {
|
||||
trace( 1, "ref '$ref' deleted in repo" );
|
||||
$recreate++;
|
||||
last;
|
||||
}
|
||||
|
||||
if ( $r{$ref} eq $b{$ref} ) {
|
||||
# same on both sides; ignore
|
||||
delete $r{$ref};
|
||||
delete $b{$ref};
|
||||
next;
|
||||
}
|
||||
|
||||
`git rev-list --count --left-right $b{$ref}...$r{$ref}` =~ /^(\d+)\s+(\d+)$/ or _die "git too old";
|
||||
if ($1) {
|
||||
trace( 1, "ref '$ref' rewound in repo" );
|
||||
$recreate++;
|
||||
last;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
trace( 1, "no bundle found" );
|
||||
$recreate++;
|
||||
}
|
||||
|
||||
return $bundle if not $recreate;
|
||||
|
||||
trace( 1, "creating bundle for '$repo'" );
|
||||
-f $bundle and ( unlink $bundle or die "a horrible death" );
|
||||
system("git bundle create $bundle --branches --tags >&2");
|
||||
|
||||
return $bundle;
|
||||
}
|
||||
|
||||
sub trace {
|
||||
Gitolite::Common::trace(@_);
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# complete rewrite of the sshkeys-lint program. Usage has changed, see
|
||||
# usage() function or run without arguments.
|
||||
|
||||
use Getopt::Long;
|
||||
my $admin = 0;
|
||||
my $quiet = 0;
|
||||
my $help = 0;
|
||||
GetOptions( 'admin|a=s' => \$admin, 'quiet|q' => \$quiet, 'help|h' => \$help );
|
||||
|
||||
use Data::Dumper;
|
||||
$Data::Dumper::Deepcopy = 1;
|
||||
$|++;
|
||||
|
||||
my $in_gl_section = 0;
|
||||
my $warnings = 0;
|
||||
|
||||
sub dbg {
|
||||
use Data::Dumper;
|
||||
for my $i (@_) {
|
||||
print STDERR "DBG: " . Dumper($i);
|
||||
}
|
||||
}
|
||||
|
||||
sub msg {
|
||||
my $warning = shift;
|
||||
return if $quiet and not $warning;
|
||||
$warnings++ if $warning;
|
||||
print "sshkeys-lint: " . ( $warning ? "WARNING: " : "" ) . $_ for @_;
|
||||
}
|
||||
|
||||
usage() if $help;
|
||||
|
||||
our @pubkeyfiles = @ARGV; @ARGV = ();
|
||||
my $kd = "$ENV{HOME}/.gitolite/keydir";
|
||||
if ( not @pubkeyfiles ) {
|
||||
chomp( @pubkeyfiles = `find $kd -type f -name "*.pub" | sort` );
|
||||
}
|
||||
|
||||
if ( -t STDIN ) {
|
||||
@ARGV = ("$ENV{HOME}/.ssh/authorized_keys");
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
my @authkeys;
|
||||
my %seen_fprints;
|
||||
my %pkf_by_fp;
|
||||
msg 0, "==== checking authkeys file:\n";
|
||||
fill_authkeys(); # uses up STDIN
|
||||
|
||||
if ($admin) {
|
||||
my $fp = fprint("$admin.pub");
|
||||
my $fpu = ( $fp && $seen_fprints{$fp}{user} || 'no access' );
|
||||
# dbg("fpu = $fpu, admin=$admin");
|
||||
#<<<
|
||||
die "\t\t*** FATAL ***\n" .
|
||||
"$admin.pub maps to $fpu, not $admin.\n" .
|
||||
"You will not be able to access gitolite with this key.\n" .
|
||||
"Look for the 'ssh troubleshooting' link in http://sitaramc.github.com/gitolite/.\n"
|
||||
if $fpu ne "user $admin";
|
||||
#>>>
|
||||
}
|
||||
|
||||
msg 0, "==== checking pubkeys:\n" if @pubkeyfiles;
|
||||
for my $pkf (@pubkeyfiles) {
|
||||
# get the short name for the pubkey file
|
||||
( my $pkfsn = $pkf ) =~ s(^$kd/)();
|
||||
|
||||
my $fp = fprint($pkf);
|
||||
next unless $fp;
|
||||
msg 1, "$pkfsn appears to be a COPY of $pkf_by_fp{$fp}\n" if $pkf_by_fp{$fp};
|
||||
$pkf_by_fp{$fp} ||= $pkfsn;
|
||||
my $fpu = ( $seen_fprints{$fp}{user} || 'no access' );
|
||||
msg 0, "$pkfsn maps to $fpu\n";
|
||||
}
|
||||
|
||||
if ($warnings) {
|
||||
print "\n$warnings warnings found\n";
|
||||
}
|
||||
|
||||
exit $warnings;
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
sub fill_authkeys {
|
||||
while (<>) {
|
||||
my $seq = $.;
|
||||
next if ak_comment($_); # also sets/clears $in_gl_section global
|
||||
my $fp = fprint($_);
|
||||
my $user = user($_);
|
||||
|
||||
check( $seq, $fp, $user );
|
||||
|
||||
$authkeys[$seq]{fprint} = $fp;
|
||||
$authkeys[$seq]{ustatus} = $user;
|
||||
}
|
||||
}
|
||||
|
||||
sub check {
|
||||
my ( $seq, $fp, $user ) = @_;
|
||||
|
||||
msg 1, "line $seq, $user key found *outside* gitolite section!\n"
|
||||
if $user =~ /^user / and not $in_gl_section;
|
||||
|
||||
msg 1, "line $seq, $user key found *inside* gitolite section!\n"
|
||||
if $user !~ /^user / and $in_gl_section;
|
||||
|
||||
if ( $seen_fprints{$fp} ) {
|
||||
#<<<
|
||||
msg 1, "authkeys line $seq ($user) will be ignored by sshd; " .
|
||||
"same key found on line " .
|
||||
$seen_fprints{$fp}{seq} . " (" .
|
||||
$seen_fprints{$fp}{user} . ")\n";
|
||||
return;
|
||||
#>>>
|
||||
}
|
||||
|
||||
$seen_fprints{$fp}{seq} = $seq;
|
||||
$seen_fprints{$fp}{user} = $user;
|
||||
}
|
||||
|
||||
sub user {
|
||||
my $user = '';
|
||||
$user ||= "user $1" if /^command=.*gitolite-shell (.*?)"/;
|
||||
$user ||= "unknown command" if /^command/;
|
||||
$user ||= "shell access" if /^ssh-(rsa|dss)/;
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
sub ak_comment {
|
||||
local $_ = shift;
|
||||
$in_gl_section = 1 if /^# gitolite start/;
|
||||
$in_gl_section = 0 if /^# gitolite end/;
|
||||
die "gitosis? what's that?\n" if /^#.*gitosis/;
|
||||
return /^\s*(#|$)/;
|
||||
}
|
||||
|
||||
sub fprint {
|
||||
local $_ = shift;
|
||||
my ( $fh, $tempfn, $in );
|
||||
if (/ssh-(dss|rsa) /) {
|
||||
# an actual key was passed. Since ssh-keygen requires an actual file,
|
||||
# make a temp file to take the data and pass on to ssh-keygen
|
||||
s/^.* (ssh-dss|ssh-rsa)/$1/;
|
||||
use File::Temp qw(tempfile);
|
||||
( $fh, $tempfn ) = tempfile();
|
||||
$in = $tempfn;
|
||||
print $fh $_;
|
||||
close $fh;
|
||||
} else {
|
||||
# a filename was passed
|
||||
$in = $_;
|
||||
}
|
||||
# dbg("in = $in");
|
||||
-f $in or die "file not found: $in\n";
|
||||
open( $fh, "ssh-keygen -l -f $in |" ) or die "could not fork: $!\n";
|
||||
my $fp = <$fh>;
|
||||
# dbg("fp = $fp");
|
||||
close $fh;
|
||||
unlink $tempfn if $tempfn;
|
||||
warn "$fp\n" unless $fp =~ /([0-9a-f][0-9a-f](:[0-9a-f][0-9a-f])+)/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
sub usage {
|
||||
print <<EOF;
|
||||
|
||||
Usage: gitolite sshkeys-lint [-q] [optional list of pubkey filenames]
|
||||
(optionally, STDIN can be a pipe or redirected from a file; see below)
|
||||
|
||||
Look for potential problems in ssh keys.
|
||||
|
||||
sshkeys-lint expects:
|
||||
- the contents of an authorized_keys file via STDIN, otherwise it uses
|
||||
\$HOME/.ssh/authorized_keys
|
||||
- one or more pubkey filenames as arguments, otherwise it uses all the keys
|
||||
found (recursively) in \$HOME/.gitolite/keydir
|
||||
|
||||
The '-q' option will print only warnings instead of all mappings.
|
||||
|
||||
Note that this runs ssh-keygen -l for each line in the authkeys file and each
|
||||
pubkey in the argument list, so be wary of running it on something huge. This
|
||||
is meant for troubleshooting.
|
||||
|
||||
EOF
|
||||
exit 1;
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
=for usage
|
||||
Usage for this command is not that simple. Please read the full documentation
|
||||
in doc/sskm.mkd or online at http://sitaramc.github.com/gitolite/sskm.html.
|
||||
=cut
|
||||
|
||||
usage() if @ARGV and $ARGV[0] eq '-h';
|
||||
|
||||
my $rb = $rc{GL_REPO_BASE};
|
||||
my $ab = $rc{GL_ADMIN_BASE};
|
||||
# get to the keydir
|
||||
_chdir("$ab/keydir");
|
||||
|
||||
# save arguments for later
|
||||
my $operation = shift || 'list';
|
||||
my $keyid = shift || '';
|
||||
# keyid must fit a very specific pattern
|
||||
$keyid and $keyid !~ /^@[-0-9a-z_]+$/i and die "invalid keyid $keyid\n";
|
||||
|
||||
# get the actual userid and keytype
|
||||
my $gl_user = $ENV{GL_USER};
|
||||
my $keytype = '';
|
||||
$keytype = $1 if $gl_user =~ s/^zzz-marked-for-(...)-//;
|
||||
print STDERR "hello $gl_user, you are currently using "
|
||||
. (
|
||||
$keytype
|
||||
? "a key in the 'marked for $keytype' state\n"
|
||||
: "a normal (\"active\") key\n"
|
||||
);
|
||||
|
||||
# ----
|
||||
# first collect the keys
|
||||
|
||||
my ( @pubkeys, @marked_for_add, @marked_for_del );
|
||||
# get the list of pubkey files for this user, including pubkeys marked for
|
||||
# add/delete
|
||||
|
||||
for my $pubkey (`find . -type f -name "*.pub" | sort`) {
|
||||
chomp($pubkey);
|
||||
$pubkey =~ s(^./)(); # artifact of the find command
|
||||
|
||||
my $user = $pubkey;
|
||||
$user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub
|
||||
$user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz
|
||||
|
||||
next unless $user eq $gl_user or $user =~ /^zzz-marked-for-...-$gl_user/;
|
||||
|
||||
if ( $user =~ m(^zzz-marked-for-add-) ) {
|
||||
push @marked_for_add, $pubkey;
|
||||
} elsif ( $user =~ m(^zzz-marked-for-del-) ) {
|
||||
push @marked_for_del, $pubkey;
|
||||
} else {
|
||||
push @pubkeys, $pubkey;
|
||||
}
|
||||
}
|
||||
|
||||
# ----
|
||||
# list mode; just do it and exit
|
||||
sub print_keylist {
|
||||
my ( $message, @list ) = @_;
|
||||
return unless @list;
|
||||
print "== $message ==\n";
|
||||
my $count = 1;
|
||||
for (@list) {
|
||||
my $fp = fingerprint($_);
|
||||
s/zzz-marked(\/|-for-...-)//g;
|
||||
print $count++ . ": $fp : $_\n";
|
||||
}
|
||||
}
|
||||
if ( $operation eq 'list' ) {
|
||||
print "you have the following keys:\n";
|
||||
print_keylist( "active keys", @pubkeys );
|
||||
print_keylist( "keys marked for addition/replacement", @marked_for_add );
|
||||
print_keylist( "keys marked for deletion", @marked_for_del );
|
||||
print "\n\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
# ----
|
||||
# please see docs for details on how a user interacts with this
|
||||
|
||||
if ( $keytype eq '' ) {
|
||||
# user logging in with a normal key
|
||||
die "valid operations: add, del, undo-add, confirm-del\n" unless $operation =~ /^(add|del|confirm-del|undo-add)$/;
|
||||
if ( $operation eq 'add' ) {
|
||||
print STDERR "please supply the new key on STDIN. (I recommend you
|
||||
don't try to do this interactively, but use a pipe)\n";
|
||||
kf_add( $gl_user, $keyid, safe_stdin() );
|
||||
} elsif ( $operation eq 'del' ) {
|
||||
kf_del( $gl_user, $keyid );
|
||||
} elsif ( $operation eq 'confirm-del' ) {
|
||||
die "you dont have any keys marked for deletion\n" unless @marked_for_del;
|
||||
kf_confirm_del( $gl_user, $keyid );
|
||||
} elsif ( $operation eq 'undo-add' ) {
|
||||
die "you dont have any keys marked for addition\n" unless @marked_for_add;
|
||||
kf_undo_add( $gl_user, $keyid );
|
||||
}
|
||||
} elsif ( $keytype eq 'del' ) {
|
||||
# user is using a key that was marked for deletion. The only possible use
|
||||
# for this is that she changed her mind for some reason (maybe she marked
|
||||
# the wrong key for deletion) or is not able to get her client-side sshd
|
||||
# to stop using this key
|
||||
die "valid operations: undo-del\n" unless $operation eq 'undo-del';
|
||||
|
||||
# reinstate the key
|
||||
kf_undo_del( $gl_user, $keyid );
|
||||
} elsif ( $keytype eq 'add' ) {
|
||||
die "valid operations: confirm-add\n" unless $operation eq 'confirm-add';
|
||||
# user is trying to validate a key that has been previously marked for
|
||||
# addition. This isn't interactive, but it *could* be... if someone asked
|
||||
kf_confirm_add( $gl_user, $keyid );
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
# ----
|
||||
|
||||
# make a temp clone and switch to it
|
||||
our $TEMPDIR;
|
||||
BEGIN { $TEMPDIR = `mktemp -d -t tmp.XXXXXXXXXX`; }
|
||||
END { `/bin/rm -rf $TEMPDIR`; }
|
||||
|
||||
sub cd_temp_clone {
|
||||
chomp($TEMPDIR);
|
||||
hushed_git( "clone", "$rb/gitolite-admin.git", "$TEMPDIR" );
|
||||
chdir($TEMPDIR);
|
||||
my $hostname = `hostname`; chomp($hostname);
|
||||
hushed_git( "config", "--get", "user.email" ) and hushed_git( "config", "user.email", $ENV{USER} . "@" . $hostname );
|
||||
hushed_git( "config", "--get", "user.name" ) and hushed_git( "config", "user.name", "$ENV{USER} on $hostname" );
|
||||
}
|
||||
|
||||
sub fingerprint {
|
||||
my $fp = `ssh-keygen -l -f $_[0]`;
|
||||
die "does not seem to be a valid pubkey\n" unless $fp =~ /(([0-9a-f]+:)+[0-9a-f]+ )/i;
|
||||
return $1;
|
||||
}
|
||||
|
||||
sub safe_stdin {
|
||||
# read one line from STDIN
|
||||
my $data;
|
||||
my $ret = read STDIN, $data, 4096;
|
||||
# current pubkeys are approx 400 bytes so we go a little overboard
|
||||
die "could not read pubkey data" . ( defined($ret) ? "" : ": $!" ) . "\n" unless $ret;
|
||||
die "pubkey data seems to have more than one line\n" if $data =~ /\n./;
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub hushed_git {
|
||||
local (*STDOUT) = \*STDOUT;
|
||||
local (*STDERR) = \*STDERR;
|
||||
open( STDOUT, ">", "/dev/null" );
|
||||
open( STDERR, ">", "/dev/null" );
|
||||
system( "git", @_ );
|
||||
}
|
||||
|
||||
sub highlander {
|
||||
# there can be only one
|
||||
my ( $keyid, $die_if_empty, @a ) = @_;
|
||||
# too many?
|
||||
if ( @a > 1 ) {
|
||||
print STDERR "
|
||||
more than one key satisfies this condition, and I can't deal with that!
|
||||
The keys are:
|
||||
|
||||
";
|
||||
print STDERR "\t" . join( "\n\t", @a ), "\n\n";
|
||||
exit 1;
|
||||
}
|
||||
# too few?
|
||||
die "no keys with " . ( $keyid || "empty" ) . " keyid found\n" if $die_if_empty and not @a;
|
||||
|
||||
return @a;
|
||||
}
|
||||
|
||||
sub kf_add {
|
||||
my ( $gl_user, $keyid, $keymaterial ) = @_;
|
||||
|
||||
# add a new "marked for addition" key for $gl_user.
|
||||
cd_temp_clone();
|
||||
chdir("keydir");
|
||||
|
||||
mkdir("zzz-marked");
|
||||
_print( "zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub", $keymaterial );
|
||||
hushed_git( "add", "." ) and die "git add failed\n";
|
||||
my $fp = fingerprint("zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub");
|
||||
hushed_git( "commit", "-m", "sskm: add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||
}
|
||||
|
||||
sub kf_confirm_add {
|
||||
my ( $gl_user, $keyid ) = @_;
|
||||
# find entries in both @pubkeys and @marked_for_add whose basename matches $gl_user$keyid
|
||||
my @pk = highlander( $keyid, 0, grep { m(^(.*/)?$gl_user$keyid.pub$) } @pubkeys );
|
||||
my @mfa = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub$) } @marked_for_add );
|
||||
|
||||
cd_temp_clone();
|
||||
chdir("keydir");
|
||||
|
||||
my $fp = fingerprint( $mfa[0] );
|
||||
if ( $pk[0] ) {
|
||||
hushed_git( "mv", "-f", $mfa[0], $pk[0] );
|
||||
hushed_git( "commit", "-m", "sskm: confirm-add (replace) $pk[0] ($fp)" ) and die "git commit failed\n";
|
||||
} else {
|
||||
hushed_git( "mv", "-f", $mfa[0], "$gl_user$keyid.pub" );
|
||||
hushed_git( "commit", "-m", "sskm: confirm-add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||
}
|
||||
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||
}
|
||||
|
||||
sub kf_undo_add {
|
||||
# XXX some code at start is shared with kf_confirm_add
|
||||
my ( $gl_user, $keyid ) = @_;
|
||||
my @mfa = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-add-$gl_user$keyid.pub$) } @marked_for_add );
|
||||
|
||||
cd_temp_clone();
|
||||
chdir("keydir");
|
||||
|
||||
my $fp = fingerprint( $mfa[0] );
|
||||
hushed_git( "rm", $mfa[0] );
|
||||
hushed_git( "commit", "-m", "sskm: undo-add $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||
}
|
||||
|
||||
sub kf_del {
|
||||
my ( $gl_user, $keyid ) = @_;
|
||||
|
||||
cd_temp_clone();
|
||||
chdir("keydir");
|
||||
|
||||
mkdir("zzz-marked");
|
||||
my @pk = highlander( $keyid, 1, grep { m(^(.*/)?$gl_user$keyid.pub$) } @pubkeys );
|
||||
|
||||
my $fp = fingerprint( $pk[0] );
|
||||
hushed_git( "mv", $pk[0], "zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub" ) and die "git mv failed\n";
|
||||
hushed_git( "commit", "-m", "sskm: del $pk[0] ($fp)" ) and die "git commit failed\n";
|
||||
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||
}
|
||||
|
||||
sub kf_confirm_del {
|
||||
my ( $gl_user, $keyid ) = @_;
|
||||
my @mfd = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub$) } @marked_for_del );
|
||||
|
||||
cd_temp_clone();
|
||||
chdir("keydir");
|
||||
|
||||
my $fp = fingerprint( $mfd[0] );
|
||||
hushed_git( "rm", $mfd[0] );
|
||||
hushed_git( "commit", "-m", "sskm: confirm-del $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||
}
|
||||
|
||||
sub kf_undo_del {
|
||||
my ( $gl_user, $keyid ) = @_;
|
||||
|
||||
my @mfd = highlander( $keyid, 1, grep { m(^zzz-marked/zzz-marked-for-del-$gl_user$keyid.pub$) } @marked_for_del );
|
||||
|
||||
print STDERR "
|
||||
You're undeleting a key that is currently marked for deletion.
|
||||
Hit ENTER to undelete this key
|
||||
Hit Ctrl-C to cancel the undelete
|
||||
Please see documentation for caveats on the undelete process as well as how to
|
||||
actually delete it.
|
||||
";
|
||||
<>; # yeay... always wanted to do that -- throw away user input!
|
||||
|
||||
cd_temp_clone();
|
||||
chdir("keydir");
|
||||
|
||||
my $fp = fingerprint( $mfd[0] );
|
||||
hushed_git( "mv", "-f", $mfd[0], "$gl_user$keyid.pub" );
|
||||
hushed_git( "commit", "-m", "sskm: undo-del $gl_user$keyid ($fp)" ) and die "git commit failed\n";
|
||||
system("gitolite push >/dev/null 2>/dev/null") and die "git push failed\n";
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Usage: ssh git@host sudo <user> <command> <arguments>
|
||||
#
|
||||
# Let super-user run commands as any other user. "Super-user" is defined as
|
||||
# "have write access to the gitolite-admin repo".
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
|
||||
[ -z "$2" ] && usage
|
||||
[ "$1" = "-h" ] && usage
|
||||
[ -z "$GL_USER" ] && die GL_USER not set
|
||||
|
||||
gitolite access -q gitolite-admin $GL_USER W any || die "You are not authorised"
|
||||
|
||||
user="$1"; shift
|
||||
cmd="$1"; shift
|
||||
|
||||
# switch user
|
||||
GL_USER="$user"
|
||||
|
||||
# figure out if the command is allowed from a remote user
|
||||
gitolite query-rc -q COMMANDS $cmd || die "Command '$cmd' not allowed"
|
||||
gitolite $cmd "$@"
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
my $svnserve = $rc{SVNSERVE} || '';
|
||||
$svnserve ||= "/usr/bin/svnserve -r /var/svn/ -t --tunnel-user=%u";
|
||||
|
||||
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
||||
|
||||
die "expecting 'svnserve -t', got '$cmd'\n" unless $cmd eq 'svnserve -t';
|
||||
|
||||
$svnserve =~ s/%u/$ENV{GL_USER}/g;
|
||||
exec $svnserve;
|
||||
die "svnserve exec failed\n";
|
|
@ -1,31 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Usage: ssh git@host symbolic-ref <repo> <arguments to git-symbolic-ref>
|
||||
#
|
||||
# allow 'git symbolic-ref' over a gitolite connection
|
||||
|
||||
# Security: remember all arguments to commands must match a very conservative
|
||||
# pattern. Once that is assured, the symbolic-ref command has no security
|
||||
# related side-effects, so we don't check arguments at all.
|
||||
|
||||
# Note: because of the restriction on allowed characters in arguments, you
|
||||
# can't supply an arbitrary string to the '-m' option. The simplest
|
||||
# work-around is-to-just-use-join-up-words-like-this if you feel the need to
|
||||
# supply a "reason" string. In any case this is useless by default; you'd
|
||||
# have to have core.logAllRefUpdates set for it to have any meaning.
|
||||
|
||||
die() { echo "$@" >&2; exit 1; }
|
||||
usage() { perl -lne 'print substr($_, 2) if /^# Usage/../^$/' < $0; exit 1; }
|
||||
[ -z "$1" ] && usage
|
||||
[ "$1" = "-h" ] && usage
|
||||
[ -z "$GL_USER" ] && die GL_USER not set
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
repo=$1; shift
|
||||
repo=${repo%.git}
|
||||
gitolite access -q "$repo" $GL_USER W any || die You are not authorised
|
||||
|
||||
# change head
|
||||
cd $GL_REPO_BASE/$repo.git
|
||||
|
||||
git symbolic-ref "$@"
|
|
@ -1,57 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Easy;
|
||||
|
||||
=for usage
|
||||
Usage: ssh git@host who-pushed <repo> <SHA>
|
||||
|
||||
Determine who pushed the given commit. The first few hex digits of the SHA
|
||||
should suffice.
|
||||
|
||||
Each line of the output contains the following fields: timestamp, a
|
||||
transaction ID, username, refname, and the old and new SHAs for the ref.
|
||||
|
||||
We assume the logfile names have been left as default, or if changed, in such
|
||||
a way that they come up oldest first when sorted.
|
||||
|
||||
The program searches ALL the log files, in reverse sorted order (i.e., newest
|
||||
first). This means it could take a long time if your log directory is large
|
||||
and contains lots of old log files. Patches to limit the search to an
|
||||
optional date range are welcome.
|
||||
|
||||
Note on the "transaction ID" field: if looking at the log file doesn't help
|
||||
you figure out what its purpose is, please just ignore it.
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or @ARGV < 2 or $ARGV[0] eq '-h';
|
||||
usage() if $ARGV[1] !~ /^[0-9a-f]+$/i;
|
||||
|
||||
my $repo = shift;
|
||||
my $sha = shift; $sha =~ tr/A-F/a-f/;
|
||||
|
||||
$ENV{GL_USER} and ( can_read($repo) or die "no read permissions on '$repo'" );
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
my $repodir = "$ENV{GL_REPO_BASE}/$repo.git";
|
||||
chdir $repodir or die "repo '$repo' missing";
|
||||
(my $logdir = $ENV{GL_LOGFILE}) =~ s(/[^/]+$)();
|
||||
|
||||
for my $logfile ( reverse glob("$logdir/*") ) {
|
||||
@ARGV = ($logfile);
|
||||
for my $line ( reverse grep { m(\tupdate\t($repo|$repodir)\t) } <> ) {
|
||||
chomp($line);
|
||||
my @fields = split /\t/, $line;
|
||||
my ($ts, $pid, $who, $ref, $d_old, $new) = @fields[ 0, 1, 4, 6, 7, 8];
|
||||
|
||||
# d_old is what you display
|
||||
my $old = $d_old;
|
||||
$old = "" if $d_old eq ("0" x 40);
|
||||
$old = "$old.." if $old;
|
||||
|
||||
system("git rev-list $old$new 2>/dev/null | grep ^$sha >/dev/null && echo '$ts $pid $who $ref $d_old $new'");
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Easy;
|
||||
|
||||
=for usage
|
||||
Usage: gitolite writable <reponame>|@all on|off
|
||||
|
||||
Disable/re-enable pushes to all repos or named repo. Useful to run
|
||||
non-git-aware backups and so on.
|
||||
|
||||
'on' enables, 'off' disables, writes (pushes) to the named repo or all repos.
|
||||
|
||||
With 'off', any subsequent text is taken to be the message to be shown to
|
||||
users when their pushes get rejected. If it is not supplied, it will take it
|
||||
from STDIN; this allows longer messages.
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or @ARGV < 2 or $ARGV[0] eq '-h';
|
||||
usage() if $ARGV[1] ne 'on' and $ARGV[1] ne 'off';
|
||||
|
||||
my $repo = shift;
|
||||
my $on = ( shift eq 'on' );
|
||||
|
||||
if ( $repo eq '@all' ) {
|
||||
_die "you are not authorized" if $ENV{GL_USER} and not is_admin();
|
||||
} else {
|
||||
_die "you are not authorized" if $ENV{GL_USER} and not owns($repo);
|
||||
}
|
||||
|
||||
my $msg = join( " ", @ARGV );
|
||||
# try STDIN only if no msg found in args *and* it's an 'off' command
|
||||
if ( not $msg and not $on ) {
|
||||
say2 "...please type the message to be shown to users:";
|
||||
$msg = join( "", <> );
|
||||
}
|
||||
|
||||
my $sf = ".gitolite.down";
|
||||
my $rb = $ENV{GL_REPO_BASE};
|
||||
|
||||
if ( $repo eq '@all' ) {
|
||||
target( $ENV{HOME} );
|
||||
} else {
|
||||
target("$rb/$repo.git");
|
||||
}
|
||||
|
||||
sub target {
|
||||
my $repodir = shift;
|
||||
if ($on) {
|
||||
unlink "$repodir/$sf";
|
||||
} else {
|
||||
_print( "$repodir/$sf", $msg );
|
||||
}
|
||||
}
|
102
src/gitolite
102
src/gitolite
|
@ -1,102 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# all gitolite CLI tools run as sub-commands of this command
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
=for args
|
||||
Usage: gitolite [sub-command] [options]
|
||||
|
||||
The following built-in subcommands are available; they should all respond to
|
||||
'-h' if you want further details on each:
|
||||
|
||||
setup 1st run: initial setup; all runs: hook fixups
|
||||
compile compile gitolite.conf
|
||||
|
||||
query-rc get values of rc variables
|
||||
|
||||
list-groups list all group names in conf
|
||||
list-users list all users/user groups in conf
|
||||
list-repos list all repos/repo groups in conf
|
||||
list-phy-repos list all repos actually on disk
|
||||
list-memberships list all groups a name is a member of
|
||||
list-members list all members of a group
|
||||
|
||||
Warnings:
|
||||
- list-users is disk bound and could take a while on sites with 1000s of repos
|
||||
- list-memberships does not check if the name is known; unknown names come
|
||||
back with 2 answers: the name itself and '@all'
|
||||
|
||||
In addition, running 'gitolite help' should give you a list of custom commands
|
||||
available. They may or may not respond to '-h', depending on how they were
|
||||
written.
|
||||
=cut
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
use FindBin;
|
||||
|
||||
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; }
|
||||
BEGIN { $ENV{GL_LIBDIR} = "$ENV{GL_BINDIR}/lib"; }
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
my ( $command, @args ) = @ARGV;
|
||||
gl_log( 'cli', 'gitolite', @ARGV ) if -d $rc{GL_ADMIN_BASE} and $$ == ( $ENV{GL_TID} || 0 );
|
||||
args();
|
||||
|
||||
# the first two commands need options via @ARGV, as they have their own
|
||||
# GetOptions calls and older perls don't have 'GetOptionsFromArray'
|
||||
|
||||
if ( $command eq 'setup' ) {
|
||||
shift @ARGV;
|
||||
require Gitolite::Setup;
|
||||
Gitolite::Setup->import;
|
||||
setup();
|
||||
|
||||
} elsif ( $command eq 'query-rc' ) {
|
||||
shift @ARGV;
|
||||
query_rc(); # doesn't return
|
||||
|
||||
# the rest don't need @ARGV per se
|
||||
|
||||
} elsif ( $command eq 'compile' ) {
|
||||
require Gitolite::Conf;
|
||||
Gitolite::Conf->import;
|
||||
compile(@args);
|
||||
|
||||
} elsif ( $command eq 'trigger' ) {
|
||||
trigger(@args);
|
||||
|
||||
} elsif ( my $c = _which("commands/$command", 'x' ) ) {
|
||||
trace( 2, "attempting gitolite command $c" );
|
||||
_system( $c, @args );
|
||||
exit 0;
|
||||
|
||||
} elsif ( $command eq 'list-phy-repos' ) {
|
||||
_chdir( $rc{GL_REPO_BASE} );
|
||||
print "$_\n" for ( @{ list_phy_repos(@args) } );
|
||||
|
||||
} elsif ( $command =~ /^list-/ ) {
|
||||
trace( 2, "attempting lister command $command" );
|
||||
require Gitolite::Conf::Load;
|
||||
Gitolite::Conf::Load->import;
|
||||
my $fn = lister_dispatch($command);
|
||||
print "$_\n" for ( @{ $fn->(@args) } );
|
||||
|
||||
} else {
|
||||
_die "unknown gitolite sub-command";
|
||||
}
|
||||
|
||||
gl_log('END') if $$ == $ENV{GL_TID};
|
||||
|
||||
sub args {
|
||||
usage() if not $command or $command eq '-h';
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
|
@ -1,239 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# gitolite shell, invoked from ~/.ssh/authorized_keys
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
use FindBin;
|
||||
|
||||
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; }
|
||||
BEGIN { $ENV{GL_LIBDIR} = "$ENV{GL_BINDIR}/lib"; }
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
|
||||
# set HOME
|
||||
BEGIN { $ENV{HOME} = $ENV{GITOLITE_HTTP_HOME} if $ENV{GITOLITE_HTTP_HOME}; }
|
||||
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# the main() sub expects ssh-ish things; set them up...
|
||||
my $id = '';
|
||||
if ( exists $ENV{G3T_USER} ) {
|
||||
$id = in_file(); # file:// masquerading as ssh:// for easy testing
|
||||
} elsif ( exists $ENV{SSH_CONNECTION} ) {
|
||||
$id = in_ssh();
|
||||
} elsif ( exists $ENV{REQUEST_URI} ) {
|
||||
$id = in_http();
|
||||
} else {
|
||||
_die "who the *heck* are you?";
|
||||
}
|
||||
|
||||
# sanity...
|
||||
my $soc = $ENV{SSH_ORIGINAL_COMMAND};
|
||||
$soc =~ s/[\n\r]+/<<newline>>/g;
|
||||
_die "I don't like newlines in the command: '$soc'\n" if $ENV{SSH_ORIGINAL_COMMAND} ne $soc;
|
||||
|
||||
# the INPUT trigger massages @ARGV and $ENV{SSH_ORIGINAL_COMMAND} as needed
|
||||
trigger('INPUT');
|
||||
|
||||
main($id);
|
||||
|
||||
gl_log('END') if $$ == $ENV{GL_TID};
|
||||
|
||||
exit 0;
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub in_file {
|
||||
gl_log( 'file', "ARGV=" . join( ",", @ARGV ), "SOC=$ENV{SSH_ORIGINAL_COMMAND}" );
|
||||
|
||||
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /git-\w+-pack/ ) {
|
||||
print STDERR "TRACE: gsh(", join( ")(", @ARGV ), ")\n";
|
||||
print STDERR "TRACE: gsh(SOC=$ENV{SSH_ORIGINAL_COMMAND})\n";
|
||||
}
|
||||
return 'file';
|
||||
}
|
||||
|
||||
sub in_http {
|
||||
http_setup_die_handler();
|
||||
|
||||
_die "GITOLITE_HTTP_HOME not set" unless $ENV{GITOLITE_HTTP_HOME};
|
||||
|
||||
_die "fallback to DAV not supported" if $ENV{REQUEST_METHOD} eq 'PROPFIND';
|
||||
|
||||
# fake out SSH_ORIGINAL_COMMAND and SSH_CONNECTION when called via http,
|
||||
# so the rest of the code stays the same (except the exec at the end).
|
||||
http_simulate_ssh_connection();
|
||||
|
||||
$ENV{REMOTE_USER} ||= $rc{HTTP_ANON_USER};
|
||||
@ARGV = ( $ENV{REMOTE_USER} );
|
||||
|
||||
return 'http';
|
||||
}
|
||||
|
||||
sub in_ssh {
|
||||
my $ip;
|
||||
( $ip = $ENV{SSH_CONNECTION} || '(no-IP)' ) =~ s/ .*//;
|
||||
|
||||
gl_log( 'ssh', "ARGV=" . join( ",", @ARGV ), "SOC=" . ( $ENV{SSH_ORIGINAL_COMMAND} || '' ), "FROM=$ip" );
|
||||
|
||||
$ENV{SSH_ORIGINAL_COMMAND} ||= '';
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# call this once you are sure arg-1 is the username and SSH_ORIGINAL_COMMAND
|
||||
# has been setup (even if it's not actually coming via ssh).
|
||||
sub main {
|
||||
my $id = shift;
|
||||
|
||||
umask $rc{UMASK};
|
||||
|
||||
# set up the user
|
||||
my $user = $ENV{GL_USER} = shift @ARGV;
|
||||
|
||||
# set up the repo and the attempted access
|
||||
my ( $verb, $repo ) = parse_soc(); # returns only for git commands
|
||||
sanity($repo);
|
||||
$ENV{GL_REPO} = $repo;
|
||||
my $aa = ( $verb =~ 'upload' ? 'R' : 'W' );
|
||||
|
||||
# auto-create?
|
||||
if ( repo_missing($repo) and access( $repo, $user, '^C', 'any' ) !~ /DENIED/ ) {
|
||||
require Gitolite::Conf::Store;
|
||||
Gitolite::Conf::Store->import;
|
||||
new_wild_repo( $repo, $user, $aa );
|
||||
gl_log( 'create', $repo, $user, $aa );
|
||||
}
|
||||
|
||||
# a ref of 'any' signifies that this is a pre-git check, where we don't
|
||||
# yet know the ref that will be eventually pushed (and even that won't
|
||||
# apply if it's a read operation). See the matching code in access() for
|
||||
# more information.
|
||||
unless ( $ENV{GL_BYPASS_ACCESS_CHECKS} ) {
|
||||
my $ret = access( $repo, $user, $aa, 'any' );
|
||||
trace( 1, "access($repo, $user, $aa, 'any')", "-> $ret" );
|
||||
trigger( 'ACCESS_1', $repo, $user, $aa, 'any', $ret );
|
||||
_die $ret . "\n(or you mis-spelled the reponame)" if $ret =~ /DENIED/;
|
||||
|
||||
gl_log( "pre_git", $repo, $user, $aa, 'any', "-> $ret" );
|
||||
}
|
||||
|
||||
trigger( 'PRE_GIT', $repo, $user, $aa, 'any', $verb );
|
||||
if ($ENV{REQUEST_URI}) {
|
||||
_system( "git", "http-backend" );
|
||||
} else {
|
||||
my $repodir = "'$rc{GL_REPO_BASE}/$repo.git'";
|
||||
_system( "git", "shell", "-c", "$verb $repodir" );
|
||||
}
|
||||
trigger( 'POST_GIT', $repo, $user, $aa, 'any', $verb );
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub parse_soc {
|
||||
my $soc = $ENV{SSH_ORIGINAL_COMMAND};
|
||||
$soc ||= 'info';
|
||||
|
||||
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
|
||||
if ( $soc =~ m(^($git_commands) '/?(.*?)(?:\.git(\d)?)?'$) ) {
|
||||
my ( $verb, $repo, $trace_level ) = ( $1, $2, $3 );
|
||||
$ENV{D} = $trace_level if $trace_level;
|
||||
_die "invalid repo name: '$repo'" if $repo !~ $REPONAME_PATT;
|
||||
trace( 2, "git command", $soc );
|
||||
return ( $verb, $repo );
|
||||
}
|
||||
|
||||
# after this we should not return; caller expects us to handle it all here
|
||||
# and exit out
|
||||
|
||||
_die "suspicious characters loitering about '$soc'" if $soc !~ $REMOTE_COMMAND_PATT;
|
||||
|
||||
my @words = split ' ', $soc;
|
||||
if ( $rc{COMMANDS}{ $words[0] } ) {
|
||||
trace( 2, "gitolite command", $soc );
|
||||
_system( "gitolite", @words );
|
||||
exit 0;
|
||||
}
|
||||
|
||||
_die "unknown git/gitolite command: '$soc'";
|
||||
}
|
||||
|
||||
sub sanity {
|
||||
my $repo = shift;
|
||||
_die "'$repo' contains bad characters" if $repo !~ $REPONAME_PATT;
|
||||
_die "'$repo' ends with a '/'" if $repo =~ m(/$);
|
||||
_die "'$repo' contains '..'" if $repo =~ m(\.\.);
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# helper functions for "in_http"
|
||||
|
||||
sub http_setup_die_handler {
|
||||
|
||||
$SIG{__DIE__} = sub {
|
||||
my $service = ( $ENV{SSH_ORIGINAL_COMMAND} =~ /git-receive-pack/ ? 'git-receive-pack' : 'git-upload-pack' );
|
||||
my $message = shift; chomp($message);
|
||||
print STDERR "$message\n";
|
||||
|
||||
# format the service response, then the message. With initial
|
||||
# help from Ilari and then a more detailed email from Shawn...
|
||||
$service = "# service=$service\n"; $message = "ERR $message\n";
|
||||
$service = sprintf( "%04X", length($service) + 4 ) . "$service"; # no CRLF on this one
|
||||
$message = sprintf( "%04X", length($message) + 4 ) . "$message";
|
||||
|
||||
http_print_headers();
|
||||
print $service;
|
||||
print "0000"; # flush-pkt, apparently
|
||||
print $message;
|
||||
print STDERR $service;
|
||||
print STDERR $message;
|
||||
exit 0; # if it's ok for die_webcgi in git.git/http-backend.c, it's ok for me ;-)
|
||||
}
|
||||
}
|
||||
|
||||
sub http_simulate_ssh_connection {
|
||||
# these patterns indicate normal git usage; see "services[]" in
|
||||
# http-backend.c for how I got that. Also note that "info" is overloaded;
|
||||
# git uses "info/refs...", while gitolite uses "info" or "info?...". So
|
||||
# there's a "/" after info in the list below
|
||||
if ( $ENV{PATH_INFO} =~ m(^/(.*)/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$)) ) {
|
||||
my $repo = $1;
|
||||
my $verb = ( $ENV{REQUEST_URI} =~ /git-receive-pack/ ) ? 'git-receive-pack' : 'git-upload-pack';
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'";
|
||||
} else {
|
||||
# this is one of our custom commands; could be anything really,
|
||||
# because of the adc feature
|
||||
my ($verb) = ( $ENV{PATH_INFO} =~ m(^/(\S+)) );
|
||||
my $args = $ENV{QUERY_STRING};
|
||||
$args =~ s/\+/ /g;
|
||||
$args =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = $verb;
|
||||
$ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args;
|
||||
http_print_headers(); # in preparation for the eventual output!
|
||||
}
|
||||
$ENV{SSH_CONNECTION} = "$ENV{REMOTE_ADDR} $ENV{REMOTE_PORT} $ENV{SERVER_ADDR} $ENV{SERVER_PORT}";
|
||||
}
|
||||
|
||||
my $http_headers_printed = 0;
|
||||
|
||||
sub http_print_headers {
|
||||
my ( $code, $text ) = @_;
|
||||
|
||||
return if $http_headers_printed++;
|
||||
$code ||= 200;
|
||||
$text ||= "OK - gitolite";
|
||||
|
||||
$|++;
|
||||
print "Status: $code $text\r\n";
|
||||
print "Expires: Fri, 01 Jan 1980 00:00:00 GMT\r\n";
|
||||
print "Pragma: no-cache\r\n";
|
||||
print "Cache-Control: no-cache, max-age=0, must-revalidate\r\n";
|
||||
print "Content-Type: text/plain\r\n";
|
||||
print "\r\n";
|
||||
}
|
1092
src/gitolite.pm
Normal file
1092
src/gitolite.pm
Normal file
File diff suppressed because it is too large
Load diff
262
src/gl-auth-command
Executable file
262
src/gl-auth-command
Executable file
|
@ -0,0 +1,262 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# you: what's the invocation?
|
||||
# me: Hail, O Lord Ganesha, destroyer of obsta...
|
||||
# you: err hmm not *that* sort of invocation... I meant how does this program
|
||||
# get invoked?
|
||||
# me: oh hehe <hides sheepish grin>, ok here we go...
|
||||
#
|
||||
# ssh mode
|
||||
# - started by sshd
|
||||
# - one argument, the "user" name
|
||||
# - one env var, SSH_ORIGINAL_COMMAND, containing the command
|
||||
# - command typically: git-(receive|upload)-pack 'reponame(.git)?'
|
||||
# - special gitolite commands: info, expand, (get|set)(perms|desc)
|
||||
# - special non-gitolite commands: rsync, svnserve, htpasswd
|
||||
# - other commands: anything in $GL_ADC_PATH if defined (see rc file)
|
||||
#
|
||||
# (smart) http mode
|
||||
# - started by apache (httpd)
|
||||
# - no arguments
|
||||
# - REQUEST_URI contains verb and repo, REMOTE_USER contains username
|
||||
# - REQUEST_URI looks like /path/reponame.git/(info/refs\?service=)?git-(receive|upload)-pack
|
||||
# - no special processing commands currently handled
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# common definitions
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# these are set by the "rc" file
|
||||
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE, $GL_PERFLOGT);
|
||||
# and these are set by gitolite.pm
|
||||
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT, $ADC_CMD_ARGS_PATT);
|
||||
our %repos;
|
||||
our %groups;
|
||||
our %repo_config;
|
||||
|
||||
# the common setup module is in the same directory as this running program is
|
||||
my $bindir = $0;
|
||||
$bindir =~ s/\/[^\/]+$//;
|
||||
$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//;
|
||||
require "$bindir/gitolite.pm";
|
||||
|
||||
# ask where the rc file is, get it, and "do" it
|
||||
&where_is_rc();
|
||||
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
|
||||
|
||||
# we need to pass GL_ADMINDIR and the bindir to the child hooks
|
||||
$ENV{GL_ADMINDIR} = $GL_ADMINDIR;
|
||||
$ENV{GL_BINDIR} = $bindir;
|
||||
|
||||
# add a custom path for git binaries, if specified
|
||||
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
|
||||
|
||||
# set default permission of wildcard repositories
|
||||
$ENV{GL_WILDREPOS_DEFPERMS} = $GL_WILDREPOS_DEFPERMS if $GL_WILDREPOS_DEFPERMS;
|
||||
|
||||
# set the umask before creating any files
|
||||
umask($REPO_UMASK);
|
||||
|
||||
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# start...
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# if the first argument is a "-s", this user is allowed to get a shell using
|
||||
# this key
|
||||
my $shell_allowed = 0;
|
||||
if (@ARGV and $ARGV[0] eq '-s') {
|
||||
$shell_allowed = 1;
|
||||
shift;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# set up SSH_ORIGINAL_COMMAND and SSH_CONNECTION in http mode
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# fake out SSH_ORIGINAL_COMMAND and SSH_CONNECTION so the rest of the code
|
||||
# stays the same (except the exec at the end).
|
||||
|
||||
my $user;
|
||||
if ($ENV{REQUEST_URI}) {
|
||||
die "fallback to DAV not supported\n" if $ENV{REQUEST_METHOD} eq 'PROPFIND';
|
||||
|
||||
# these patterns indicate normal git usage; see "services[]" in
|
||||
# http-backend.c for how I got that. Also note that "info" is overloaded;
|
||||
# git uses "info/refs...", while gitolite uses "info" or "info?...". So
|
||||
# there's a "/" after info in the list below
|
||||
if ($ENV{PATH_INFO} =~ m(^/(.*)/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) {
|
||||
my $repo = $1;
|
||||
my $verb = ($ENV{REQUEST_URI} =~ /git-receive-pack/) ? 'git-receive-pack' : 'git-upload-pack';
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'";
|
||||
} else {
|
||||
# this is one of our custom commands; could be anything really,
|
||||
# because of the adc feature
|
||||
my ($verb) = ($ENV{PATH_INFO} =~ m(^/(\S+)));
|
||||
my $args = $ENV{QUERY_STRING};
|
||||
$args =~ s/\+/ /g;
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = $verb;
|
||||
$ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args;
|
||||
&print_http_headers(); # in preparation for the eventual output!
|
||||
}
|
||||
$ENV{SSH_CONNECTION} = "$ENV{REMOTE_ADDR} $ENV{REMOTE_PORT} $ENV{SERVER_ADDR} $ENV{SERVER_PORT}";
|
||||
$user = $ENV{GL_USER} = $ENV{REMOTE_USER};
|
||||
} else {
|
||||
# no (more) arguments given in ssh mode? default user is $USER
|
||||
# (fedorahosted works like this, and it is harmless for others)
|
||||
@ARGV = ($ENV{USER}) unless @ARGV;
|
||||
$user=$ENV{GL_USER}=shift;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# logging, timestamp env vars
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
$ENV{GL_LOG} = &get_logfilename($GL_LOGT);
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# sanity checks on SSH_ORIGINAL_COMMAND
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# no SSH_ORIGINAL_COMMAND given...
|
||||
unless ($ENV{SSH_ORIGINAL_COMMAND}) {
|
||||
# if the user is allowed to use a shell, give him one
|
||||
if ($shell_allowed) {
|
||||
my $shell = $ENV{SHELL};
|
||||
$shell =~ s/.*\//-/; # change "/bin/bash" to "-bash"
|
||||
&log_it($shell);
|
||||
exec { $ENV{SHELL} } $shell;
|
||||
}
|
||||
# otherwise, pretend he typed in "info" and carry on...
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = 'info';
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# slave mode should not do much
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
die "server is in slave mode; you can only fetch\n"
|
||||
if ($GL_SLAVE_MODE and $ENV{SSH_ORIGINAL_COMMAND} !~ /^(info|expand|get|git-upload-)/);
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# admin defined commands
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# please see doc/admin-defined-commands.mkd for details
|
||||
if ($GL_ADC_PATH and -d $GL_ADC_PATH) {
|
||||
my ($cmd, @args) = split ' ', $ENV{SSH_ORIGINAL_COMMAND};
|
||||
if (-x "$GL_ADC_PATH/$cmd") {
|
||||
# yes this is rather strict, sorry.
|
||||
do { die "I don't like $_\n" unless $_ =~ $ADC_CMD_ARGS_PATT } for ($cmd, @args);
|
||||
&log_it("$GL_ADC_PATH/$ENV{SSH_ORIGINAL_COMMAND}");
|
||||
exec("$GL_ADC_PATH/$cmd", @args);
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# get and set perms for actual repo created by wildcard-autoviv
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\b/;
|
||||
|
||||
# note that all the subs called here chdir somewhere else and do not come
|
||||
# back; they all blithely take advantage of the fact that processing custom
|
||||
# commands is sort of a dead end for normal (git) processing
|
||||
|
||||
if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) {
|
||||
die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS;
|
||||
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
||||
my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+'?\/?(.*?)(?:\.git)?'?)?$/);
|
||||
# deal with "no argument" cases
|
||||
$verb eq 'expand' ? $repo = '^' : die "$verb needs an argument\n" unless $repo;
|
||||
if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) {
|
||||
# with an actual reponame, you can "getperms" or "setperms"
|
||||
get_set_perms($repo, $verb, $user);
|
||||
}
|
||||
elsif ($repo =~ $REPONAME_PATT and $verb =~ /(get|set)desc/) {
|
||||
# with an actual reponame, you can "getdesc" or "setdesc"
|
||||
get_set_desc($repo, $verb, $user);
|
||||
}
|
||||
elsif ($verb eq 'expand') {
|
||||
# with a wildcard, you can "expand" it to see what repos actually match
|
||||
die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT;
|
||||
expand_wild($GL_ADMINDIR, $GL_CONF_COMPILED, $repo, $user);
|
||||
} else {
|
||||
die "$cmd doesn't make sense to me\n";
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# non-git commands
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# if the command does NOT fit the pattern of a normal git command, send it off
|
||||
# somewhere else...
|
||||
|
||||
# side notes on detecting a normal git command: the pattern we check allows
|
||||
# old style as well as new style ("git-subcommand arg" or "git subcommand
|
||||
# arg"). Currently, this is how git sends across the command (including the
|
||||
# single quotes):
|
||||
# git-receive-pack 'reponame.git'
|
||||
|
||||
my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
|
||||
unless ( $verb and ( $verb eq 'git-init' or $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) {
|
||||
# ok, it's not a normal git command; call the special command helper
|
||||
&special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE, $SVNSERVE);
|
||||
exit;
|
||||
}
|
||||
die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/;
|
||||
die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./;
|
||||
|
||||
# reponame
|
||||
$ENV{GL_REPO}=$repo;
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# the real git commands (git-receive-pack, etc...)
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# first level permissions check
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
my ($perm, $creator, $wild) = &repo_rights($repo);
|
||||
if ($perm =~ /C/) {
|
||||
# it was missing, and you have create perms
|
||||
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
|
||||
new_repo($repo, "$GL_ADMINDIR/hooks/common", $user);
|
||||
# note pwd is now the bare "repo.git"; new_repo does that...
|
||||
wrap_print("gl-perms", "$GL_WILDREPOS_DEFPERMS\n") if $GL_WILDREPOS_DEFPERMS;
|
||||
&setup_repo_configs($repo, \%repo_config);
|
||||
&setup_daemon_access($repo);
|
||||
&add_del_line ("$repo.git", $PROJECTS_LIST, &setup_gitweb_access($repo, '', ''));
|
||||
wrap_chdir($ENV{HOME});
|
||||
}
|
||||
|
||||
# we know the user and repo; we just need to know what perm he's trying
|
||||
# aa == attempted access
|
||||
my $aa = ($verb =~ $R_COMMANDS ? 'R' : 'W');
|
||||
die "$aa access for $repo DENIED to $user
|
||||
(Or there may be no repository at the given path. Did you spell it correctly?)\n" unless $perm =~ /$aa/;
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# over to git now
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if ($ENV{REQUEST_URI}) {
|
||||
&log_it($ENV{REQUEST_URI});
|
||||
exec $ENV{GIT_HTTP_BACKEND};
|
||||
# the GIT_HTTP_BACKEND env var should be set either by the rc file, or as
|
||||
# a SetEnv in the apache config somewhere
|
||||
}
|
||||
|
||||
&log_it();
|
||||
|
||||
$repo = "'$REPO_BASE/$repo.git'";
|
||||
exec("git", "shell", "-c", "$verb $repo") unless $verb eq 'git-init';
|
578
src/gl-compile-conf
Executable file
578
src/gl-compile-conf
Executable file
|
@ -0,0 +1,578 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Data::Dumper;
|
||||
$Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
|
||||
# === add-auth-keys ===
|
||||
|
||||
# part of the gitolite (GL) suite
|
||||
|
||||
# (1) - "compiles" ~/.ssh/authorized_keys from the list of pub-keys
|
||||
# (2) - also "compiles" the user-friendly GL conf file into something easier
|
||||
# to parse. We're doing this because both the gl-auth-command and the
|
||||
# (gl-)update hook need this, and it seems easier to do this than
|
||||
# replicate the parsing code in both those places. As a bonus, it's
|
||||
# probably more efficient.
|
||||
# (3) - finally does what I have resisted doing all along -- handle gitweb and
|
||||
# git-daemon access. It won't *setup* gitweb/daemon for you -- you have
|
||||
# to that yourself. What this does is make sure that "repo.git"
|
||||
# contains the file "git-daemon-export-ok" (for daemon case) and the
|
||||
# line "repo.git" exists in the "projects.list" file (for gitweb case).
|
||||
|
||||
# how run: manual, by GL admin
|
||||
# when:
|
||||
# - anytime a pubkey is added/deleted
|
||||
# - anytime gitolite.conf is changed
|
||||
# input:
|
||||
# - GL_CONF (default: ~/.gitolite/conf/gitolite.conf)
|
||||
# - GL_KEYDIR (default: ~/.gitolite/keydir)
|
||||
# output:
|
||||
# - ~/.ssh/authorized_keys (dictated by sshd)
|
||||
# - GL_CONF_COMPILED (default: ~/.gitolite/conf/gitolite.conf-compiled.pm)
|
||||
# security:
|
||||
# - touches a very critical system file that manages the restrictions on
|
||||
# incoming users. Be sure to audit AUTH_COMMAND and AUTH_OPTIONS (see
|
||||
# below) on any change to this script
|
||||
# - no security checks within program. The GL admin runs this manually
|
||||
|
||||
# warnings:
|
||||
# - if the "start" line exists, but the "end" line does not, you lose the
|
||||
# rest of the existing authkey file. In general, "don't do that (TM)",
|
||||
# but we do have a "vim -d" popping up so you can see the changes being
|
||||
# made, just in case...
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# common definitions
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# setup quiet mode if asked; please do not use this when running manually
|
||||
open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q');
|
||||
|
||||
# these are set by the "rc" file
|
||||
our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_GITCONFIG_WILD, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS, $GL_PERFLOGT);
|
||||
# and these are set by gitolite.pm
|
||||
our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $ABRT, $WARN);
|
||||
|
||||
# the common setup module is in the same directory as this running program is
|
||||
my $bindir = $0;
|
||||
$bindir =~ s/\/[^\/]+$//;
|
||||
$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//;
|
||||
require "$bindir/gitolite.pm";
|
||||
|
||||
# ask where the rc file is, get it, and "do" it
|
||||
&where_is_rc();
|
||||
die "$ABRT parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
|
||||
|
||||
# add a custom path for git binaries, if specified
|
||||
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# definitions specific to this program
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# groups can now represent user groups or repo groups.
|
||||
|
||||
# $groups{group}{member} = "master" (or name of fragment file in which the
|
||||
# group is defined).
|
||||
our %groups = ();
|
||||
|
||||
# %repos has two functions.
|
||||
|
||||
# $repos{repo}{R|W}{user} = 1 if user has R (or W) permissions for at least
|
||||
# one branch in repo. This is used by the "level 1 check" (see faq). There's
|
||||
# also the new "C" (create a repo) permission now
|
||||
|
||||
# $repos{repo}{user} is a list of {ref, perms} pairs. This is used by the
|
||||
# level 2 check. In order to allow "exclude" rules, the order of rules now
|
||||
# matters, so what used to be entirely "hash of hash of hash" now has a list
|
||||
# in between :)
|
||||
my %repos = ();
|
||||
|
||||
# rule sequence number
|
||||
my $rule_seq = 0;
|
||||
|
||||
# <sigh>... having been forced to use a list as described above, we lose some
|
||||
# efficiency due to the possibility of the same {ref, perms} pair showing up
|
||||
# multiple times for the same repo+user. So...
|
||||
my %rurp_seen = ();
|
||||
|
||||
our $current_data_version; # this comes from gitolite.pm
|
||||
|
||||
# catch usernames<->pubkeys mismatches; search for "lint" below
|
||||
my %user_list = ();
|
||||
|
||||
# repo configurations
|
||||
our %repo_config = ();
|
||||
|
||||
# gitweb descriptions and owners; plain text, keyed by "$repo.git"
|
||||
my %desc = ();
|
||||
my %owner = ();
|
||||
|
||||
# set the umask before creating any files
|
||||
umask($REPO_UMASK);
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# subroutines
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
sub expand_list
|
||||
{
|
||||
my @list = @_;
|
||||
my @new_list = ();
|
||||
|
||||
for my $item (@list)
|
||||
{
|
||||
if ($item =~ /^@/) # nested group
|
||||
{
|
||||
die "$ABRT undefined group $item\n" unless $groups{$item};
|
||||
# add those names to the list
|
||||
push @new_list, sort keys %{ $groups{$item} };
|
||||
}
|
||||
else
|
||||
{
|
||||
push @new_list, $item;
|
||||
}
|
||||
}
|
||||
|
||||
return @new_list;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# "compile" GL conf
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
sub check_fragment_repo_disallowed
|
||||
{
|
||||
# trying to set access for $repo (='foo')...
|
||||
my ($fragment, $repo) = @_;
|
||||
|
||||
# processing the master config, not a fragment
|
||||
return 0 if $fragment eq 'master';
|
||||
# fragment is also called 'foo' (you're allowed to have a
|
||||
# fragment that is only concerned with one repo)
|
||||
return 0 if $fragment eq $repo;
|
||||
# same thing in big-config-land; foo is just @foo now
|
||||
return 0 if $GL_BIG_CONFIG and ("\@$fragment" eq $repo);
|
||||
my @matched = grep { $repo =~ /^$_$/ }
|
||||
grep { $groups{"\@$fragment"}{$_} eq 'master' }
|
||||
sort keys %{ $groups{"\@$fragment"} };
|
||||
return 0 if @matched > 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub parse_conf_line
|
||||
{
|
||||
my ($line, $fragment, $repos_p, $ignored_p) = @_;
|
||||
|
||||
# user or repo groups
|
||||
if ($line =~ /^(@\S+) = (.*)/)
|
||||
{
|
||||
die "$ABRT defining groups is not allowed inside fragments\n"
|
||||
if $GL_BIG_CONFIG and $fragment ne 'master';
|
||||
# store the members of each group as hash key. Keep track of when
|
||||
# the group was *first* created by using $fragment as the *value*
|
||||
do { $groups{$1}{$_} ||= $fragment } for ( expand_list( split(' ', $2) ) );
|
||||
die "$ABRT bad group $1\n" unless $1 =~ $REPONAME_PATT;
|
||||
}
|
||||
# repo(s)
|
||||
elsif ($line =~ /^repo (.*)/)
|
||||
{
|
||||
# grab the list...
|
||||
@{ $repos_p } = split ' ', $1;
|
||||
unless (@{ $repos_p } == 1 and $repos_p->[0] eq '@all') {
|
||||
# ...expand groups in the default case
|
||||
@{ $repos_p } = expand_list ( @{ $repos_p } ) unless $GL_BIG_CONFIG;
|
||||
# ...sanity check
|
||||
for (@{ $repos_p }) {
|
||||
die "$ABRT bad reponame $_\n"
|
||||
if ($GL_WILDREPOS and $_ !~ $REPOPATT_PATT);
|
||||
die "$ABRT bad reponame $_ or you forgot to set \$GL_WILDREPOS\n"
|
||||
if (not $GL_WILDREPOS and $_ !~ $REPONAME_PATT);
|
||||
}
|
||||
}
|
||||
s/\bCREAT[EO]R\b/\$creator/g for @{ $repos_p };
|
||||
}
|
||||
# actual permission line
|
||||
elsif ($line =~ /^(-|C|R|RW\+?(?:C?D?|D?C?)) (.* )?= (.+)/)
|
||||
{
|
||||
my $perms = $1;
|
||||
my @refs; @refs = split( ' ', $2 ) if $2;
|
||||
@refs = expand_list ( @refs );
|
||||
my @users = split ' ', $3;
|
||||
die "$ABRT \$GL_WILDREPOS is not set, you cant use 'C' in config\n" if $perms eq 'C' and not $GL_WILDREPOS;
|
||||
|
||||
# if no ref is given, this PERM applies to all refs
|
||||
@refs = qw(refs/.*) unless @refs;
|
||||
# deprecation warning
|
||||
map { warn "WARNING: old syntax 'PATH/' found; please use new syntax 'NAME/'\n" if s(^PATH/)(NAME/) } @refs;
|
||||
# fully qualify refs that dont start with "refs/" or "NAME/";
|
||||
# prefix them with "refs/heads/"
|
||||
@refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs;
|
||||
@refs = map { s(/USER/)(/\$gl_user/); $_ } @refs;
|
||||
|
||||
# expand the user list, unless it is just "@all"
|
||||
@users = expand_list ( @users )
|
||||
unless ($GL_BIG_CONFIG or (@users == 1 and $users[0] eq '@all'));
|
||||
do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users;
|
||||
|
||||
s/\bCREAT[EO]R\b/~\$creator/g for @users;
|
||||
s/\bREADERS\b/\$readers/g for @users;
|
||||
s/\bWRITERS\b/\$writers/g for @users;
|
||||
|
||||
# ok, we can finally populate the %repos hash
|
||||
for my $repo (@{ $repos_p }) # each repo in the current stanza
|
||||
{
|
||||
# if we're processing a delegated config file (not the master
|
||||
# config), we need to prevent attempts by that admin to obtain
|
||||
# rights on stuff outside his domain
|
||||
|
||||
# trying to set access for $repo (='foo')...
|
||||
if (check_fragment_repo_disallowed( $fragment, $repo ))
|
||||
{
|
||||
$ignored_p->{$fragment}{$repo} = 1;
|
||||
next;
|
||||
}
|
||||
for my $user (@users)
|
||||
{
|
||||
# lint check, to catch pubkey/username typos
|
||||
if ($user =~ /^@/ and $user ne '@all') {
|
||||
# this is a usergroup, not a normal user; happens with GL_BIG_CONFIG
|
||||
if (exists $groups{$user}) {
|
||||
$user_list{$_}++ for keys %{ $groups{$user} };
|
||||
}
|
||||
} else {
|
||||
$user_list{$user}++;
|
||||
}
|
||||
|
||||
# for 1st level check (see faq/tips doc)
|
||||
$repos{$repo}{C}{$user} = 1, next if $perms eq 'C';
|
||||
$repos{$repo}{R}{$user} = 1 if $perms =~ /R/;
|
||||
$repos{$repo}{W}{$user} = 1 if $perms =~ /W|D/;
|
||||
|
||||
# if the user specified even a single 'D' anywhere, make
|
||||
# that fact easy to find; this changes the meaning of RW+
|
||||
# to no longer permit deletes (see update hook)
|
||||
$repos{$repo}{DELETE_IS_D} = 1 if $perms =~ /D/;
|
||||
$repos{$repo}{CREATE_IS_C} = 1 if $perms =~ /RW.*C/;
|
||||
|
||||
# for 2nd level check, store each "ref, perms" pair in order
|
||||
for my $ref (@refs)
|
||||
{
|
||||
# checking NAME based restrictions is expensive for
|
||||
# the update hook (see the changes to src/hooks/update
|
||||
# in this commit for why) so we would *very* much like
|
||||
# to avoid doing it for the large majority of repos
|
||||
# that do *not* use NAME limits. Setting a flag that
|
||||
# can be checked right away will help us do that
|
||||
$repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//;
|
||||
my $p_user = $user; $p_user =~ s/(creator|readers|writers)$/$1 - wild/;
|
||||
push @{ $repos{$repo}{$p_user} }, [ $rule_seq++, $ref, $perms ]
|
||||
unless $rurp_seen{$repo}{$p_user}{$ref}{$perms}++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# configuration
|
||||
elsif ($line =~ /^config (.+) = ?(.*)/)
|
||||
{
|
||||
my ($key, $value) = ($1, $2);
|
||||
my @validkeys = split(' ', ($GL_GITCONFIG_KEYS || '') );
|
||||
my @matched = grep { $key =~ /^$_$/ } @validkeys;
|
||||
die "$ABRT git config $key not allowed\n" if (@matched < 1);
|
||||
for my $repo (@{ $repos_p }) # each repo in the current stanza
|
||||
{
|
||||
$repo_config{$repo}{$key} = $value;
|
||||
# no problem if it's a plain repo (non-pattern, non-groupname)
|
||||
# OR wild configs are allowed
|
||||
unless ( ($repo =~ $REPONAME_PATT and $repo !~ /^@/) or $GL_GITCONFIG_WILD) {
|
||||
my @r = ($repo); # single wildpatt
|
||||
@r = sort keys %{ $groups{$repo} } if $groups{$repo}; # or a group; get its members
|
||||
do {
|
||||
print STDERR "$WARN git config set for $_ but \$GL_GITCONFIG_WILD not set\n" unless $_ =~ $REPONAME_PATT
|
||||
} for @r;
|
||||
}
|
||||
}
|
||||
}
|
||||
# include
|
||||
elsif ($line =~ /^include "(.+)"/)
|
||||
{
|
||||
my $file = $1;
|
||||
$file = "$GL_ADMINDIR/conf/$file" unless $file =~ /^\//;
|
||||
die "$WARN $fragment attempting to include configuration\n" if $fragment ne 'master';
|
||||
die "$ABRT included file not found: '$file'\n" unless -f $file;
|
||||
|
||||
parse_conf_file( $file, $fragment );
|
||||
}
|
||||
# very simple syntax for the gitweb description of repo; one of:
|
||||
# reponame = "some description string"
|
||||
# reponame "owner name" = "some description string"
|
||||
elsif ($line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/)
|
||||
{
|
||||
my ($repo, $owner, $desc) = ($1, $2, $3);
|
||||
die "$ABRT bad repo name $repo\n" unless $repo =~ $REPONAME_PATT;
|
||||
die "$WARN $fragment attempting to set description for $repo\n" if check_fragment_repo_disallowed( $fragment, $repo );
|
||||
$desc{"$repo.git"} = $desc;
|
||||
$owner{"$repo.git"} = $owner || '';
|
||||
}
|
||||
else
|
||||
{
|
||||
die "$ABRT can't make head or tail of '$line'\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub cleanup_conf_line
|
||||
{
|
||||
my ($line) = @_;
|
||||
|
||||
# kill comments, but take care of "#" inside *simple* strings
|
||||
$line =~ s/^((".*?"|[^#"])*)#.*/$1/;
|
||||
# normalise whitespace; keeps later regexes very simple
|
||||
$line =~ s/=/ = /;
|
||||
$line =~ s/\s+/ /g;
|
||||
$line =~ s/^ //;
|
||||
$line =~ s/ $//;
|
||||
return $line;
|
||||
}
|
||||
|
||||
sub parse_conf_file
|
||||
{
|
||||
my ($conffile, $fragment) = @_;
|
||||
# the second arg, $fragment, is passed in as "master" when parsing the
|
||||
# main config, and the fragment name when parsing a fragment. In the
|
||||
# latter case, the parser uses that information to ignore (and warn about)
|
||||
# any repos in the fragment that are not members of the "repo group" of
|
||||
# the same name.
|
||||
my %ignored = ();
|
||||
|
||||
my $conf_fh = wrap_open( "<", $conffile );
|
||||
|
||||
# the syntax is fairly simple, so we parse it inline
|
||||
|
||||
my @repos;
|
||||
my $line;
|
||||
while (<$conf_fh>)
|
||||
{
|
||||
$line = cleanup_conf_line($_);
|
||||
# skip blank lines
|
||||
next unless $line =~ /\S/;
|
||||
|
||||
parse_conf_line( $line, $fragment, \@repos, \%ignored );
|
||||
}
|
||||
for my $ig (sort keys %ignored)
|
||||
{
|
||||
warn "\n\t\t***** WARNING *****\n" .
|
||||
"\t$ig.conf attempting to set access for " .
|
||||
join (", ", sort keys %{ $ignored{$ig} }) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
# parse the main config file
|
||||
parse_conf_file($GL_CONF, 'master');
|
||||
|
||||
# parse any delegated fragments
|
||||
wrap_chdir($GL_ADMINDIR);
|
||||
for my $fragment_file (glob("conf/fragments/*.conf"))
|
||||
{
|
||||
# we already check (elsewhere) that a fragment called "foo" will not try
|
||||
# to specify access control for a repo whose name is not "foo" or is not
|
||||
# part of a group called "foo" created by master
|
||||
|
||||
# meanwhile, I found a possible attack where the admin for group B creates
|
||||
# a "convenience" group of (a subset of) his users, and then the admin for
|
||||
# repo group A (alphabetically before B) adds himself to that same group
|
||||
# in his own fragment.
|
||||
|
||||
# as a result, admin_A now has access to group B repos :(
|
||||
|
||||
# so now we lock the groups hash to the value it had after parsing
|
||||
# "master", and localise any changes to it by this fragment so that they
|
||||
# don't propagate to the next fragment. Thus, each fragment now has only
|
||||
# those groups that are defined in "master" and itself
|
||||
|
||||
local %groups = %groups;
|
||||
|
||||
my $fragment = $fragment_file;
|
||||
$fragment =~ s/^conf\/fragments\/(.*).conf$/$1/;
|
||||
parse_conf_file($fragment_file, $fragment);
|
||||
}
|
||||
|
||||
my $compiled_fh = wrap_open( ">", "$GL_CONF_COMPILED.new" );
|
||||
my $data_version = $current_data_version;
|
||||
print $compiled_fh Data::Dumper->Dump([$data_version], [qw(*data_version)]);
|
||||
my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]);
|
||||
$dumped_data .= Data::Dumper->Dump([\%repo_config], [qw(*repo_config)]) if %repo_config;
|
||||
# the dump uses single quotes, but we convert any strings containing $creator,
|
||||
# $readers, $writers, to double quoted strings. A wee bit sneaky, but not too
|
||||
# much...
|
||||
$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g;
|
||||
print $compiled_fh $dumped_data;
|
||||
if (%groups) {
|
||||
$dumped_data = Data::Dumper->Dump([\%groups], [qw(*groups)]);
|
||||
$dumped_data =~ s/\bCREAT[EO]R\b/\$creator/g;
|
||||
$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g;
|
||||
print $compiled_fh $dumped_data;
|
||||
}
|
||||
close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n";
|
||||
rename "$GL_CONF_COMPILED.new", "$GL_CONF_COMPILED";
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# (that ends the config file compiler and write)
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# what's the git version?
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# we don't like stuff older than 1.6.2
|
||||
|
||||
my $git_version = `git --version`;
|
||||
die "
|
||||
*** ERROR ***
|
||||
did not get a proper version number. Please see if git is in the PATH on
|
||||
the server. If it is not, please edit ~/.gitolite.rc on the server and
|
||||
set the \$GIT_PATH variable to the correct value\n
|
||||
" unless $git_version;
|
||||
my ($gv_maj, $gv_min, $gv_patchrel) = ($git_version =~ m/git version (\d+)\.(\d+)\.(\d+)/);
|
||||
die "$ABRT I can't understand $git_version\n" unless ($gv_maj >= 1);
|
||||
$git_version = $gv_maj*10000 + $gv_min*100 + $gv_patchrel; # now it's "normalised"
|
||||
|
||||
die "\n\t\t***** AAARGH! *****\n" .
|
||||
"\tyour git version is older than 1.6.2\n" .
|
||||
"\tsince that is now more than one year old, and gitolite needs some of\n" .
|
||||
"\tthe newer features, please upgrade.\n"
|
||||
if $git_version < 10602; # that's 1.6.2 to you
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# the rest of this program can be "switched off"; see doc/big-config.mkd for
|
||||
# details.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# any new repos to be created?
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# repo-base needs to be an absolute path for this loop to work right
|
||||
# so if it was not already absolute, prefix $HOME.
|
||||
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
||||
|
||||
unless ($GL_NO_CREATE_REPOS) {
|
||||
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
|
||||
|
||||
# autocreate repos. Start with the ones that are normal repos in %repos
|
||||
my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos;
|
||||
# then, for each repogroup, find the members of the group and add them in
|
||||
map { push @repos, keys %{ $groups{$_} } } grep { /^@/ } keys %repos;
|
||||
# weed out duplicates (the code in the loop below is disk activity!)
|
||||
my %seen = map { $_ => 1 } @repos;
|
||||
@repos = sort keys %seen;
|
||||
|
||||
for my $repo (sort @repos) {
|
||||
next unless $repo =~ $REPONAME_PATT;
|
||||
next if $repo =~ m(^\@|EXTCMD/); # these are not real repos
|
||||
unless (-d "$repo.git") {
|
||||
print STDERR "creating $repo...\n";
|
||||
new_repo($repo, "$GL_ADMINDIR/hooks/common");
|
||||
# new_repo would have chdir'd us away; come back
|
||||
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
|
||||
}
|
||||
|
||||
# when repos are copied over from elsewhere, one had to run easy install
|
||||
# once again to make the new (OS-copied) repo contain the proper update
|
||||
# hook. Perhaps we can make this easier now, and eliminate the easy
|
||||
# install, with a quick check (and a new, empty, "hook" as a sentinel)
|
||||
unless (-l "$repo.git/hooks/gitolite-hooked") {
|
||||
ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo.git/hooks");
|
||||
# in case of package install, GL_ADMINDIR is no longer the top cop;
|
||||
# override with the package hooks
|
||||
ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo.git/hooks") if $GL_PACKAGE_HOOKS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# collect repo_patt for each actual repo
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# go through each actual repo on disk, and match it to either its own name in
|
||||
# the config (non-wild) or a wild pattern that matches it. Lots of things
|
||||
# later will need this correspondence so we may as well snarf it in one shot
|
||||
|
||||
|
||||
my %repo_patts = ();
|
||||
%repo_patts = &collect_repo_patts(\%repos) unless $GL_NO_DAEMON_NO_GITWEB;
|
||||
|
||||
# NOTE: we're overloading GL_NO_DAEMON_NO_GITWEB to mean "no git config" also.
|
||||
# In fact anything that requires trawling through the existing repos doing
|
||||
# stuff to all of them is skipped if this variable is set. This is primarily
|
||||
# for the Fedora folks, but it should be useful for anyone who has a huge set
|
||||
# of repos and wants to manage gitweb/daemon/etc access via other means (they
|
||||
# typically have the whole thing controlled by a web-app and a database
|
||||
# anyway, and gitolite is only doing the access control and nothing more).
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# various updates to all real repos
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# update repo configurations, gitweb description, daemon export-ok, etc
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# all these require a "chdir" to the repo, so we club them for efficiency
|
||||
|
||||
my %projlist = ();
|
||||
|
||||
# for each real repo (and remember this will be empty, thus skipping all this,
|
||||
# if $GL_NO_DAEMON_NO_GITWEB is on!)
|
||||
|
||||
# note: we do them in 2 separate loops to avoid breaking the optimisation in
|
||||
# sub parse_acl (look for variable $saved_crwu)
|
||||
|
||||
for my $repo (keys %repo_patts) {
|
||||
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
|
||||
# daemon is easy
|
||||
&setup_daemon_access($repo);
|
||||
}
|
||||
|
||||
for my $repo (keys %repo_patts) {
|
||||
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
|
||||
# gitweb is a little more complicated. Here're some notes:
|
||||
# - "setup_gitweb_access" also sets "owner", despite the name
|
||||
# - specifying a description also counts as enabling gitweb
|
||||
# - description and owner are not specified for wildrepos; they're
|
||||
# specified for *actual* repos, even if the repo was created by a
|
||||
# wild card spec and "C" permissions. If you see the
|
||||
# conf/example.conf file, you will see that repo owner/desc don't go
|
||||
# into the "repo foo" section; they're essentialy independent.
|
||||
# Anyway, I believe it doesn't make sense to have all wild repos
|
||||
# (for some pattern) to have the same description and owner.
|
||||
$projlist{"$repo.git"} = 1 if &setup_gitweb_access($repo, $desc{"$repo.git"} || '', $owner{"$repo.git"} || '');
|
||||
|
||||
# git config
|
||||
# implementation note: this must happen *after* one of the previous 2
|
||||
# calls (setup daemon or gitweb). The reason is that they call
|
||||
# "can_read", which eventually calls parse_acl with the right "creator"
|
||||
# set for the *current* repo, which in turn stores translated values for
|
||||
# $creator in the repo_config hash, which, (phew!) is needed for a match
|
||||
# that eventually gets you a valid $repo_config{} below
|
||||
&setup_repo_configs($repo, \%repo_config) if $repo_config{$repo};
|
||||
}
|
||||
|
||||
# write out the project list
|
||||
my $projlist_fh = wrap_open( ">", $PROJECTS_LIST);
|
||||
for my $proj (sort keys %projlist) {
|
||||
print $projlist_fh "$proj\n";
|
||||
}
|
||||
close $projlist_fh;
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# "compile" ssh authorized_keys
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
unless ($GL_NO_SETUP_AUTHKEYS) {
|
||||
&setup_authkeys($bindir, $GL_KEYDIR, \%user_list);
|
||||
}
|
101
src/gl-conf-convert
Executable file
101
src/gl-conf-convert
Executable file
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# migrate gitosis.conf to gitolite.conf format
|
||||
|
||||
# not very smart, but there shouldn't be any errors for simple configurations.
|
||||
# the biggest thing you'll find is probably some comments rearranged or
|
||||
# something, due to the "flush" thing below
|
||||
|
||||
# for stuff it can't handle, it'll ignore the trivial ones (like gitweb and
|
||||
# daemon), and put in an obviously syntax error-ed line for "repositories" and
|
||||
# "map" statements.
|
||||
|
||||
my @repos;
|
||||
my @RO_repos;
|
||||
my @comments;
|
||||
my @users;
|
||||
my $groupname;
|
||||
|
||||
# a gitosis.conf stanza ends when a new "[group name]" line shows up, so you
|
||||
# can't write as you go; you have to accumulate and flush
|
||||
sub flush {
|
||||
die "repos but no users?\n" if (not @users and (@repos or @RO_repos));
|
||||
# just a groupname
|
||||
if (@users and not (@repos or @RO_repos)) {
|
||||
print "\@$groupname = ", join(" ", @users), "\n";
|
||||
}
|
||||
# RW repos
|
||||
if (@repos)
|
||||
{
|
||||
print "repo ", join(" ", @repos), "\n";
|
||||
print " RW = ", join(" ", @users), "\n";
|
||||
}
|
||||
# RO repos
|
||||
if (@RO_repos)
|
||||
{
|
||||
print "repo ", join(" ", @RO_repos), "\n";
|
||||
print " R = ", join(" ", @users), "\n";
|
||||
}
|
||||
# comments; yes there'll be some reordering, sorry!
|
||||
print @comments if @comments;
|
||||
|
||||
# empty out for next round
|
||||
@users = ();
|
||||
@repos = ();
|
||||
@RO_repos = ();
|
||||
@comments = ();
|
||||
}
|
||||
|
||||
while (<>)
|
||||
{
|
||||
# pure comment lines or blank lines
|
||||
if (/^\s*#/ or /^\s*$/) {
|
||||
push @comments, $_;
|
||||
next;
|
||||
}
|
||||
|
||||
# not supported
|
||||
if (/^repositories *=/ or /^map /) {
|
||||
print STDERR "not supported: $_";
|
||||
s/^/NOT SUPPORTED: /;
|
||||
print;
|
||||
next;
|
||||
}
|
||||
|
||||
chomp;
|
||||
|
||||
# normalise whitespace to help later regexes
|
||||
s/\s+/ /g;
|
||||
s/ ?= ?/ = /;
|
||||
s/^ //;
|
||||
s/ $//;
|
||||
|
||||
# the chaff...
|
||||
next if /^\[(gitosis|repo)\]$/
|
||||
or /^(gitweb|daemon|loglevel|description|owner) =/;
|
||||
|
||||
# the wheat...
|
||||
if (/^members = (.*)/) {
|
||||
push @users, split(' ', $1);
|
||||
next;
|
||||
}
|
||||
if (/^write?able = (.*)/) {
|
||||
push @repos, split(' ', $1);
|
||||
next;
|
||||
}
|
||||
if (/^readonly = (.*)/) {
|
||||
push @RO_repos, split(' ', $1);
|
||||
next;
|
||||
}
|
||||
|
||||
# new group starts
|
||||
if (/^\[group (.*?) ?\]/) {
|
||||
flush();
|
||||
$groupname = $1;
|
||||
}
|
||||
}
|
||||
|
||||
flush();
|
104
src/gl-dont-panic
Executable file
104
src/gl-dont-panic
Executable file
|
@ -0,0 +1,104 @@
|
|||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
First: DON'T PANIC
|
||||
NOTE: This advice pertains to gitolite specific issues. If you don't
|
||||
have ANY access to the server at all, it is OK to panic.
|
||||
|
||||
Step 1: prepare
|
||||
|
||||
- copy this program to your gitolite server
|
||||
- if you lost your admin key, create a new keypair on your workstation
|
||||
and copy the pub part of this new key also to the server
|
||||
- rename it to whatever your gitolite admin username is, with a .pub
|
||||
extension. (Example, I would call it "sitaram.pub")
|
||||
|
||||
Step 2: use one of the fixes below (on the server)
|
||||
|
||||
- (FIX #1: REWINDING BAD ADMIN COMMITS) if your last commit(s) to the
|
||||
gitolite-admin repo pushed a very bad config and you want to rewind it
|
||||
to a known good state, run this:
|
||||
|
||||
$0 rewind
|
||||
|
||||
(this doesn't actually rewind; it creates a new commit that has
|
||||
the same state as the last good commit, which has the same effect)
|
||||
|
||||
- (FIX #2: PUSHING A NEW ADMIN KEY) if you lost your admin key, or you
|
||||
had used the wrong key initially, then you get yourself a new keypair
|
||||
and run this with the new pubkey:
|
||||
|
||||
$0 sitaram.pub # example using my name
|
||||
|
||||
Please note that this simply *replaces* the key for user "sitaram".
|
||||
It does NOT add a new admin called "sitaram". In fact it does not
|
||||
touch the config file (access rules) at all.
|
||||
|
||||
Step 3: completing the fix (on your workstation)
|
||||
|
||||
- do a 'git pull' on the gitolite admin clone or make a fresh clone
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# arg check
|
||||
die() { echo "$@"; exit 1; }
|
||||
cd $HOME # if he didn't *start* there, it's his bloody fault
|
||||
[ -f "$1" ] || [ "$1" = "rewind" ] || die "need a valid file or 'rewind'"
|
||||
if [ "$1" = "rewind" ]
|
||||
then
|
||||
:
|
||||
else
|
||||
bn1=`basename $1`;
|
||||
admin_name=`basename $1 .pub`;
|
||||
[ "$bn1" = "$admin_name" ] && die "filename needs to end in '.pub'"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# setup stuff
|
||||
REPO_BASE=$( cd $HOME; perl -e 'do ".gitolite.rc"; print $REPO_BASE' )
|
||||
GL_BINDIR=$( cd $HOME; perl -ne 'print($1), exit if /^command="(.*?)\/gl-auth-command /' < $HOME/.ssh/authorized_keys)
|
||||
GL_ADMINDIR=$(cd $HOME; perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR')
|
||||
export REPO_BASE
|
||||
export GL_BINDIR
|
||||
export GL_ADMINDIR
|
||||
|
||||
TEMPDIR=$(mktemp -d)
|
||||
export TEMPDIR
|
||||
trap "/bin/rm -rf $TEMPDIR" 0
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# rewind the admin repo
|
||||
if [ "$1" = "rewind" ]
|
||||
then
|
||||
git clone $REPO_BASE/gitolite-admin.git $TEMPDIR
|
||||
cd $TEMPDIR
|
||||
|
||||
echo printing the last 9 commits to the config; echo
|
||||
git log -9 --date=relative --format="%h %ar%x09%s" | perl -pe 'print "$.\t"'
|
||||
echo; read -p 'please enter how many commits you want to rewind: ' n
|
||||
good=`git rev-parse --short HEAD~$n`
|
||||
|
||||
git checkout -f $good .
|
||||
git commit -m "emergency revert to $good"
|
||||
GL_BYPASS_UPDATE_HOOK=1 git push
|
||||
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# add/overwrite a key ($1)
|
||||
git clone $REPO_BASE/gitolite-admin.git $TEMPDIR
|
||||
cp $1 $TEMPDIR/keydir
|
||||
cd $TEMPDIR
|
||||
git add keydir
|
||||
git commit -m "emergency add/update $admin_name key (from $1)"
|
||||
GL_BYPASS_UPDATE_HOOK=1 git push
|
||||
exit $?
|
648
src/gl-easy-install
Executable file
648
src/gl-easy-install
Executable file
|
@ -0,0 +1,648 @@
|
|||
#!/bin/bash
|
||||
|
||||
# easy install for gitolite
|
||||
|
||||
# you run this on the client side, and it takes care of all the server side
|
||||
# work. You don't have to do anything on the server side directly
|
||||
|
||||
# to do a manual install (since I have tested this only on Linux), open this
|
||||
# script in a nice, syntax coloring, text editor and follow the instructions
|
||||
# prefixed by the word "MANUAL" in the comments below :-)
|
||||
|
||||
# run without any arguments for "usage" info
|
||||
|
||||
# important setting: bail on any errors (else we have to check every single
|
||||
# command!)
|
||||
set -e
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# bootstrap and main
|
||||
# ----------------------------------------------------------------------
|
||||
if [[ $1 != boot/strap ]]
|
||||
then
|
||||
# did someone tell you you can't call functions before they're defined in
|
||||
# bash? Don't believe everything you hear ;-)
|
||||
. $0 boot/strap
|
||||
main "$@"
|
||||
cleanup
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# no direct executable statements after this; only functions
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
main() {
|
||||
basic_sanity "$@"
|
||||
|
||||
setup_tempdir
|
||||
|
||||
version_info "$@"
|
||||
|
||||
[[ -n $admin_name ]] && setup_local_ssh
|
||||
|
||||
copy_gl # src, conf, etc
|
||||
|
||||
run_install
|
||||
|
||||
[[ $upgrade == 0 ]] && initial_conf_key
|
||||
|
||||
# MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf"
|
||||
ssh $p_port $user@$host "cd $GL_ADMINDIR; \$PWD/src/gl-compile-conf $quiet"
|
||||
|
||||
setup_pta
|
||||
|
||||
clone_it
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# setup temp files
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
setup_tempdir() {
|
||||
export tmpgli=tmp-gl-install
|
||||
trap cleanup 0
|
||||
mkdir -p $tmpgli
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
rm -rf $tmpgli
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# service functions
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
die() { echo "$@"; echo; echo "run $0 without any arguments for help and tips"; cleanup; exit 1; }
|
||||
prompt() {
|
||||
# receives two arguments. A short piece of text to be displayed, without
|
||||
# pausing, in "quiet" mode, and a much longer one to be displayed, *with*
|
||||
# a pause, in normal (verbose) mode
|
||||
[[ $quiet == -q ]] && [[ -n $1 ]] && {
|
||||
eval "echo \"$1\""
|
||||
return
|
||||
}
|
||||
shift
|
||||
echo
|
||||
echo
|
||||
echo ------------------------------------------------------------------------
|
||||
eval "echo \"$1\""
|
||||
echo
|
||||
read -p '...press enter to continue or Ctrl-C to bail out'
|
||||
}
|
||||
usage() {
|
||||
cat <<EOFU
|
||||
Usage: $0 [-q] user host [port] admin_name # install
|
||||
$0 [-q] user host [port] # upgrade
|
||||
|
||||
- (optional) "-q" as first arg sets "quiet" mode: no verbose descriptions of
|
||||
what is going on, no pauses unless absolutely necessary
|
||||
- "user" is the username on the server where you will be installing gitolite
|
||||
- "host" is that server's hostname (or IP address)
|
||||
- "port" is the ssh server port on "host"; optional, defaults to 22
|
||||
- "admin_name" is *your* name as it should appear in the eventual gitolite
|
||||
config file. For upgrades (ie., gitolite is already installed on the
|
||||
server), this argument is not needed, and will be *ignored* if provided.
|
||||
|
||||
- (optional) "host_nickname" is a nickname that can be given to a particular
|
||||
installation of gitolite. This allows for multiple servers to easily be
|
||||
administered from this machine. Defaults to "gitolite"
|
||||
|
||||
Example usage: $0 git my.git.server sitaram [gitolite_server_1]
|
||||
|
||||
Notes:
|
||||
- "user","admin_name" and "host_nickname" must be simple names -- no
|
||||
special characters etc please (only alphanumerics, dot, hyphen,
|
||||
underscore)
|
||||
- traditionally, the "user" is "git", but it can be anything you want
|
||||
- "admin_name" should be your name, for clarity, or whoever will be the
|
||||
gitolite admin
|
||||
|
||||
Pre-requisites:
|
||||
- you must already have pubkey based access to user@host. If you currently
|
||||
only have password access, use "ssh-copy-id" or something equivalent (or
|
||||
copy the key manually). Somehow (doesn't matter how), get to the point
|
||||
where you can type "ssh user@host" and get a command line.
|
||||
|
||||
**DO NOT RUN THIS PROGRAM UNTIL THAT WORKS**
|
||||
|
||||
EOFU
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# basic sanity / argument checks
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
basic_sanity() {
|
||||
# MANUAL: this *must* be run as "src/gl-easy-install", not by cd-ing to
|
||||
# src and then running "./gl-easy-install"
|
||||
|
||||
bindir=${0%/*}
|
||||
# switch to parent of bindir; we assume the conf files are all there
|
||||
cd "$bindir"; cd ..
|
||||
|
||||
# are we in quiet mode?
|
||||
quiet=
|
||||
[[ "$1" == "-q" ]] && {
|
||||
quiet=-q
|
||||
shift
|
||||
}
|
||||
|
||||
# MANUAL: (info) we'll use "git" as the user, "server" as the host, and
|
||||
# "sitaram" as the admin_name in example commands shown below, if any
|
||||
|
||||
[[ -z $2 ]] && usage
|
||||
user=$1
|
||||
host=$2
|
||||
port=22
|
||||
admin_name=$3
|
||||
host_nickname=$4
|
||||
if [ -z $4 ]
|
||||
then
|
||||
host_nickname="gitolite"
|
||||
fi
|
||||
|
||||
# but if the 3rd arg is a number, that's a port number, and the 4th arg is
|
||||
# the admin_name
|
||||
if echo $3 | perl -lne 'exit 1 unless /^[0-9]+$/'
|
||||
then
|
||||
port=$3
|
||||
admin_name=$4
|
||||
host_nickname=$5
|
||||
if [ -z $5 ]
|
||||
then
|
||||
host_nickname=gitolite
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $user | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' ||
|
||||
die "user '$user' invalid"
|
||||
[[ "$user" == "root" ]] && die I refuse to install to root
|
||||
echo $admin_name | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' ||
|
||||
die "admin_name '$admin_name' invalid"
|
||||
echo $host_nickname | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' ||
|
||||
die "host nickname '$host_nickname' invalid"
|
||||
|
||||
# MANUAL: make sure you're in the gitolite directory, at the top level.
|
||||
# The following files should all be visible:
|
||||
|
||||
ls hooks/gitolite-admin/post-update \
|
||||
hooks/common/update \
|
||||
src/gitolite.pm \
|
||||
src/gl-install \
|
||||
src/gl-auth-command \
|
||||
src/gl-compile-conf \
|
||||
conf/example.conf \
|
||||
conf/example.gitolite.rc >/dev/null ||
|
||||
die "cant find at least some files in gitolite sources/config; aborting"
|
||||
|
||||
# MANUAL: make sure you have password-less (pubkey) auth on the server.
|
||||
# That is, running "ssh git@server" should log in straight away, without
|
||||
# asking for a password
|
||||
|
||||
[[ $port -ne 22 ]] && p_port="-p $port"
|
||||
ssh $p_port -o PasswordAuthentication=no $user@$host true ||
|
||||
die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# version info
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
version_info() {
|
||||
|
||||
# MANUAL: if needed, make a note of the version you are upgrading from, and to
|
||||
|
||||
# record which version is being sent across; we assume it's HEAD
|
||||
if git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
||||
then
|
||||
git describe --tags --long HEAD 2>/dev/null > conf/VERSION || echo '(unknown)' > conf/VERSION
|
||||
else
|
||||
[[ -f conf/VERSION ]] || echo '(unknown)' > conf/VERSION
|
||||
fi
|
||||
|
||||
# what was the old version there?
|
||||
export upgrade_details="you are upgrading \
|
||||
$(ssh $p_port $user@$host cat gitolite-install/conf/VERSION 2>/dev/null || echo '(or installing first-time)' ) \
|
||||
to $(cat conf/VERSION)"
|
||||
|
||||
prompt "$upgrade_details" "$v_upgrade_details"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# new keypair, ssh-config para; only on "install" (not upgrade)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
setup_local_ssh() {
|
||||
|
||||
# MANUAL: create a new key for you as a "gitolite user" (as opposed to you
|
||||
# as the "gitolite admin" who needs to login to the server and get a
|
||||
# command line). For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this
|
||||
# would create two files in ~/.ssh (sitaram and sitaram.pub)
|
||||
|
||||
prompt "setting up keypair..." "$v_setting_up_keypair"
|
||||
|
||||
if [[ -f "$HOME/.ssh/$admin_name.pub" ]]
|
||||
then
|
||||
prompt "" "$v_reuse_pubkey"
|
||||
else
|
||||
ssh-keygen -t rsa -f "$HOME/.ssh/$admin_name" || die "ssh-keygen failed for some reason..."
|
||||
fi
|
||||
|
||||
# MANUAL: copy the pubkey created to the server, say to /tmp. This would
|
||||
# be "scp ~/.ssh/sitaram.pub git@server:/tmp" (the script does this at a
|
||||
# later stage, you do it now for convenience). Note: only the pubkey
|
||||
# (sitaram.pub). Do NOT copy the ~/.ssh/sitaram file -- that is a private
|
||||
# key!
|
||||
|
||||
# MANUAL: if you're running ssh-agent (see if you have an environment
|
||||
# variable called SSH_AGENT_PID in your "env"), you should add this new
|
||||
# key. The command is "ssh-add ~/.ssh/sitaram"
|
||||
|
||||
if ssh-add -l &>/dev/null
|
||||
then
|
||||
prompt " ...adding key to agent..." "$v_ssh_add"
|
||||
ssh-add "$HOME/.ssh/$admin_name"
|
||||
fi
|
||||
|
||||
# MANUAL: you now need to add some lines to the end of your ~/.ssh/config
|
||||
# file. If the file doesn't exist, create it. Make sure the file is
|
||||
# "chmod 644".
|
||||
|
||||
# The lines to be included look like this:
|
||||
|
||||
# host gitolite
|
||||
# user git
|
||||
# hostname server
|
||||
# port 22
|
||||
# identityfile ~/.ssh/sitaram
|
||||
|
||||
echo "host $host_nickname
|
||||
user $user
|
||||
hostname $host
|
||||
port $port
|
||||
identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza
|
||||
|
||||
if grep "host *$host_nickname" "$HOME/.ssh/config" &>/dev/null
|
||||
then
|
||||
prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." "$v_found_para"
|
||||
else
|
||||
prompt "creating gitolite para in ~/.ssh/config..." "$v_creating_para"
|
||||
cat $tmpgli/.gl-stanza >> "$HOME/.ssh/config"
|
||||
# if the file didn't exist at all, it might have the wrong permissions
|
||||
chmod 644 "$HOME/.ssh/config"
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# server side
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
copy_gl() {
|
||||
|
||||
# MANUAL: copy the gitolite directories "src", "conf", and "doc" to the
|
||||
# server, to a directory called (for example) "gitolite-install". You may
|
||||
# have to create the directory first.
|
||||
|
||||
ssh $p_port $user@$host mkdir -p gitolite-install
|
||||
scp $quiet ${p_port/p/P} -p -r src conf doc hooks $user@$host:gitolite-install/
|
||||
|
||||
# MANUAL: now log on to the server (ssh git@server) and get a command
|
||||
# line. This step is for your convenience; the script does it all from
|
||||
# the client side but that may be too much typing for manual use ;-)
|
||||
|
||||
# MANUAL: cd to the "gitolite-install" directory where the sources are.
|
||||
# Then copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you
|
||||
# wish to change any paths. Make a note of the GL_ADMINDIR and REPO_BASE
|
||||
# paths; you will need them later
|
||||
|
||||
prompt "finding/creating gitolite rc..." "$v_edit_glrc"
|
||||
|
||||
# lets try and get the file from there first
|
||||
if scp ${p_port/p/P} $user@$host:.gitolite.rc $tmpgli &>/dev/null
|
||||
then
|
||||
prompt " ...trying to reuse existing rc" \
|
||||
"Oh hey... you already had a '.gitolite.rc' file on the server.
|
||||
Let's see if we can use that instead of the default one..."
|
||||
< $tmpgli/.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.old
|
||||
< conf/example.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.new
|
||||
# msysgit doesn't have "comm". diff is not ideal for our purposes
|
||||
# because we only care about differences in one direction, but we'll
|
||||
# have to make do...
|
||||
set +e
|
||||
diff -u $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+.*\$' > $tmpgli/glrc.comm13
|
||||
set -e
|
||||
if [[ ! -s $tmpgli/glrc.comm13 ]]
|
||||
then
|
||||
[[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc
|
||||
else
|
||||
echo new variables found in rc file:
|
||||
cat $tmpgli/glrc.comm13
|
||||
echo
|
||||
# MANUAL: if you're upgrading, read the instructions below and
|
||||
# manually make sure your final ~/.gitolite.rc has both your existing
|
||||
# customisations as well as any new variables that the new version of
|
||||
# gitolite has introduced
|
||||
prompt "" "$v_upgrade_glrc"
|
||||
${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc $tmpgli/.gitolite.rc
|
||||
fi
|
||||
else
|
||||
cp conf/example.gitolite.rc $tmpgli/.gitolite.rc
|
||||
[[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc
|
||||
fi
|
||||
|
||||
# copy the rc across
|
||||
scp $quiet ${p_port/p/P} $tmpgli/.gitolite.rc $user@$host:
|
||||
}
|
||||
|
||||
run_install() {
|
||||
|
||||
prompt "installing/upgrading..." "$v_ignore_stuff"
|
||||
|
||||
# extract the GL_ADMINDIR, REPO_BASE and GIT_PATH locations
|
||||
GL_ADMINDIR=$(ssh $p_port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'")
|
||||
REPO_BASE=$( ssh $p_port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'")
|
||||
GIT_PATH=$( ssh $p_port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GIT_PATH'")
|
||||
|
||||
# determine if this is an upgrade; we decide based on whether a file
|
||||
# called $GL_ADMINDIR/conf/gitolite.conf exists on the remote side. We
|
||||
# can't do this till we know the correct value for GL_ADMINDIR
|
||||
upgrade=0
|
||||
if ssh $p_port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null
|
||||
then
|
||||
upgrade=1
|
||||
ssh $p_port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf 2> /dev/null | grep '@SHELL' &&
|
||||
prompt "" "$v_at_shell_bwi"
|
||||
[[ -n $admin_name ]] && echo -e "\n *** WARNING ***: looks like an upgrade... ignoring argument '$admin_name'"
|
||||
else
|
||||
[[ -z $admin_name ]] && die " *** ERROR ***: doesn't look like an upgrade, so I need a name for the admin"
|
||||
fi
|
||||
|
||||
# MANUAL: still in the "gitolite-install" directory? Good. Run
|
||||
# "src/gl-install"
|
||||
|
||||
ssh $p_port $user@$host "cd gitolite-install; src/gl-install $quiet"
|
||||
|
||||
# MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done!
|
||||
# -- ignore the rest of this file for the purposes of an upgrade
|
||||
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# from here on it's install only
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# MANUAL: setup the initial config file. Edit $GL_ADMINDIR/conf/gitolite.conf
|
||||
# and add at least the following lines to it:
|
||||
|
||||
# repo gitolite-admin
|
||||
# RW+ = sitaram
|
||||
|
||||
initial_conf_key() {
|
||||
echo "#gitolite conf
|
||||
# please see conf/example.conf for details on syntax and features
|
||||
|
||||
repo gitolite-admin
|
||||
RW+ = $admin_name
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
|
||||
" > $tmpgli/gitolite.conf
|
||||
|
||||
# send the config and the key to the remote
|
||||
scp $quiet ${p_port/p/P} $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/
|
||||
scp $quiet ${p_port/p/P} "$HOME/.ssh/$admin_name.pub" $user@$host:$GL_ADMINDIR/keydir
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# hey lets go the whole hog on this; setup push-to-admin!
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
setup_pta() {
|
||||
|
||||
# MANUAL: you have to now make the first commit in the admin repo. This
|
||||
# is a little more complex, so read carefully and substitute the correct
|
||||
# paths. What you have to do is:
|
||||
|
||||
# cd $REPO_BASE/gitolite-admin.git
|
||||
# GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
|
||||
# GIT_WORK_TREE=$GL_ADMINDIR git commit -am start
|
||||
|
||||
# Substitute $GL_ADMINDIR and $REPO_BASE appropriately. Note there is no
|
||||
# space around the "=" in the second and third lines.
|
||||
|
||||
echo "cd $REPO_BASE/gitolite-admin.git
|
||||
PATH=\$PATH:$GIT_PATH
|
||||
GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
|
||||
GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start
|
||||
" | ssh $p_port $user@$host
|
||||
|
||||
# MANUAL: now that the admin repo is created, you have to set the hooks
|
||||
# properly. The install program does this. So cd back to the
|
||||
# "gitolite-install" directory and run "src/gl-install"
|
||||
|
||||
ssh $p_port $user@$host "cd gitolite-install; src/gl-install $quiet"
|
||||
|
||||
# MANUAL: you're done! Log out of the server, come back to your
|
||||
# workstation, and clone the admin repo using "git clone
|
||||
# gitolite:gitolite-admin", or pull once again if you already have a
|
||||
# clone
|
||||
}
|
||||
|
||||
clone_it()
|
||||
{
|
||||
cleanup
|
||||
cd "$HOME"
|
||||
if [[ -d $host_nickname-admin ]]
|
||||
then
|
||||
echo $HOME/$host_nickname-admin exists, skipping clone step...
|
||||
else
|
||||
prompt "cloning $host_nickname-admin repo..." "$v_cloning"
|
||||
git clone $host_nickname:gitolite-admin $host_nickname-admin
|
||||
fi
|
||||
|
||||
# MANUAL: be sure to read the message below; this applies to you too...
|
||||
|
||||
echo
|
||||
echo
|
||||
echo ---------------------------------------------------------------
|
||||
eval "echo \"$v_done\""
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# prompt strings
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
v_upgrade_details="
|
||||
\$upgrade_details
|
||||
|
||||
Note: getting '(unknown)' for the 'from' version should only happen once.
|
||||
Getting '(unknown)' for the 'to' version means you are probably installing
|
||||
from a tar file dump, not a real clone. This is not an error but it's nice to
|
||||
have those version numbers in case you need support. Try and install from a
|
||||
clone
|
||||
"
|
||||
|
||||
v_setting_up_keypair="
|
||||
the next command will create a new keypair for your gitolite access
|
||||
|
||||
The pubkey will be \$HOME/.ssh/\$admin_name.pub. You will have to choose a
|
||||
passphrase or hit enter for none. I recommend not having a passphrase for
|
||||
now, *especially* if you do not have a passphrase for the key which you are
|
||||
already using to get server access!
|
||||
|
||||
Add one using 'ssh-keygen -p' after all the setup is done and you've
|
||||
successfully cloned and pushed the gitolite-admin repo. After that, install
|
||||
'keychain' or something similar, and add the following command to your bashrc
|
||||
(since this is a non-default key)
|
||||
|
||||
ssh-add "\\\$HOME/.ssh/\$admin_name"
|
||||
|
||||
This makes using passphrases very convenient.
|
||||
"
|
||||
|
||||
v_reuse_pubkey="
|
||||
Hmmm... pubkey \$HOME/.ssh/\$admin_name.pub exists; should I just (re-)use it?
|
||||
|
||||
IMPORTANT: once the install completes, *this* key can no longer be used to get
|
||||
a command line on the server -- it will be used by gitolite, for git access
|
||||
only. If that is a problem, please ABORT now.
|
||||
|
||||
doc/6-ssh-troubleshooting.mkd will explain what is happening here, if you need
|
||||
more info.
|
||||
"
|
||||
|
||||
v_ssh_add="
|
||||
you're running ssh-agent. We'll try and do an ssh-add of the
|
||||
private key we just created, otherwise this key won't get picked up. If
|
||||
you specified a passphrase in the previous step, you'll get asked for one
|
||||
now -- type in the same one.
|
||||
"
|
||||
|
||||
v_found_para="
|
||||
your \\\$HOME/.ssh/config already has settings for gitolite. I will assume
|
||||
they're correct, but if they're not, please edit that file, delete that
|
||||
paragraph (that line and the following few lines), Ctrl-C, and rerun.
|
||||
|
||||
In case you want to check right now (from another terminal) if they're
|
||||
correct, here's what they are *supposed* to look like:
|
||||
|
||||
\$(cat \$tmpgli/.gl-stanza)
|
||||
|
||||
"
|
||||
|
||||
v_creating_para="
|
||||
creating settings for your gitolite access in \$HOME/.ssh/config;
|
||||
these are the lines that will be appended to your ~/.ssh/config:
|
||||
|
||||
\$(cat \$tmpgli/.gl-stanza)
|
||||
|
||||
"
|
||||
|
||||
v_edit_glrc="
|
||||
the gitolite rc file needs to be edited by hand. The defaults are sensible,
|
||||
so if you wish, you can just exit the editor.
|
||||
|
||||
Otherwise, make any changes you wish and save it. Read the comments to
|
||||
understand what is what -- the rc file's documentation is inline.
|
||||
|
||||
Please remember this file will actually be copied to the server, and that all
|
||||
the paths etc. represent paths on the server!
|
||||
"
|
||||
|
||||
v_upgrade_glrc="
|
||||
looks like you're upgrading, and there are some new rc variables that this
|
||||
version is expecting that your old rc file doesn't have.
|
||||
|
||||
I'm going to run your \\\$EDITOR with two filenames. The first is the example
|
||||
file from this gitolite version. It will have a block (code and comments) for
|
||||
each of the variables shown above with a '+' sign.
|
||||
|
||||
The second is your current rc file, the destination. Copy those lines into
|
||||
this file, preferably *with* the surrounding comments (for clarity) and save
|
||||
it.
|
||||
|
||||
This is necessary; please dont skip this!
|
||||
|
||||
[It's upto you to figure out how your \\\$EDITOR handles 2 filename arguments,
|
||||
switch between them, copy lines, etc ;-)]
|
||||
"
|
||||
|
||||
v_ignore_stuff="
|
||||
ignore any 'please edit this file' or 'run this command' type lines in the
|
||||
next set of command outputs coming up. They're only relevant for a manual
|
||||
install, not this one...
|
||||
"
|
||||
|
||||
v_at_shell_bwi="
|
||||
you are using the @SHELL feature in your gitolite config. This feature has
|
||||
now changed in a backward incompatible way; see doc/6-ssh-troubleshooting.mkd
|
||||
for information on migrating this to the new syntax.
|
||||
|
||||
DO NOT hit enter unless you have understood that information and properly
|
||||
migrated your setup, or you are sure you have shell access to the server
|
||||
through some other means than the $admin_name key.
|
||||
|
||||
"
|
||||
|
||||
v_done="
|
||||
done!
|
||||
|
||||
IMPORTANT NOTE -- PLEASE READ!!!
|
||||
*Your* URL for cloning any repo from this server will be
|
||||
\$host_nickname:reponame.git
|
||||
|
||||
Note: If you are upgrading and you set a host nickname during initial
|
||||
setup, please use that host nickname instead of \"gitolite\"
|
||||
above.
|
||||
|
||||
*Other* users you set up will have to use
|
||||
\$user@\$host:reponame.git
|
||||
However, if your server uses a non-standard ssh port, they should use
|
||||
ssh://\$user@\$host:\$port/reponame.git
|
||||
|
||||
If this is your first time installing gitolite, please also:
|
||||
tail -31 \$0
|
||||
for next steps.
|
||||
"
|
||||
|
||||
v_cloning="
|
||||
now we will clone the gitolite-admin repo to your workstation and see if it
|
||||
all hangs together. We'll do this in your \\\$HOME for now, and you can move
|
||||
it elsewhere later if you wish to.
|
||||
"
|
||||
|
||||
tail="
|
||||
NOTE: All the below stuff is on your *workstation*. You should not, normally,
|
||||
have to do anything directly on your server to administer/use gitolite.
|
||||
|
||||
The admin repo is currently cloned at ~/gitolite-admin. You can reclone it
|
||||
anywhere else if you wish. To administer gitolite, make changes to the config
|
||||
file (conf/gitolite.conf) and/or the pubkeys (in subdirectory 'keydir') in any
|
||||
clone, then git add, git commit, and git push.
|
||||
|
||||
ADDING REPOS: Do NOT add repos manually on the server. Edit the config file
|
||||
to give *some* user access to the repo. When you push, an empty repo will be
|
||||
created on the server.
|
||||
|
||||
ADDING USERS: copy their pubkey as keydir/<username>.pub, add it, commit and
|
||||
push.
|
||||
|
||||
CONFIG FILE FORMAT: see comments in conf/example.conf in the gitolite source.
|
||||
|
||||
SSH MAGIC: Remember you (the admin) now have *two* keys to access the server
|
||||
hosting your gitolite setup -- one to get you a command line, and one to get
|
||||
you gitolite access; see doc/6-ssh-troubleshooting.mkd. If you're not using
|
||||
keychain or some such software, you may have to run an 'ssh-add' command to
|
||||
add that key each time you log in.
|
||||
|
||||
URLS: *Your* URL for cloning any repo on this server is different from the
|
||||
url that the *other* users have to use. The easy install command should tell
|
||||
you what these URLs look like, at the end of each successful run. Feel free
|
||||
to re-run easy install again (using the same arguments) if you missed it.
|
||||
|
||||
UPGRADING GITOLITE: just pull a fresh clone from github, and run the same easy
|
||||
install command as before, with the same arguments.
|
||||
"
|
114
src/gl-install
Executable file
114
src/gl-install
Executable file
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# INTERNAL COMMAND. NOT MEANT TO BE RUN BY THE USER DIRECTLY.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our ($REPO_BASE, $GL_ADMINDIR, $GL_CONF, $GIT_PATH, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $GL_PERFLOGT, $REPO_UMASK);
|
||||
|
||||
# setup quiet mode if asked; please do not use this when running manually
|
||||
open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q');
|
||||
|
||||
# wrapper around mkdir; it's not an error if the directory exists, but it is
|
||||
# an error if it doesn't exist and we can't create it
|
||||
sub wrap_mkdir
|
||||
{
|
||||
my $dir = shift;
|
||||
my $perm = shift; # optional
|
||||
if ( -d $dir ) {
|
||||
print "$dir already exists\n";
|
||||
return;
|
||||
}
|
||||
mkdir($dir) or die "mkdir $dir failed: $!\n";
|
||||
chmod $perm, $dir if $perm;
|
||||
print "created $dir\n";
|
||||
}
|
||||
|
||||
# the common setup module is in the same directory as this running program is
|
||||
my $bindir = $0;
|
||||
$bindir =~ s/\/[^\/]+$//;
|
||||
require "$bindir/gitolite.pm";
|
||||
|
||||
# ask where the rc file is, get it, and "do" it
|
||||
&where_is_rc();
|
||||
unless ($ENV{GL_RC}) {
|
||||
# doesn't exist. Copy it across, tell user to edit it and come back
|
||||
my $glrc = $ENV{HOME} . "/.gitolite.rc";
|
||||
if ($GL_PACKAGE_CONF) {
|
||||
system("cp $GL_PACKAGE_CONF/example.gitolite.rc $glrc");
|
||||
} else {
|
||||
system("cp $bindir/../conf/example.gitolite.rc $glrc");
|
||||
}
|
||||
print "created $glrc\n";
|
||||
print "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
# ok now the rc file exists; read it to get the other paths
|
||||
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
|
||||
|
||||
# add a custom path for git binaries, if specified
|
||||
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
|
||||
|
||||
# set the umask before creating any files/directories
|
||||
umask($REPO_UMASK);
|
||||
|
||||
# mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist
|
||||
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
||||
wrap_mkdir($ENV{GL_REPO_BASE_ABS});
|
||||
wrap_mkdir($GL_ADMINDIR, 0700);
|
||||
# mkdir $GL_ADMINDIR's subdirs
|
||||
for my $dir qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin) {
|
||||
# some of them will stay empty; too lazy to fix right now ;-)
|
||||
wrap_mkdir("$GL_ADMINDIR/$dir", 0700);
|
||||
}
|
||||
|
||||
# "src" and "doc" will be overwritten on each install, but not conf
|
||||
if ($GL_PACKAGE_HOOKS) {
|
||||
system("cp -R -p $GL_PACKAGE_HOOKS $GL_ADMINDIR");
|
||||
} else {
|
||||
system("cp -R -p $bindir/../src $bindir/../doc $bindir/../hooks $GL_ADMINDIR");
|
||||
system("cp $bindir/../conf/VERSION $GL_ADMINDIR/conf");
|
||||
}
|
||||
|
||||
unless (-f $GL_CONF or $GL_PACKAGE_CONF) {
|
||||
print <<EOF;
|
||||
please do the following:
|
||||
1. create and edit $GL_CONF to contain something like this:
|
||||
repo gitolite-admin
|
||||
RW+ = yourname
|
||||
2. copy "yourname.pub" to $GL_ADMINDIR/keydir
|
||||
3. run this command
|
||||
$GL_ADMINDIR/src/gl-compile-conf
|
||||
EOF
|
||||
}
|
||||
|
||||
# finally, hooks must be propagated to all the repos in case they changed
|
||||
chdir("$ENV{GL_REPO_BASE_ABS}") or die "chdir $ENV{GL_REPO_BASE_ABS} failed: $!\n";
|
||||
for my $repo (`find . -type d -name "*.git"`) {
|
||||
chomp ($repo);
|
||||
# propagate our own, plus any local admin-defined, hooks
|
||||
ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo/hooks");
|
||||
# in case of package install, GL_ADMINDIR is no longer the top cop;
|
||||
# override with the package hooks
|
||||
ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo/hooks") if $GL_PACKAGE_HOOKS;
|
||||
chmod 0755, "$repo/hooks/update";
|
||||
}
|
||||
|
||||
# oh and one of those repos is a bit more special and has an extra hook :)
|
||||
if ( -d "gitolite-admin.git/hooks" ) {
|
||||
print "copying post-update hook to gitolite-admin repo...\n";
|
||||
unlink "gitolite-admin.git/hooks/post-update";
|
||||
symlink "$GL_ADMINDIR/hooks/gitolite-admin/post-update", "gitolite-admin.git/hooks/post-update"
|
||||
or die "could not symlink post-update hook\n";
|
||||
# ditto... (see previous block)
|
||||
ln_sf("$GL_PACKAGE_HOOKS/gitolite-admin", "post-update", "gitolite-admin.git/hooks") if $GL_PACKAGE_HOOKS;
|
||||
chmod 0755, "gitolite-admin.git/hooks/post-update";
|
||||
}
|
||||
|
||||
# fixup program renames
|
||||
for my $oldname qw(pta-hook.sh conf-convert.pl 00-easy-install.sh 99-emergency-addkey.sh gl-emergency-addkey install.pl update-hook.pl hooks/update ga-post-update-hook VERSION) {
|
||||
unlink "$GL_ADMINDIR/src/$oldname";
|
||||
unlink "$ENV{HOME}/gitolite-install/src/$oldname";
|
||||
}
|
23
src/gl-mirror-shell
Executable file
23
src/gl-mirror-shell
Executable file
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
|
||||
export GL_BYPASS_UPDATE_HOOK
|
||||
GL_BYPASS_UPDATE_HOOK=1
|
||||
|
||||
export REPO_BASE=`cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE' `
|
||||
export REPO_UMASK=`cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_UMASK' `
|
||||
umask $REPO_UMASK
|
||||
|
||||
if echo $SSH_ORIGINAL_COMMAND | egrep git-upload\|git-receive >/dev/null
|
||||
then
|
||||
|
||||
# the (special) admin post-update hook needs these, so we cheat
|
||||
export GL_ADMINDIR
|
||||
export GL_BINDIR
|
||||
GL_ADMINDIR=` cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'`
|
||||
GL_BINDIR=`echo $0 | perl -lpe 's/^/$ENV{PWD}\// unless /^\//; s/\/[^\/]+$//;'`
|
||||
|
||||
SSH_ORIGINAL_COMMAND=`echo $SSH_ORIGINAL_COMMAND | sed -e "s/'/'$REPO_BASE\//"`
|
||||
exec git shell -c "$SSH_ORIGINAL_COMMAND"
|
||||
else
|
||||
bash -c "cd $REPO_BASE; $SSH_ORIGINAL_COMMAND"
|
||||
fi
|
38
src/gl-mirror-sync
Executable file
38
src/gl-mirror-sync
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
mirror=$1
|
||||
[ -z "$1" ] && { echo need \"user@host\" or ssh hostalias; exit 1; }
|
||||
ssh -o PasswordAuthentication=no $mirror echo hello-there | grep hello-there >/dev/null ||
|
||||
{ echo I cant ssh to $mirror; exit 1; }
|
||||
|
||||
cd $HOME
|
||||
REPO_BASE=` cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE'`
|
||||
cd $REPO_BASE
|
||||
|
||||
ssh $mirror cat \$HOME/.gitolite.rc | expand | egrep '^ *\$GL_SLAVE_MODE *= *1; *$' >/dev/null || {
|
||||
echo $mirror does not seem to be in slave mode
|
||||
exit 1;
|
||||
}
|
||||
|
||||
find . -type d -name "*.git" | cut -c3- | sort | while read r
|
||||
do
|
||||
cd $HOME; cd $REPO_BASE; cd $r
|
||||
printf "$r "
|
||||
|
||||
if [ `git rev-parse HEAD` = "HEAD" ]
|
||||
then
|
||||
echo is empty\; skipping
|
||||
continue
|
||||
fi
|
||||
|
||||
# this is essentially the same code as in the post-receive hook
|
||||
if git push --mirror $mirror:$r
|
||||
then
|
||||
:
|
||||
else
|
||||
ssh $mirror mkdir -p $r
|
||||
ssh $mirror git init --bare $r
|
||||
git push --mirror $mirror:$r ||
|
||||
echo "WARNING: mirror push to $mirror failed"
|
||||
fi < /dev/null
|
||||
done
|
109
src/gl-setup
Executable file
109
src/gl-setup
Executable file
|
@ -0,0 +1,109 @@
|
|||
#!/bin/sh
|
||||
|
||||
GL_PACKAGE_CONF=/tmp/share/gitolite/conf
|
||||
# must be the same as the value for the same variable in
|
||||
# $GL_PACKAGE_CONF/example.gitolite.rc. Sorry about the catch-22 :)
|
||||
|
||||
# TODO need to fix for portability to ksh and so on
|
||||
# TODO need to get the version in there somehow
|
||||
|
||||
# This program is meant to be completely non-interactive, suitable for running
|
||||
# server-side from a "post RPM/DEB install" script, or manually by users.
|
||||
# Please see the doc/0-user-setup.mkd for details.
|
||||
|
||||
# usage:
|
||||
# $0 [foo.pub]
|
||||
|
||||
# The pubkey filename must end with ".pub" and is mandatory when you first run
|
||||
# this command. Otherwise it is optional, and can be used to override a
|
||||
# pubkey file if you happen to have lost all gitolite-access to the repos (but
|
||||
# do have shell access via some other means)
|
||||
|
||||
die() { echo "$@"; exit 1; }
|
||||
|
||||
|
||||
if [ -n "$GITOLITE_HTTP_HOME" ]
|
||||
then
|
||||
HOME=$GITOLITE_HTTP_HOME
|
||||
admin_name=$1
|
||||
else
|
||||
pubkey_file=$1
|
||||
admin_name=
|
||||
if [ -n "$pubkey_file" ]
|
||||
then
|
||||
echo $pubkey_file | grep '.pub$' >/dev/null || die "$pubkey_file must end in .pub"
|
||||
[ -f $pubkey_file ] || die "cant find $pubkey_file"
|
||||
admin_name=` basename $pubkey_file .pub`
|
||||
echo $admin_name | grep '@' >/dev/null && die "please don't use '@' in the initial admin name"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f $HOME/.gitolite.rc ]
|
||||
then
|
||||
perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < $GL_PACKAGE_CONF/example.gitolite.rc | sort > .newvars
|
||||
perl -ne 's/^\s+//; s/[\s=].*//; print if /^\$/;' < $HOME/.gitolite.rc | sort > .oldvars
|
||||
comm -23 .newvars .oldvars > .diffvars
|
||||
if [ -s .diffvars ]
|
||||
then
|
||||
cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc.new
|
||||
echo new version of the rc file saved in $HOME/.gitolite.rc.new
|
||||
echo
|
||||
echo please update $HOME/.gitolite.rc manually if you need features
|
||||
echo controlled by any of the following variables:
|
||||
echo ----
|
||||
sed -e 's/^/ /' < .diffvars
|
||||
echo ----
|
||||
fi
|
||||
rm -f .newvars .oldvars .diffvars
|
||||
else
|
||||
[ -n "$GITOLITE_HTTP_HOME" ] || [ -n "$pubkey_file" ] || die "looks like first run -- I need a pubkey file"
|
||||
[ -z "$GITOLITE_HTTP_HOME" ] || [ -n "$admin_name" ] || die "looks like first run -- I need an admin name"
|
||||
|
||||
cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc
|
||||
printf "The default settings in the "rc" file ($HOME/.gitolite.rc) are fine for most\n"
|
||||
printf "people but if you wish to make any changes, you can do so now.\n\nhit enter..."
|
||||
read i
|
||||
${EDITOR:-vi} $HOME/.gitolite.rc
|
||||
fi
|
||||
|
||||
# setup ssh stuff. We break our normal rule that we will not fiddle with
|
||||
# authkeys etc., because in this case it seems appropriate
|
||||
(
|
||||
cd $HOME
|
||||
mkdir -p .ssh
|
||||
chmod go-rwx .ssh
|
||||
touch .ssh/authorized_keys
|
||||
chmod go-w . .ssh .ssh/authorized_keys
|
||||
)
|
||||
|
||||
# now we get to gitolite itself
|
||||
|
||||
gl-install -q
|
||||
|
||||
GL_ADMINDIR=` cd $HOME;perl -e 'do ".gitolite.rc"; print $GL_ADMINDIR'`
|
||||
REPO_BASE=` cd $HOME;perl -e 'do ".gitolite.rc"; print $REPO_BASE' `
|
||||
|
||||
[ -f $GL_ADMINDIR/conf/gitolite.conf ] || {
|
||||
cat <<EOF > $GL_ADMINDIR/conf/gitolite.conf
|
||||
repo gitolite-admin
|
||||
RW+ = $admin_name
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
EOF
|
||||
}
|
||||
[ -n "$pubkey_file" ] && cp $pubkey_file $GL_ADMINDIR/keydir
|
||||
|
||||
touch $HOME/.ssh/authorized_keys
|
||||
gl-compile-conf -q
|
||||
|
||||
# setup push-to-admin
|
||||
od=$PWD
|
||||
cd; cd $REPO_BASE/gitolite-admin.git
|
||||
GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
|
||||
GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start
|
||||
cd $od
|
||||
|
||||
# now that the admin repo is created, you have to set the hooks properly; best
|
||||
# do it by running install again
|
||||
gl-install -q
|
56
src/gl-setup-authkeys
Executable file
56
src/gl-setup-authkeys
Executable file
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
# shim program
|
||||
|
||||
# arg-1: keydir
|
||||
|
||||
# - an external program populates "keydir" with *all* keys and then
|
||||
# calls us, giving "keydir" as arg-1
|
||||
# - we then call gitolite.pm's "setup_authkeys" function to do its thing
|
||||
|
||||
# IMPLEMENTATION NOTE: make sure this is in the same directory as
|
||||
# "gitolite.pm" and all the rest of "src/".
|
||||
|
||||
# DISCUSSION:
|
||||
#
|
||||
# For now, we will assume *all* the keys are in the keydir passed. The
|
||||
# setup_authkeys routine factored out from the old gl-compile-conf is
|
||||
# not setup to take a partial set of keys and create the
|
||||
# ~/.ssh/authorized_keys file.
|
||||
#
|
||||
# Also, there are issues to do with *deleted* keys that need to be taken
|
||||
# care of.
|
||||
#
|
||||
# All in all, unless it is shown to be quite inefficient, I'd much
|
||||
# prefer processing *all* keys each time there is a change.
|
||||
|
||||
our ($GL_PERFLOGT);
|
||||
|
||||
# setup
|
||||
my $bindir = $0;
|
||||
$bindir =~ s/\/[^\/]+$//;
|
||||
$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//;
|
||||
require "$bindir/gitolite.pm";
|
||||
|
||||
# prevent newbie from running it accidentally and clobbering his authkeys
|
||||
# file!
|
||||
if (@ARGV and $ARGV[0] eq '-batch') {
|
||||
shift;
|
||||
} else {
|
||||
print STDERR "
|
||||
This is a cronnable, batchable, program to rewrite ~/.ssh/authorized_keys
|
||||
using public keys in a given directory.
|
||||
|
||||
If you are ABSOLUTELY sure you know what you're doing, here's how:
|
||||
|
||||
$0 -batch keydir
|
||||
|
||||
where 'keydir' contains a bunch of '*.pub' files\n\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# quick sanity check and run
|
||||
my $keydir = shift or die "I need a directory name\n";
|
||||
-d $keydir or die "$keydir should be a directory\n";
|
||||
|
||||
&setup_authkeys($bindir, $keydir);
|
53
src/gl-system-install
Executable file
53
src/gl-system-install
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/sh
|
||||
|
||||
# install the gitolite software *system wide*. Not too robust, fancy, etc.,
|
||||
# but does have a usage message and catches simple problems.
|
||||
|
||||
usage() { echo "
|
||||
Usage:
|
||||
$0 shared-bin-dir shared-conf-dir shared-hooks-dir
|
||||
|
||||
Example:
|
||||
$0 /usr/local/bin /var/gitolite/conf /var/gitolite/hooks
|
||||
|
||||
In this example, all the binaries go to /usr/local/bin, and the shared
|
||||
conf and hooks files/directories go to the other 2 directories given
|
||||
"
|
||||
exit 1;
|
||||
}
|
||||
|
||||
die() { echo "$@"; echo; usage; exit 1; }
|
||||
|
||||
[ -z "$3" ] && usage
|
||||
|
||||
gl_bin_dir=$1
|
||||
[ -d $gl_bin_dir ] || die "$gl_bin_dir not found"
|
||||
gl_conf_dir=$2
|
||||
[ -d $gl_conf_dir ] || die "$gl_conf_dir not found"
|
||||
gl_hooks_dir=$3
|
||||
[ -d $gl_hooks_dir ] || die "$gl_hooks_dir not found"
|
||||
|
||||
bindir=`echo $0 | perl -lpe 's/^/$ENV{PWD}\// unless /^\//; s/\/[^\/]+$//;'`
|
||||
cd $bindir/.. # we assume the standard gitolite source tree is here!
|
||||
|
||||
cp src/* $gl_bin_dir || die "cp src/* to $gl_bin_dir failed"
|
||||
rm $gl_bin_dir/gl-easy-install
|
||||
perl -lpi -e "s(^GL_PACKAGE_CONF=.*)(GL_PACKAGE_CONF=$gl_conf_dir)" $gl_bin_dir/gl-setup
|
||||
|
||||
# record which version is being sent across; we assume it's HEAD
|
||||
if git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
||||
then
|
||||
git describe --tags --long HEAD 2>/dev/null > conf/VERSION || die "git describe failed -- this should not happen!"
|
||||
else
|
||||
[ -f conf/VERSION ] || echo '(unknown)' > conf/VERSION
|
||||
fi
|
||||
|
||||
cp -R conf/* $gl_conf_dir || die "cp conf/* to $gl_conf_dir failed"
|
||||
perl -lpi \
|
||||
-e "s(^#\s*\\\$GL_PACKAGE_CONF\s*=.*)(\\\$GL_PACKAGE_CONF = '$gl_conf_dir';)" \
|
||||
$gl_conf_dir/example.gitolite.rc
|
||||
perl -lpi \
|
||||
-e "s(^#\s*\\\$GL_PACKAGE_HOOKS\s*=.*)(\\\$GL_PACKAGE_HOOKS = '$gl_hooks_dir';)" \
|
||||
$gl_conf_dir/example.gitolite.rc
|
||||
|
||||
cp -R hooks/* $gl_hooks_dir || die "cp hooks/* to $gl_hooks_dir failed"
|
40
src/gl-time
Executable file
40
src/gl-time
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# this program is a performance measurement wrapper around anything that it is
|
||||
# called with; it's arg-1 becomes the program being measured, with arg-2
|
||||
# onwards being arg-1's arguments
|
||||
|
||||
# sorta like the "time" command... hence the name :-)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Time::HiRes qw(gettimeofday tv_interval);
|
||||
|
||||
our ($GL_PERFLOGT);
|
||||
|
||||
# rc file
|
||||
do "$ENV{HOME}/.gitolite.rc";
|
||||
# this file is always in a fixed place; code in the main gitolite that
|
||||
# seems to indicate it is not, is obsolete and needs to be fixed.
|
||||
|
||||
# the common setup module is in the same directory as this running program is
|
||||
my $bindir = $0;
|
||||
$bindir =~ s/\/[^\/]+$//;
|
||||
$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//;
|
||||
require "$bindir/gitolite.pm";
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
my $starttime = [gettimeofday];
|
||||
|
||||
my $pgm = shift;
|
||||
my $returncode = system($pgm, @ARGV);
|
||||
$returncode >>= 8;
|
||||
$ENV{GL_USER} = shift;
|
||||
|
||||
my $elapsedtime = tv_interval($starttime);
|
||||
|
||||
$ENV{GL_LOG} = &get_logfilename($GL_PERFLOGT);
|
||||
# log_it logs to $ENV{GL_LOG}
|
||||
&log_it("", "$elapsedtime\trc=$returncode");
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue