Compare commits
199 Commits
Author | SHA1 | Date |
---|---|---|
Denis Knauf | a6f6886e84 | |
Sitaram Chamarty | 089f0f9d9e | |
Sitaram Chamarty | 5aef1adc7b | |
Sitaram Chamarty | 1fefb1c0d9 | |
Sitaram Chamarty | ea3d04ea0a | |
Sitaram Chamarty | 84424e48b9 | |
Sitaram Chamarty | b303694882 | |
Sitaram Chamarty | b9bbb78278 | |
Sitaram Chamarty | 3513f4a153 | |
Sitaram Chamarty | 4f4658274d | |
Sitaram Chamarty | 2048484578 | |
Sitaram Chamarty | 8e3ee2f9c1 | |
Sitaram Chamarty | 3103d68a75 | |
Sven Strickroth | f89408adb1 | |
Sitaram Chamarty | fc7ddfc818 | |
Sitaram Chamarty | f1c69a3ec0 | |
Sitaram Chamarty | 2741fadc9d | |
Sitaram Chamarty | b6d6260dbb | |
Sitaram Chamarty | 72e36f32aa | |
Stephen Palmer | d2214b06b5 | |
Sitaram Chamarty | 96cc2eaf41 | |
Sebastian Koslowski | 96be9503ef | |
Sitaram Chamarty | 7cec71b0ef | |
Sitaram Chamarty | cd838411fa | |
Sitaram Chamarty | 2018267a45 | |
Sitaram Chamarty | a26532d635 | |
Sitaram Chamarty | 5f9789ed8e | |
Sitaram Chamarty | d3d93961a0 | |
Sitaram Chamarty | 1f96180df0 | |
gitolite tester | 57760d7e1b | |
gitolite tester | 16f2d9b879 | |
Sitaram Chamarty | c03d107bac | |
Sitaram Chamarty | d491b5384f | |
Sitaram Chamarty | 8a9564f171 | |
Sitaram Chamarty | a509b208e3 | |
Sitaram Chamarty | be61cd2d66 | |
Sitaram Chamarty | 70ad045e08 | |
Andrew Page | 2aa129bc70 | |
Sitaram Chamarty | a802071a5e | |
Sitaram Chamarty | 4eb8cd4ad1 | |
Sitaram Chamarty | 3eefc06551 | |
Eugene E. Kashpureff Jr | 896ada58c0 | |
Sitaram Chamarty | 51ab768e2a | |
Sitaram Chamarty | f636ce3ba3 | |
Sitaram Chamarty | 0d371ac957 | |
Sitaram Chamarty | 2dbaa4d12e | |
Sitaram Chamarty | 6328ec2cbe | |
Sitaram Chamarty | 3fe8ecf974 | |
Sitaram Chamarty | 9606e35528 | |
Sitaram Chamarty | 724c741335 | |
Sitaram Chamarty | e59c3ba9f9 | |
Sitaram Chamarty | aec8c71890 | |
Sitaram Chamarty | ed4862ff96 | |
Sitaram Chamarty | cc9727c42b | |
Sitaram Chamarty | 7409635823 | |
Olof Johansson | ba67f6f9ca | |
Nate Jones | 31166e1e1c | |
Sitaram Chamarty | b2a3509e63 | |
Patrick Westerhoff | 57bea39a1e | |
Sitaram Chamarty | f4eb6dcb53 | |
Sitaram Chamarty | 8ad1eee220 | |
Dave Abrahams | d3279e4ad0 | |
Sitaram Chamarty | fd0778e6d6 | |
Sitaram Chamarty | f35db87efc | |
Sitaram Chamarty | f545bc08f6 | |
Sitaram Chamarty | db2cf23379 | |
Sitaram Chamarty | af437c3a7b | |
Sitaram Chamarty | 49580fe4b3 | |
Sitaram Chamarty | 53f9a867df | |
Sitaram Chamarty | 53543ee3e6 | |
Konstantin Gribov | fb9829a698 | |
Sitaram Chamarty | 621815611c | |
Sitaram Chamarty | a454111d32 | |
Frode Rystad | 858f13cf31 | |
Sitaram Chamarty | c9d5a13194 | |
Sitaram Chamarty | 7dcb857347 | |
Sitaram Chamarty | 56d11deb55 | |
Sitaram Chamarty | a171053ab3 | |
Sitaram Chamarty | bcef2be640 | |
Sitaram Chamarty | fbd745958e | |
Sitaram Chamarty | 57f82ee044 | |
Sitaram Chamarty | 4373c5c74c | |
Sitaram Chamarty | 3c0f177481 | |
Sitaram Chamarty | cd37fe7c36 | |
Randal L. Schwartz | 8e15d3a510 | |
milki | 1f6a17c155 | |
Sitaram Chamarty | 5300809103 | |
Sitaram Chamarty | 10cd5b9abe | |
Sitaram Chamarty | 8b78dee18c | |
Sitaram Chamarty | f59ad8cafc | |
Sitaram Chamarty | 17c41ce63b | |
Sitaram Chamarty | ad34cf2856 | |
John Keeping | 4abadc2b54 | |
Sitaram Chamarty | db70595b87 | |
Sitaram Chamarty | 78866f6f28 | |
Sitaram Chamarty | 42e0bac48c | |
Sitaram Chamarty | d9df70a04f | |
Sitaram Chamarty | 7170ad9124 | |
Sitaram Chamarty | a64401bd9a | |
Sitaram Chamarty | 21dbe53d39 | |
Sitaram Chamarty | 06d3398fb0 | |
Sitaram Chamarty | d623388c9f | |
Sitaram Chamarty | 8595303c82 | |
Sitaram Chamarty | 6f740908bb | |
Sitaram Chamarty | 84d123e124 | |
Sitaram Chamarty | 17841e8208 | |
Sitaram Chamarty | e1d9aee98b | |
Sitaram Chamarty | 37e97d29fe | |
Sitaram Chamarty | 0f3a09ce60 | |
Sitaram Chamarty | 04367af3e8 | |
Mike Kelly | 62a66662be | |
Sitaram Chamarty | 75387fd6cb | |
Sitaram Chamarty | 5298a79cb5 | |
Sitaram Chamarty | dd083085cf | |
Sitaram Chamarty | 2907561529 | |
Sitaram Chamarty | 55d64752ae | |
Sitaram Chamarty | b6ce11a19f | |
Sitaram Chamarty | b12a967272 | |
Sitaram Chamarty | d04e79d291 | |
Sitaram Chamarty | 20d2120ea5 | |
Sitaram Chamarty | 3a59f5aff0 | |
Sitaram Chamarty | 8aba6ec2be | |
Sitaram Chamarty | 72b6a54e0a | |
Sitaram Chamarty | 27c0190b76 | |
Sitaram Chamarty | 8644690506 | |
Sitaram Chamarty | 07169c37ec | |
Sitaram Chamarty | bc3eb34211 | |
Sitaram Chamarty | 17a680e0f6 | |
Sitaram Chamarty | e511943a45 | |
Sitaram Chamarty | fa2893be7c | |
Sitaram Chamarty | 699bafa096 | |
Sitaram Chamarty | e76be7ff11 | |
Sitaram Chamarty | 196706c145 | |
Sitaram Chamarty | 6d057fb84c | |
Sitaram Chamarty | d8df4a9344 | |
Andreas Stenius | 47a0c44540 | |
Sitaram Chamarty | 49d132a969 | |
Sitaram Chamarty | c145528849 | |
Sitaram Chamarty | 850882c1a6 | |
Sitaram Chamarty | 88c8d774d0 | |
Thomas Hager | 88b4c86c38 | |
Sitaram Chamarty | 48ed4deb8f | |
Sitaram Chamarty | e919a0b7ca | |
Sitaram Chamarty | 7d6b04605d | |
Sitaram Chamarty | eabcf83dee | |
Sitaram Chamarty | a952f2d627 | |
Sitaram Chamarty | e0ed14172b | |
Sitaram Chamarty | 9e1cb5936c | |
Sitaram Chamarty | 5d366b5c0e | |
Sitaram Chamarty | d74f596e23 | |
Sitaram Chamarty | 198dcfd4c8 | |
Sitaram Chamarty | 895b3614ed | |
Sitaram Chamarty | 6b65e7853f | |
Sitaram Chamarty | 1ad0a761f7 | |
Sitaram Chamarty | c3ec518cef | |
Sitaram Chamarty | cf3dd885fc | |
Thomas Hager | 3a7b547759 | |
Thomas Hager | aaccb367ec | |
Sitaram Chamarty | 51833fccfb | |
Sitaram Chamarty | 2c8e0dfd2f | |
Sitaram Chamarty | 95e6c2ae8b | |
Sitaram Chamarty | 9006b07d2e | |
Sitaram Chamarty | 1dc68b540d | |
Sitaram Chamarty | 1c15b4cc2d | |
Sitaram Chamarty | 2cb7d8313e | |
Sitaram Chamarty | 2629d7f00a | |
Sitaram Chamarty | 581e79d745 | |
Sitaram Chamarty | 67327ebfb4 | |
Sitaram Chamarty | 273e6fd627 | |
Sitaram Chamarty | b5024027ca | |
Sitaram Chamarty | b60dd9c349 | |
Sitaram Chamarty | 04a6f75e5c | |
Sitaram Chamarty | 720729e4b4 | |
Sitaram Chamarty | afc2c14a65 | |
Sitaram Chamarty | 4c5bb27739 | |
Sitaram Chamarty | 8c28fd2241 | |
Sitaram Chamarty | bbaacfaee7 | |
Sitaram Chamarty | c5e0e929a7 | |
Sitaram Chamarty | 48f1e7c781 | |
Sitaram Chamarty | 39fc5b32bb | |
Sitaram Chamarty | 7858beb541 | |
Sitaram Chamarty | 55e9b47bd1 | |
Sitaram Chamarty | d3610191d3 | |
Sitaram Chamarty | 6e5c9954fd | |
Sitaram Chamarty | 057506b73f | |
Sitaram Chamarty | e1c7e546aa | |
Sitaram Chamarty | de40461d9a | |
Sitaram Chamarty | 7c6728af89 | |
Sitaram Chamarty | 9bbc5703e3 | |
Sitaram Chamarty | ecb172b785 | |
Sitaram Chamarty | 8df28a02db | |
Sitaram Chamarty | 495390926d | |
Sitaram Chamarty | 35953a5bd3 | |
Sitaram Chamarty | ad77cef7de | |
Sitaram Chamarty | 81b503d2bd | |
Sitaram Chamarty | 9b3efb9084 | |
gitolite tester | 8e8ccb50f4 | |
gitolite tester | 25bb1c00db | |
Sitaram Chamarty | b78466b164 |
|
@ -0,0 +1,86 @@
|
|||
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.
|
|
@ -0,0 +1,278 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
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.
|
17
INSTALL
17
INSTALL
|
@ -1,17 +0,0 @@
|
|||
1. Clone the repo and copy src somewhere (or leave it where it is, if you're
|
||||
sure no one will 'git pull' on a running system!)
|
||||
|
||||
cp -a src /some/full/path
|
||||
|
||||
2. (Optional) Make a symlink for the single executable 'gitolite' to
|
||||
somewhere in `$PATH`
|
||||
|
||||
ln -sf /full/path/to/some/damn/place/gitolite $HOME/bin
|
||||
|
||||
3. Run setup. That is, either run:
|
||||
|
||||
gitolite setup -a YourName -pk /tmp/YourName.pub
|
||||
|
||||
or, if you did not do step 2, run:
|
||||
|
||||
/some/full/path/src/gitolite -a YourName -pk /tmp/YourName.pub
|
|
@ -0,0 +1,359 @@
|
|||
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/
|
|
@ -38,8 +38,9 @@ sub intro {
|
|||
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 doc/g2migr.mkd");
|
||||
msg( '', => "(online at http://sitaramc.github.com/gitolite/g3/g2migr.html)" );
|
||||
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( '', '' );
|
||||
}
|
||||
|
||||
|
@ -52,16 +53,15 @@ sub rc_basic {
|
|||
}
|
||||
|
||||
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_GET_MEMBERSHIPS_PGM not yet implemented" ) if $GL_GET_MEMBERSHIPS_PGM;
|
||||
msg( SEVERE => "GL_NO_CREATE_REPOS not yet implemented" ) if $GL_NO_CREATE_REPOS;
|
||||
msg( SEVERE => 'htpasswd, rsync, and svnserve not yet implemented' ) if $HTPASSWD_FILE or $RSYNC_BASE or $SVNSERVE;
|
||||
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; many ADCs not yet implemented") if $GL_ADC_PATH;
|
||||
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';
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,9 @@ sub conf {
|
|||
chdir($GL_ADMINDIR);
|
||||
|
||||
my $conf = `find . -name "*.conf" | xargs cat`;
|
||||
msg( "SEVERE", "fallthru in NAME rules; see docs" ) if $conf =~ m(NAME/);
|
||||
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 {
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
#!/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";
|
||||
#}
|
||||
}
|
1450
doc/Markdown.pl
1450
doc/Markdown.pl
File diff suppressed because it is too large
Load Diff
34
doc/add.mkd
34
doc/add.mkd
|
@ -1,34 +0,0 @@
|
|||
# adding users and repos
|
||||
|
||||
Do NOT add repos directly on the server. Clone the 'gitolite-admin' repo to
|
||||
your workstation, make changes to it, then add, commit, and push. When the
|
||||
push hits the server, the server "acts" upon your changes.
|
||||
|
||||
Full documentation on the conf file is [here][conf].
|
||||
|
||||
Here's a sample sequence, on your workstation, after your install is done
|
||||
|
||||
git clone git@host:gitolite-admin
|
||||
cd gitolite-admin
|
||||
vi conf/gitolite.conf
|
||||
|
||||
# now add lines like these:
|
||||
repo foo
|
||||
RW+ = me
|
||||
RW = alice
|
||||
R = wally
|
||||
# now save the file and add it
|
||||
git add conf
|
||||
|
||||
# add a couple of users; get their pubkeys by email or something, then:
|
||||
cp /some/where/alice.pub keydir
|
||||
cp /else/where/wally.pub keydir
|
||||
git add keydir
|
||||
|
||||
# now commit and push
|
||||
git commit -m 'added repo foo'
|
||||
git push
|
||||
|
||||
# at this point gitolite will create the new repo 'foo' (if it did not
|
||||
# already exist) then update the authorized keys file to include alice and
|
||||
# wally's pubkeys
|
31
doc/conf.mkd
31
doc/conf.mkd
|
@ -1,31 +0,0 @@
|
|||
# the gitolite.conf file
|
||||
|
||||
This file is the crux of all of gitolite's access control. The basic syntax
|
||||
is very simple.
|
||||
|
||||
Note: `<user>+` means one or more user or user group names, `<repo>+` means
|
||||
one or more repo or repo group names, and `<refex>*` means zero or more
|
||||
refexes.
|
||||
|
||||
* [group][group] definitions (optional, for convenience)
|
||||
|
||||
@<group> = <user>+
|
||||
@<group> = <repo>+
|
||||
|
||||
* [repo][repo] definitions and access [rules][]
|
||||
|
||||
repo <repo>+
|
||||
<perm> <refex>* = <user>+
|
||||
# one or more such lines
|
||||
|
||||
* [gitolite options][options] that apply to the repo(s) in the last
|
||||
"repo ..." line, for example:
|
||||
|
||||
option deny-rules = 1
|
||||
|
||||
* [git config][git-config] keys and values that also apply to the last named
|
||||
repo(s), for example:
|
||||
|
||||
config hooks.emailprefix = '[%GL_REPO] '
|
||||
|
||||
In addition, you can also have [include][] statements.
|
74
doc/cust.mkd
74
doc/cust.mkd
|
@ -1,74 +0,0 @@
|
|||
# customising gitolite
|
||||
|
||||
Much of gitolite (g3)'s functionality comes from programs and scripts that are
|
||||
not considered "core". This keeps the core simpler, and allows you to enhance
|
||||
gitolite for your own purposes without too much fuss.
|
||||
|
||||
## types of non-core programs
|
||||
|
||||
There are 5 basic types of non-core programs.
|
||||
|
||||
* *commands* can be run from the shell command line. Those listed in the
|
||||
COMMANDS hash of the rc file can also be run remotely.
|
||||
* *hooks* are standard git hooks; see below.
|
||||
* *sugar scripts* change the conf language for your convenience. The word
|
||||
sugar comes from "syntactics sugar".
|
||||
* *triggers* are to gitolite what hooks are to git. I just chose a
|
||||
different name to avoid confusion and constant disambiguation in the docs.
|
||||
* **VREFs** are extensions to the access control check part of gitolite.
|
||||
|
||||
[Here][non-core] is a list of non-core programs shipped with gitolite, with
|
||||
some description of each.
|
||||
|
||||
## #commands gitolite "commands"
|
||||
|
||||
Gitolite comes with several commands that users can run. Remote user run the
|
||||
commands by saying:
|
||||
|
||||
ssh git@host command-name [args...]
|
||||
|
||||
while on the server you can run
|
||||
|
||||
gitolite command [args...]
|
||||
|
||||
Very few commands are designed to be run both ways, but it can be done, by
|
||||
checking for the presence of env var `GL_USER`.
|
||||
|
||||
You can get a **list of available commands** by using the `help` command.
|
||||
Naturally, a remote user will see a much smaller list than the server user.
|
||||
|
||||
You add commands to the "allowed from remote" list by adding its name (or
|
||||
uncommenting it if it's already added but commented out) to the COMMANDS hash
|
||||
in the [rc][] file.
|
||||
|
||||
If you write your own commands, put them in src/commands.
|
||||
|
||||
## #hooks hooks and gitolite
|
||||
|
||||
Gitolite uses the `update` hook for all repos. In addition, it uses the
|
||||
`post-update` hook for the gitolite-admin repo.
|
||||
|
||||
If you want to add your own hook, it's easy as long as it's not the 'update'
|
||||
hook. Just add it to `$HOME/.gitolite/hooks/common` and run `gitolite setup`.
|
||||
|
||||
The rest is between you and 'man githooks' :-)
|
||||
|
||||
## #sugar syntactic sugar
|
||||
|
||||
Sugar scripts help you change the perceived syntax of the conf language. The
|
||||
base syntax of the language is as described [here][conf], so sugar scripts
|
||||
take something *else* and convert it into that.
|
||||
|
||||
That way, the admin sees additional features (like allowing continuation
|
||||
lines), while the parser in the core gitolite engine does not change.
|
||||
|
||||
If you want to write your own sugar scripts, please read the "your own sugar"
|
||||
section in [dev-notes][] first then email me.
|
||||
|
||||
## triggers
|
||||
|
||||
Triggers have their own [document][triggers].
|
||||
|
||||
## VREFs
|
||||
|
||||
VREFs also have their own [document][vref].
|
|
@ -1,118 +0,0 @@
|
|||
# notes for developers
|
||||
|
||||
Gitolite has a huge bunch of existing features that gradually need to moved
|
||||
over. Plus you may want to write your own programs to interact with it.
|
||||
|
||||
Hints for developers wishing to help migrate features over from g2 are
|
||||
[here][dev-hints].
|
||||
|
||||
Here are some random notes on developing hooks, commands, triggers, and sugar
|
||||
scripts.
|
||||
|
||||
## environment variables and other inputs
|
||||
|
||||
In general, the following environment variables should always be available:
|
||||
|
||||
GL_BINDIR
|
||||
GL_REPO_BASE
|
||||
GL_ADMIN_BASE
|
||||
|
||||
Commands invoked by a remote client will also have `GL_USER` set. Hooks will
|
||||
have `GL_REPO` also set.
|
||||
|
||||
Finally, note that triggers get a lot of relevant information as arguments;
|
||||
see [here][triggers] for details.
|
||||
|
||||
## APIs
|
||||
|
||||
### the shell API
|
||||
|
||||
The following commands exist to help you write shell scripts that interact
|
||||
easily with gitolite. Each of them responds to `h` so please run that for
|
||||
more info.
|
||||
|
||||
* `gitolite access` to check access rights given repo, user, type of access
|
||||
(R, W, ...) and refname (optional). Example use: src/commands/desc
|
||||
|
||||
* `gitolite creator` to get/check the creator of a repo. Example use:
|
||||
src/commands/desc
|
||||
|
||||
* `gitolite git-config` to check gitolite options or git config variables
|
||||
directly from gitolite's "compiled output, (i.e., without looking at the
|
||||
actual `repo.git/config` file or using the `git config` command). Example
|
||||
use: none yet
|
||||
|
||||
* `gitolite query-rc` to check the value of an RC variable. Example use:
|
||||
src/commands/desc.
|
||||
|
||||
In addition, you can also look at the comments in src/Gitolite/Easy.pm (the
|
||||
perl API module) for ideas.
|
||||
|
||||
### the perl API
|
||||
|
||||
...is implemented by Gitolite::Easy; the comments in src/Gitolite/Easy.pm
|
||||
serve as documentation.
|
||||
|
||||
## your own hooks
|
||||
|
||||
### anything but the update hook
|
||||
|
||||
If you want to add your own hook, it's easy as long as it's not the 'update'
|
||||
hook. Just add it to `$HOME/.gitolite/hooks/common` and run `gitolite setup`.
|
||||
|
||||
The rest is between you and 'man githooks' :-)
|
||||
|
||||
### update hook
|
||||
|
||||
If you want to add additional `update` hook functionality, do this:
|
||||
|
||||
* write and test your update hook separately from gitolite
|
||||
|
||||
* now add the code to src/VREF. Let's say it is called "foo".
|
||||
|
||||
* to call your new update hook to all accesses for all repos, add this to
|
||||
the end of your conf file:
|
||||
|
||||
repo @all
|
||||
- VREF/foo = @all
|
||||
|
||||
As you probably guessed, you can now make your additional update hooks more
|
||||
selective, applying them only to some repos / users / combinations.
|
||||
|
||||
Note: a normal update hook expects 3 arguments (ref, old SHA, new SHA). A
|
||||
VREF will get those three, followed by at least 4 more. Your VREF should just
|
||||
ignore the extra args.
|
||||
|
||||
## your own commands
|
||||
|
||||
You can add your own commands. You can run them on the server (example,
|
||||
`gitolite access`). Then you can enable certain commands to be allowed to run
|
||||
by a remote user by adding them to the "COMMANDS" hash of the [rc][] file.
|
||||
|
||||
Commands are standalone programs, in any language you like. They simply
|
||||
receive the arguments you append. In addition, the env var `GL_USER` is
|
||||
available if it is being run remotely. src/commands/desc is the best example
|
||||
at present.
|
||||
|
||||
## your own trigger programs
|
||||
|
||||
Trigger programs are just commands whose names have been added to the
|
||||
appropriate list in the [rc][] file. Triggers get specific arguments
|
||||
depending on when they are called; see [here][triggers] for details.
|
||||
|
||||
You can write programs that are both manually runnable as well as callable by
|
||||
trigger events, especially if they don't *need* any arguments.
|
||||
|
||||
Look in the distributed [rc][] file for example programs; at this point there
|
||||
aren't many.
|
||||
|
||||
## your own "sugar"
|
||||
|
||||
Syntactic sugar helpers are NOT complete, standalone, programs. They must
|
||||
include a perl sub called `sugar_script` that takes in a listref, and returns
|
||||
a listref. The listrefs point to a list that contains the entire conf file
|
||||
(with all [include][] processing already done). You create a new list with
|
||||
contents modified as you like and return a ref to it.
|
||||
|
||||
There are a couple of examples in src/syntactic-sugar.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
## #dev-status g3 development status
|
||||
|
||||
Not yet done (will be tackled in this order unless someone asks):
|
||||
|
||||
* detailed documentation for new features
|
||||
* querying the outside world for group info (see gl-get-membership-program
|
||||
in g2)
|
||||
* mirroring
|
||||
* pulling in documentation for things that are the same in g2
|
||||
* "unrestricted arguments" for some ADCs (like git-annexe)
|
||||
* smart http
|
||||
* special features (no-create-repos, shell-access, gl-all-read-all, etc)
|
||||
|
||||
Help needed:
|
||||
|
||||
* I'd like distro packagers to play with it and help with migration advice
|
||||
for distro-upgrades
|
||||
* [rsync][pw2], htpasswd
|
||||
* git-annexe support (but this has a pre-requisite in the previous list)
|
||||
|
||||
Won't be done unless someone asks (saw no evidence that anyone used them in g2
|
||||
anyway!):
|
||||
|
||||
* mob branches
|
||||
* password access
|
||||
* specific ADCs -- there are too many for me to bother without applying
|
||||
Pareto somewhere, so I choose to not do any and wait for people to ask :-)
|
||||
|
||||
Done:
|
||||
|
||||
* core code
|
||||
* test suite
|
||||
* some documentation
|
||||
* distro packaging instructions
|
||||
* migration advice for common cases
|
|
@ -1,89 +0,0 @@
|
|||
# authentication versus authorisation
|
||||
|
||||
This document will explain why an "ssh issue" is almost never a "gitolite
|
||||
issue", and, indirectly, why I dont get too excited about the former.
|
||||
|
||||
Note: for actual ssh troubleshooting see [this][ssh-troubleshooting].
|
||||
|
||||
Here is a fundamental point: <font color="red">**Gitolite does not do
|
||||
authentication. It only does authorisation**.</font>
|
||||
|
||||
So first, let's loosely define these words:
|
||||
|
||||
> **Authentication** is the process of verifying that you are who you claim
|
||||
> to be. An authentication system will establish that I am the user
|
||||
> "sitaram" on my work system. The one behind gmail will similarly
|
||||
> establish that I am "sitaramc". And so on...
|
||||
|
||||
> **Authorisation** is the process of asking what you want to do and
|
||||
> deciding if you're allowed to do it or not.
|
||||
|
||||
Now, if you managed to read about [gitolite and ssh][gitolite-and-ssh], you
|
||||
know that gitolite is meant to be invoked as:
|
||||
|
||||
/full/path/to/gl-auth-command some-authenticated-gitolite-username
|
||||
|
||||
(where the "gitolite username" is a "virtual" username; it does not have to
|
||||
be, and usually *isn't*, an actual *unix* username).
|
||||
|
||||
As you can see, authentication happens before gitolite is called.
|
||||
|
||||
## but... but... you have all that ssh stuff in there!
|
||||
|
||||
The default mode of using gitolite does use ssh keys, but all it's doing is
|
||||
helping you **setup** ssh-based authentication **as a convenience to you**.
|
||||
|
||||
You don't have to use it, though. And many people don't. The examples I know
|
||||
are [smart http][http], and ldap-backed sshd. In both cases, gitolite has no
|
||||
role to play in creating users, setting up their passwords/keys, etc. There's
|
||||
even a `GL_NO_SETUP_AUTHKEYS` option to make sure gitolite doesn't meddle with
|
||||
the authkeys file in such installations.
|
||||
|
||||
## so you're basically saying you won't support "X"
|
||||
|
||||
(where "X" is some ssh related behaviour change or feature)
|
||||
|
||||
Well, if it's not a security issue I *probably* won't. I'm willing to change
|
||||
my mind if enough people convince me they need it. (There's a mailing list if
|
||||
you want to find others who also need the same thing.)
|
||||
|
||||
While we're on the subject, locking someone out is *not* a security issue.
|
||||
Even if you locked yourself (the admin) out, the docs tell you how to recover
|
||||
from such errors. You do need some password based method to get a shell
|
||||
command line on the server, of course.
|
||||
|
||||
## appendix: how to use other authentication systems with gitolite
|
||||
|
||||
The bottom line in terms of how to invoke gitolite has been described above,
|
||||
and as long as you manage to do that gitolite won't even know how the
|
||||
authentication was done. Which in turn means you can use whatever
|
||||
authentication scheme you want.
|
||||
|
||||
It also expects the `SSH_ORIGINAL_COMMAND` environment variable to contain the
|
||||
full command (typically starting with git-receive-pack or git-upload-pack)
|
||||
that the client sent. Also, when using [smart http][http], things are somewhat
|
||||
different: gitolite uses certain environment variables that it expects httpd
|
||||
to have set up. Even the user name comes from the `REMOTE_USER` environment
|
||||
variable instead of as a command line argument in this case.
|
||||
|
||||
However, it has to be an authentication system that is compatible with sshd or
|
||||
httpd in some form. Why? Because the git *client* accessing the server only
|
||||
knows those 2 protocols to "speak git". (Well, the `git://` protocol is
|
||||
unauthenticated, and `file://` doesn't really apply to this discussion, so
|
||||
we're ignoring those).
|
||||
|
||||
For example, let's say you have an LDAP-based authentication system somewhere.
|
||||
It is possible to make apache use that to authenticate users, so when a user
|
||||
accesses a git url using `http://sitaram:password@git.example.com/repo`, it is
|
||||
LDAP that does the actual authentication. [I wouldn't know how to do it but I
|
||||
know it is possible. Patches to this doc explaining how are welcome!]
|
||||
|
||||
There are also ssh daemons that use LDAP to store the authorised keys (instead
|
||||
of putting them all in `~/.ssh/authorized_keys`). The clients will still need
|
||||
to generate keypairs and send them to the admin, but they can be more
|
||||
centrally stored and perhaps used by other programs or tools simultaneously,
|
||||
which can be useful.
|
||||
|
||||
Finally, gitolite allows you to store *group* information externally too. See
|
||||
[here][ldap] for more on this.
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
## #glssh how gitolite uses ssh
|
||||
|
||||
Although other forms of authentications exist (see the document on
|
||||
[authentication versus authorisation][auth]), ssh is the one that most git
|
||||
users use.
|
||||
|
||||
***Therefore, gitolite is (usually) heavily dependent on ssh***.
|
||||
|
||||
Most people didn't realise this, and even if they did they don'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 :-)
|
||||
|
||||
### 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.
|
||||
|
||||
Without this `command=` option, the ssh daemon will simply give you a
|
||||
shell, which is not what we want for our gitolite keys (although we may
|
||||
well have other keys which we use to get a shell).
|
||||
|
||||
**This is the backbone of what makes gitolite work; please make sure you
|
||||
understand this**.
|
||||
|
||||
### 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
|
||||
|
||||
#### 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 that it has a 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.
|
||||
|
||||
#### 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 by 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.
|
|
@ -1,64 +0,0 @@
|
|||
# not a gitolite problem
|
||||
|
||||
These are issues I do not want to be emailed about. That does not mean you
|
||||
cannot get help -- in all cases, you're welcome to ask on [irc or the mailing
|
||||
list][contact]. Irc especially has people with much more patience than I
|
||||
have, God bless them...
|
||||
|
||||
## specific clients, or specific server OSs
|
||||
|
||||
These are things I can not support. That does not mean they will not work
|
||||
with gitolite -- on the contrary, lots of people are using them.
|
||||
|
||||
But I personally don't use them, and I won't use them, and in my admittedly
|
||||
limited experience they have given me good reason to stay well away.
|
||||
|
||||
Please ask for help on the [mailing list or IRC][contact]. Please do not
|
||||
email me directly.
|
||||
|
||||
* putty/plink
|
||||
* jgit/Eclipse
|
||||
* Mac OS client or server
|
||||
* windows as a server
|
||||
* ...probably some more I forgot; will update this list as I remember...
|
||||
|
||||
## ssh
|
||||
|
||||
The *superstar* of the "not a gitolite problem" category is actually ssh.
|
||||
|
||||
Surprised? It is so common that it has [its own document][auth] to tell
|
||||
you why it is *not* a gitolite problem, while [another one][ssh] tries to
|
||||
help you anyway!
|
||||
|
||||
Everything I know is in that latter link. Please email me about ssh ONLY if
|
||||
you find something wrong or missing in those documents.
|
||||
|
||||
## git
|
||||
|
||||
Example 1: when a first `git push` to a new repo fails, it is not because of
|
||||
gitolite, it is because you need to say `git push origin master` or something.
|
||||
This is a git issue.
|
||||
|
||||
There are several such examples. Gitolite is designed to look like just
|
||||
another bare repo server to a client (except requiring public keys -- no
|
||||
passwords allowed). It is *completely transparent* when there is no
|
||||
authorisation failure (i.e., when the access is allowed, the remote client has
|
||||
no way of knowing gitolite was even installed!)
|
||||
|
||||
Even "on disk", apart from reserving the `update` hook for itself, gitolite
|
||||
does nothing to your bare repos unless you tell it to (for example, adding
|
||||
'gitweb.owner' and such to the config file).
|
||||
|
||||
BEFORE you think gitolite is a problem, try the same thing with a normal bare
|
||||
repo. In most cases you can play with it just by doing something like this:
|
||||
|
||||
mkdir /tmp/throwaway
|
||||
cd /tmp/throwaway
|
||||
git clone --mirror <some repo you have a URL for> bare.git
|
||||
git clone bare.git worktree
|
||||
cd worktree
|
||||
<...try stuff>
|
||||
|
||||
----
|
||||
|
||||
In addition, the original nagp has more funny stuff...
|
|
@ -1,34 +0,0 @@
|
|||
# extremely brief regex overview
|
||||
|
||||
Regexes are powerful. Gitolite uses that power as much as it can. If you
|
||||
can't handle that power, hire someone who can and become a manager.
|
||||
|
||||
That said, here's a very quick overview of the highlights.
|
||||
|
||||
`^` and `$` are called "anchors". They anchor the match to the beginning and
|
||||
end of the string respectively.
|
||||
|
||||
^foo matches any string starting with 'foo'
|
||||
foo$ matches any string ending with 'foo'
|
||||
^foo$ matches exact string 'foo'.
|
||||
|
||||
To be precise, the last one is "any string starting and ending with *the same*
|
||||
'foo'". "foofoo" does not match.
|
||||
|
||||
`[0-9]` is an example of a character class; it matches any single digit.
|
||||
`[a-z]` matches any lower case alpha, and `[0-9a-f]` is the range of hex
|
||||
characters. You should now guess what `[a-zA-Z0-9_]` does.
|
||||
|
||||
`.` (the period) is special -- it matches any character. If you want to match
|
||||
an actual period, you need to say `\.`.
|
||||
|
||||
`*`, `?`, and `+` are quantifiers. They apply to the previous token. `a*`
|
||||
means "zero or more 'a' characters". Similarly `a+` means "one or more", and
|
||||
`a?` means "zero or one".
|
||||
|
||||
As a result, `.*` means "any number (including zero) of any character".
|
||||
|
||||
The previous token need not be a single character; you can use parens to make
|
||||
it longer. `(foo)+` matches one or more "foo", (like "foo", "foofoo",
|
||||
"foofoofoo", etc.)
|
||||
|
|
@ -1,427 +0,0 @@
|
|||
## #sts ssh troubleshooting
|
||||
|
||||
**This document must be read in full the first time. If you start from some
|
||||
nice looking section in the middle it may not help you unless you're already
|
||||
an expert at ssh**.
|
||||
|
||||
This document should help you troubleshoot ssh-related problems in installing
|
||||
and accessing gitolite.
|
||||
|
||||
### IMPORTANT -- READ THIS FIRST
|
||||
|
||||
#### caveats
|
||||
|
||||
* Before reading this document, it is **mandatory** to read and **completely
|
||||
understand** [this][ssh], which is a very detailed look at how gitolite
|
||||
uses ssh's features on the server side. Don't assume you know all that;
|
||||
if you knew it, you wouldn't be needing *this* document either!
|
||||
|
||||
* This document, and others linked from this, together comprise 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.
|
||||
|
||||
* Please note that authentication is not really gitolite's job at all. I'd
|
||||
rather spend time on actual gitolite features, code, and documentation
|
||||
than authentication (i.e., ssh, in the common case).
|
||||
|
||||
Surprised? [This][auth] might help explain better.
|
||||
|
||||
#### naming conventions used
|
||||
|
||||
* Your workstation is the **client**. Your userid on the client does not
|
||||
matter, and it has no relation to your gitolite username.
|
||||
|
||||
* the server is called **server** and the "hosting user" is **git**. If
|
||||
this is an RPM/DEB install, the hosting user is probably called
|
||||
"gitolite", however we will use "git" in this document.
|
||||
|
||||
#### taking stock -- relevant files and directories
|
||||
|
||||
* the client has a `~/.ssh` containing a few keypairs. It may also have a
|
||||
`config` file.
|
||||
|
||||
* the client also has a clone of the "gitolite-admin" repo, which contains a
|
||||
bunch of `*.pub` files in `keydir`. We assume this clone is in `$HOME`;
|
||||
if it is not, adjust instructions accordingly when needed.
|
||||
|
||||
* The git user on the server has a `~/.ssh/authorized_keys` file that the
|
||||
ssh daemon uses to authenticate incoming users. We often call this file
|
||||
**authkeys** to save typing, and it always means the one on the server
|
||||
(we're not interested in this file on the client side).
|
||||
|
||||
* the server also has a `~/.gitolite/keydir` which contains a bunch of
|
||||
`*.pub` files.
|
||||
|
||||
#### normal gitolite key handling
|
||||
|
||||
Here's how normal gitolite key handling works:
|
||||
|
||||
* (on client) pub key changes like adding new ones, deleting old ones, etc.,
|
||||
are done in the `keydir` directory in the gitolite-admin repo clone. Then
|
||||
the admin `git add`s and `git commit`s those changes, then `git push`es
|
||||
them to the server.
|
||||
|
||||
* (on server) a successful push from the client makes git invoke the
|
||||
post-update hook in the gitolite-admin repo. This hook is installed by
|
||||
gitolite, and it does a bunch of things which are quite transparent to
|
||||
the admin, but we'll describe briefly here:
|
||||
|
||||
* the pubkey files from this push are checked-out into
|
||||
`~/.gitolite/keydir` (and similarly the config files into
|
||||
`~/.gitolite/conf`)
|
||||
|
||||
* the "compile" script then runs, which uses these files to populate
|
||||
`~/.ssh/authorized_keys` on the server
|
||||
|
||||
The authkeys file may have other, (non-gitolite) keys also. Those
|
||||
lines are preserved. Gitolite only touches lines that are found
|
||||
between gitolite's "marker" lines (`# gitolite start` and `# gitolite
|
||||
end`).
|
||||
|
||||
### (Other resources)
|
||||
|
||||
People who think installing gitolite is too hard should take a look at this
|
||||
[tutorial][tut] to **see how simple it *actually* is**.
|
||||
|
||||
### common ssh problems
|
||||
|
||||
Since I'm pretty sure at least some of you didn't bother to read the
|
||||
"IMPORTANT: PLEASE READ FIRST" section above, let me take a minute to point
|
||||
you there again. Especially the first bullet.
|
||||
|
||||
Done? OK, read on...
|
||||
|
||||
The following problem(s) indicate that pubkey access is not working at all, so
|
||||
you should start with [appendix 1][stsapp1_]. If that doesn't fix the problem, continue
|
||||
with the other appendices in sequence.
|
||||
|
||||
* running any git clone/fetch/ls-remote or just `ssh git@server info` asks
|
||||
you for a password.
|
||||
|
||||
The following problem(s) indicate that your pubkey is bypassing gitolite and
|
||||
going straight to a shell. You should start with [appendix 2][stsapp2_] and continue with
|
||||
the rest in sequence. [Appendix 5][stsapp5_] has some background info.
|
||||
|
||||
* running `ssh git@server info` gets you the output of the GNU 'info'
|
||||
command instead of gitolite's version and access info.
|
||||
|
||||
* running `git clone git@server:repositories/reponame` (note presence of
|
||||
`repositories/` in URL) works.
|
||||
|
||||
[A proper gitolite key will only let you `git clone git@server:reponame`
|
||||
(note absence of `repositories/`)]
|
||||
|
||||
* 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).
|
||||
|
||||
[If you run `git remote -v` you will find that your clone URL included the
|
||||
`repositories/` described above!]
|
||||
|
||||
* conversely, using the correct syntax, `git clone git@server:reponame`
|
||||
(note absence of `repositories/` in the URL), gets you `fatal: 'reponame'
|
||||
does not appear to be a git repository`, and yet you are sure 'reponame'
|
||||
exists, you haven't mis-spelled it, etc.
|
||||
|
||||
### step by step
|
||||
|
||||
Since I'm pretty sure at least some of you didn't bother to read the
|
||||
"IMPORTANT: PLEASE READ FIRST" section above, let me take a minute to point
|
||||
you there again. Especially the first bullet.
|
||||
|
||||
Done? OK, now the general outline for ssh troubleshooting is this:
|
||||
|
||||
* make sure the server's overall setup even *allows* pubkey based login.
|
||||
I.e., check that git fetch/clone/ls-remote commands or a plain `ssh
|
||||
git@server info` do NOT ask for a password. If you do get asked for a
|
||||
password, see [appendix 1][stsapp1_].
|
||||
|
||||
* match client-side pubkeys (`~/.ssh/*.pub`) with the server's authkeys
|
||||
file. To do this, run `sshkeys-lint`, which tells you in detail what key
|
||||
has what access. See [appendix 2][stsapp2_].
|
||||
|
||||
* at this point, we know that we have the right key, and that if sshd
|
||||
receives that key, things will work. But we're not done yet. We still
|
||||
need to make sure that this specific key is being offered/sent by the
|
||||
client, instead of the default key. See [appendix 3][stsapp3_] and [appendix 4][sshhostaliases].
|
||||
|
||||
### random tips, tricks, and notes
|
||||
|
||||
#### 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
|
||||
|
||||
gl-tool add-shell-user ~/foo.pub
|
||||
|
||||
**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.
|
||||
|
||||
#### 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, get a shell on the server and use
|
||||
the program called `gl-admin-push` that comes with gitolite. See instructions
|
||||
[here][adminpush].
|
||||
|
||||
#### 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!)]
|
||||
|
||||
#### problems with using non-openssh public keys
|
||||
|
||||
Gitolite accepts public keys only in openssh format. Trying to use an "ssh2"
|
||||
key (used by proprietary SSH software) results in:
|
||||
|
||||
WARNING: a pubkey file can only have one line (key); ignoring YourName.pub
|
||||
|
||||
To convert ssh2-compatible keys to openssh run:
|
||||
|
||||
ssh-keygen -i -f /tmp/ssh2/YourName.pub > /tmp/openssh/YourName.pub
|
||||
|
||||
then use the resulting pubkey as you normally would in gitolite.
|
||||
|
||||
#### 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.
|
||||
|
||||
Thankfully, someone contributed [contrib/putty.mkd][contrib_putty].
|
||||
|
||||
### #stsapp1_ appendix 1: ssh daemon asks for a password
|
||||
|
||||
> **NOTE**: This section should be useful to anyone trying to get
|
||||
> password-less access working. It is not necessarily specific to gitolite,
|
||||
> so keep that in mind if the wording feels a little more general than you
|
||||
> were expecting.
|
||||
|
||||
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` step 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.
|
||||
|
||||
* 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.
|
||||
|
||||
* you may also want to check `/etc/ssh/sshd_config` to see if the "git" user
|
||||
is allowed to login at all. For example, if that file contains an
|
||||
`AllowUsers` config entry, then only users mentioned in that line are
|
||||
allowed to log in!
|
||||
|
||||
* some OSs/distributions require that the "git" user should have a password
|
||||
and/or not be a locked account. You may want to check that as well.
|
||||
|
||||
* 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.
|
||||
|
||||
### #stsapp2_ appendix 2: which key is which -- running sshkeys-lint
|
||||
|
||||
Follow these steps on the client:
|
||||
|
||||
* get a copy of `~/.ssh/authorized_keys` from the server and put it in
|
||||
`/tmp/foo` or something
|
||||
|
||||
* cd to `~/.ssh`
|
||||
|
||||
* run `sshkeys-lint *.pub < /tmp/foo`
|
||||
|
||||
This tells you, for each pubkey, what type of access (if any) it has to the
|
||||
server.
|
||||
|
||||
Note that it is not trying to log in or anything -- it's just comparing bits
|
||||
of text (the contents of STDIN taken as an authkeys file, and the contents of
|
||||
each of the `*.pub` files one by one).
|
||||
|
||||
> Note: It's also a stand-alone program, so even if your gitolite version is
|
||||
> old, you can safely bring over just this program from a more recent
|
||||
> gitolite and use it, without having to upgrade gitolite itself.
|
||||
|
||||
If the pubkey file you're interested in appears to have the correct access to
|
||||
the server, you're done with this step.
|
||||
|
||||
Otherwise you have to rename some keypairs and try again to get the effect you
|
||||
need. Be careful:
|
||||
|
||||
* do not just rename the ".pub" file; you will have to rename the
|
||||
corresponding private key also (the one with the same basename but without
|
||||
an extension)
|
||||
|
||||
* if you're running ssh-agent, you may have to delete (using `ssh-add -D`)
|
||||
and re-add identities for it to pick up the renamed ones correctly
|
||||
|
||||
#### typical cause(s)
|
||||
|
||||
The admin often has passwordless shell access to `git@server` already, and
|
||||
then used that same key to get access to gitolite (i.e., copied that same
|
||||
pubkey as YourName.pub and ran `gl-setup` on it).
|
||||
|
||||
As a result, 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.
|
||||
|
||||
To fix this, you have to use a different keypair for gitolite access. The
|
||||
best way to do this is to create a new keypair, copy the pubkey to the server
|
||||
as YourName.pub, then run `gl-setup YourName.pub` on the server. Remember to
|
||||
adjust your agent identities using ssh-add -D and ssh-add if you're using
|
||||
ssh-agent, otherwise these new keys may not work.
|
||||
|
||||
### #stsapp3_ appendix 3: ssh client may not be offering the right key
|
||||
|
||||
* make sure the right private key is being offered. Run ssh in very
|
||||
verbose mode and look for the word "Offering", like so:
|
||||
|
||||
ssh -vvv 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 (next bullet). You may also need to
|
||||
create some host aliases in `~/.ssh/config` ([appendix 4][sshhostaliases]).
|
||||
|
||||
* (ssh-agent issues) 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/YourName` and try
|
||||
the access again.
|
||||
|
||||
### F=sshhostaliases appendix 4: host aliases
|
||||
|
||||
(or "making git use the right options for ssh")
|
||||
|
||||
The ssh command has several options for non-default items to be specified.
|
||||
Two common examples are `-p` for the port number if it is not 22, and `-i` for
|
||||
the public key file if you do not want to use just `~/.ssh/id_rsa` or such.
|
||||
|
||||
Git has two ssh-based URL syntaxes, but neither allows specifying a
|
||||
non-default public key file. And a port number is only allowed in one of
|
||||
them. (See `man git-clone` for details). Finally, hosts often have to be
|
||||
referred with IP addresses (such is life), or the name is very long, or hard
|
||||
to remember.
|
||||
|
||||
Using a "host" para in `~/.ssh/config` lets you nicely encapsulate all this
|
||||
within ssh and give it a short, easy-to-remember, name. Example:
|
||||
|
||||
host gitolite
|
||||
user git
|
||||
hostname a.long.server.name.or.annoying.IP.address
|
||||
port 22
|
||||
identityfile ~/.ssh/id_rsa
|
||||
|
||||
Now you can simply use the one word `gitolite` (which is the host alias we
|
||||
defined here) and ssh will infer all those details defined under it -- just
|
||||
say `ssh gitolite` and `git clone gitolite:reponame` and things will work.
|
||||
|
||||
(By the way, the 'port' and 'identityfile' lines are needed only if you have
|
||||
non-default values, although I put them in anyway just to be complete).
|
||||
|
||||
If you have *more than one* pubkey with access to the *same* server, you
|
||||
**must** use this method to make git pick up the right key. There is no other
|
||||
way to do this, as far as I know.
|
||||
|
||||
[tut]: http://sites.google.com/site/senawario/home/gitolite-tutorial
|
||||
|
||||
### #stsapp5_ appendix 5: why bypassing gitolite causes a problem
|
||||
|
||||
When you bypass gitolite, you end up running your normal shell instead of the
|
||||
special gitolite entry point script `gl-auth-command`.
|
||||
|
||||
This means commands (like 'info') are interpreted by the shell instead of
|
||||
gitolite.
|
||||
|
||||
It also means git operations look for repos in `$HOME`.
|
||||
|
||||
However, gitolite places all your repos in a subdirectory pointed to by
|
||||
`$REPO_BASE` in the rc file (default: `repositories`), and internally prefixes
|
||||
this before calling the actual git command you invoked. Thus, the pathname of
|
||||
the repo that you use on the client is almost never the correct pathname on
|
||||
the server. (This is by design. Don't argue...)
|
||||
|
||||
This means that, you get 2 kinds of errors if you bypass gitolite
|
||||
|
||||
* when you use `git@server:reponame` with a key that bypasses gitolite
|
||||
(i.e., gets you a shell), this prefixing does not happen, and so the repo
|
||||
is not found. Neither a clone/fetch nor a push will work.
|
||||
|
||||
* conversely, consider `git@server:repositories/reponame.git`. The clone
|
||||
operation will work -- you're using the full Unix path, (assuming default
|
||||
`$REPO_BASE` setting), and so the shell finds the repo where you said it
|
||||
would be. However, 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.
|
|
@ -1,11 +0,0 @@
|
|||
# ssh
|
||||
|
||||
There are two documents you need to read, in order:
|
||||
|
||||
* [gitolite and ssh][glssh] -- this explains how gitolite uses openssh's
|
||||
features to provide any number of virtual users over just one actual
|
||||
(unix) user, and so on
|
||||
|
||||
* [ssh troubleshooting][sts] -- this is a rather long document but as far
|
||||
as I know almost every known ssh related issue is in here. If you find
|
||||
something missing, send me an email with details.
|
|
@ -1,6 +0,0 @@
|
|||
# unique setups
|
||||
|
||||
This page is for unique setups that I support. At present there is only one
|
||||
-- Fedora.
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
## #g2alt alternate implementations for some g2 features
|
||||
|
||||
The following g2 features have been dropped but suitable (or better)
|
||||
alternatives exist.
|
||||
|
||||
### gl-time for performance measurement
|
||||
|
||||
Take a look at the 'cpu-time' program that you can set to run from the
|
||||
`POST_GIT` trigger. Just set it to run as the last program in that sequence
|
||||
so it covers all previous programs.
|
|
@ -1,4 +0,0 @@
|
|||
## #g2dropped g2 features dropped
|
||||
|
||||
(none yet that are not already covered in the rc section in [this][g2migr]
|
||||
page).
|
|
@ -1,41 +0,0 @@
|
|||
## #g2incompat incompatibility with g2
|
||||
|
||||
(other than in the rc file, which is dealt with [elsewhere][g2rcdiff])
|
||||
|
||||
The following incompatibilities exist, in vaguely decreasing order of
|
||||
severity. **The ones in the first section are IMPORTANT because they allow
|
||||
access that was previously not allowed -- please fix your config before using
|
||||
the new gitolite!**
|
||||
|
||||
### fallthru in NAME rules
|
||||
|
||||
Fallthru on all VREFs is "success" now, so any NAME/ rules you have **MUST**
|
||||
change the ruleset in some way to maintain the same restrictions. The
|
||||
simplest is to add the following line to the end of each repo's rule list:
|
||||
|
||||
- NAME/ = @all
|
||||
|
||||
### subconf command in admin repo
|
||||
|
||||
(This is also affected by the previous issue, 'fallthru in NAME rules'; please
|
||||
read that as well).
|
||||
|
||||
If you're using delegation in your admin conf setup, please add the following
|
||||
lines to the end of the gitolite-admin rules in your conf/gitolite.conf file:
|
||||
|
||||
repo gitolite-admin
|
||||
- NAME/ = @all
|
||||
|
||||
subconf "fragments/*.conf"
|
||||
|
||||
The first part compensates for fallthru now being a success when processing
|
||||
[VREF][vref] rules (NAME rules are just one specific VREF). Although,
|
||||
**ideally**, you should change your ruleset so that you no longer require that
|
||||
line. As the [vref documentation][vref] says:
|
||||
|
||||
> **Virtual refs are best used as additional "deny" rules**, performing
|
||||
> extra checks that core gitolite cannot.
|
||||
|
||||
The second part explicitly says when and where to include the subconf files.
|
||||
(Before subconf was invented, this used to happen implicitly at the end of the
|
||||
main conf file, and was hardcoded to that specific glob.)
|
107
doc/g2migr.mkd
107
doc/g2migr.mkd
|
@ -1,107 +0,0 @@
|
|||
## #g2migr migrating from g2
|
||||
|
||||
<font color="red">
|
||||
|
||||
**This document is a *MUST* read if you are currently using g2 and want to
|
||||
move to g3.**
|
||||
|
||||
</font>
|
||||
|
||||
First things first: g2 will be supported for a good long time. My current
|
||||
*expert* users do not cause me any load anyway.
|
||||
|
||||
Migration should be straightforward, but it is not automatic. You should run
|
||||
the "check-g2-compat" program first, to see any *major* differences that
|
||||
affect you. The bulk of the changes are in the RC file, which must be
|
||||
manually handled (links below). The conf files have very few changes -- they
|
||||
apply only if you use "NAME/" or delegation.
|
||||
|
||||
You must first read about [incompatible][g2incompat] features and
|
||||
[dropped][g2dropped] features. Some features have been replaced with
|
||||
[alternatives][g2alt].
|
||||
|
||||
Since the majority of changes are in the rc file, they're all listed
|
||||
[here][g2rcdiff].
|
||||
|
||||
The rest of this page describes the completely standalone "check-g2-compat"
|
||||
script that you can find in the repo root (i.e., not in "src/").
|
||||
|
||||
### the "check-g2-compat" program
|
||||
|
||||
This program checks a few things only, not everything. In particular, it
|
||||
looks for settings and status that might:
|
||||
|
||||
* make g3 unusable for lots of users
|
||||
* make g3 give *more* access than g2 under some conditions.
|
||||
|
||||
It does NOT look for or warn about anything else; you're expected to read (and
|
||||
act upon, if needed) the rest of the migration guide links given a few paras
|
||||
above to cover everything else.
|
||||
|
||||
Here's an explanation of those messages that the check-g2-compat program may
|
||||
put that contain the words "see docs":
|
||||
|
||||
* `GL_ADMINDIR in the wrong place -- aborting`
|
||||
|
||||
It expects to find `GL_ADMINDIR` and `REPO_BASE` pointing to the right
|
||||
places. It aborts if these conditions are not met and does not scan
|
||||
further since that sort of guesswork is not good. If you are in that
|
||||
position, make a symlink from the real location to the expected location,
|
||||
change the RC accordingly, and re-try.
|
||||
|
||||
* `REPO_BASE in the wrong place -- aborting`
|
||||
|
||||
same as above
|
||||
|
||||
* `fallthru in NAME rules`
|
||||
|
||||
**This is a significant difference and affects access badly (gives access
|
||||
that would otherwise not be given)**. Please see the [list of non-RC
|
||||
incompatibilities][g2incompat].
|
||||
|
||||
* `subconf command in admin repo`
|
||||
|
||||
This is not so bad security wise but it might *reduce* access by not
|
||||
processing files you intended to. Again, see the same link as in the
|
||||
previous bullet.
|
||||
|
||||
* `found N gl-creater files`
|
||||
|
||||
These need to be renamed to `gl-creator` (the correct spelling at last,
|
||||
hooray!). Suggested command sequence:
|
||||
|
||||
cd $HOME/repositories
|
||||
find . -type d -name "*.git" -prune | while read r
|
||||
do
|
||||
mv $r/gl-creater $r/gl-creator
|
||||
done 2>/dev/null
|
||||
|
||||
Once you do this, the g2 will not work completely unless you change them
|
||||
back.
|
||||
|
||||
* `found N gl-perms files with R or RW`
|
||||
|
||||
Setting perms of R and RW will no longer work; you have to say READERS and
|
||||
WRITERS now. Suggested command:
|
||||
|
||||
The following variables need to be [preset][rc-preset] in the rc file
|
||||
**before** running `gitolite setup`. Otherwise the default actions will
|
||||
clobber something and require some recovery.
|
||||
|
||||
* `GL_NO_SETUP_AUTHKEYS` (default will clobber your authkeys file)
|
||||
|
||||
* `GL_NO_DAEMON_NO_GITWEB` (default will clobber your projects.list file and
|
||||
git-daemon-export-ok files)
|
||||
|
||||
* `UPDATE_CHAINS_TO` (default will fail to run this extra check when users
|
||||
push)
|
||||
|
||||
* `ADMIN_POST_UPDATE_CHAINS_TO` (severity depends on what your code is
|
||||
doing; see [g2rcdiff][] for how to fix this)
|
||||
|
||||
* `GL_ALL_INCLUDES_SPECIAL` (default will allow gitweb and daemon to be able
|
||||
to read any repos that have `R = @all`)
|
||||
|
||||
* `GIT_PATH` (presumably your git is in some non-std path so unless you
|
||||
preset `$ENV{PATH}` per instructions in the [rc file
|
||||
differences][g2rcdiff] doc, nothing will work).
|
125
doc/g2rcdiff.mkd
125
doc/g2rcdiff.mkd
|
@ -1,125 +0,0 @@
|
|||
## #g2rcdiff rc file differences between g2 and g3
|
||||
|
||||
The new rc file has far fewer variables; many have been dropped. You should
|
||||
not see much ill effect though, but please read below.
|
||||
|
||||
### #rc-preset pre-setting the rc file
|
||||
|
||||
Some of these settings are such that you cannot directly run `gitolite setup`
|
||||
when you're ready to migrate. Instead, you need to run
|
||||
|
||||
# (assuming you saved your g2 rc file somewhere)
|
||||
gitolite print-default-rc > $HOME/.gitolite.rc
|
||||
$EDITOR $HOME/.gitolite.rc
|
||||
# make appropriate changes (see below), save
|
||||
gitolite setup
|
||||
|
||||
The most serious example of this is `GL_NO_SETUP_AUTHKEYS`. If you don't
|
||||
preset the rc (in this case, by commenting out the 'ssh-authkeys' line)
|
||||
**before** running `gitolite setup`, your `~/.ssh/authorized_keys` file will
|
||||
get clobbered.
|
||||
|
||||
There are several other settings that you may want to look at, with varying
|
||||
degrees of severity; **please go through the list below before blindly running
|
||||
`gitolite setup`**. (For example, if you had `GL_NO_DAEMON_NO_GITWEB` in g2
|
||||
but forgot to comment out all the gitweb and daemon update lines in the rc
|
||||
file, your projects.list and git-daemon-export-ok files might get clobbered.
|
||||
Of course this is less severe than ssh authkeys but still...).
|
||||
|
||||
### rc file differences
|
||||
|
||||
**DROPPED** variables (possible high impact): these could be show-stoppers for
|
||||
migration, at least for now.
|
||||
|
||||
* `BIG_INFO_CAP` -- if you think you must have this, try it without and see
|
||||
if there's a difference. If you *know* you need this, convince me.
|
||||
|
||||
* `GL_ALL_READ_ALL` -- same
|
||||
|
||||
* `GL_NO_CREATE_REPOS` -- if you think you need this, email me. I know one
|
||||
group who does need this so I will be putting it in eventually but not
|
||||
right away.
|
||||
|
||||
* `HTPASSWD_FILE`, `RSYNC_BASE`, `SVNSERVE` -- need work. Email me if you
|
||||
are using any of these.
|
||||
|
||||
* `GL_GET_MEMBERSHIPS_PGM` -- is high on my todo list
|
||||
|
||||
* `GL_LOGT` -- is now fixed; you can't change it. Email me if this is a
|
||||
problem.
|
||||
|
||||
**DROPPED** variables (medium impact): these have alternative implementations
|
||||
or mechanisms, but you have to do some setup work.
|
||||
|
||||
* `GL_ADMINDIR` -- this is now at a fixed location: `~/.gitolite`. If you
|
||||
want it somewhere else go ahead and move it, then place a symlink from the
|
||||
assumed location to the real one.
|
||||
|
||||
* `REPO_BASE` -- this is now at a fixed location: `~/repositories`. If you
|
||||
want it somewhere else go ahead and move it, then place a symlink from the
|
||||
assumed location to the real one.
|
||||
|
||||
* `PROJECTS_LIST` -- it's called `GITWEB_PROJECTS_LIST` now, but more
|
||||
importantly, it is only used by update-gitweb-access-list in
|
||||
src/commands/post-compile. This variable now has nothing to do with
|
||||
gitolite core, and the rc is just helping to store settings for external
|
||||
programs like that one.
|
||||
|
||||
`WEB_INTERFACE` and `GITWEB_URI_ESCAPE` are also gone; patches to the
|
||||
update program to directly do those things are welcome. Personally, I
|
||||
think people who use spaces and other funky characters in dir/file names
|
||||
should be shot but luckily no one listens to me :-)
|
||||
|
||||
* `GL_NO_DAEMON_NO_GITWEB` -- uncomment the appropriate lines in the rc
|
||||
file, in both the `POST_COMPILE` and `POST_CREATE` trigger sections.
|
||||
|
||||
* `NICE_VALUE` -- uncomment the 'renice 10' line in the rc file. You can
|
||||
also change the 10 to something else if you wish.
|
||||
|
||||
* `GIT_PATH` -- gone, not needed. Just add these lines to the end of the rc
|
||||
file:
|
||||
|
||||
$ENV{PATH}="...whatever you want...";
|
||||
1;
|
||||
|
||||
* `GL_NO_SETUP_AUTHKEYS` -- comment out the lines that call ssh-authkeys, in
|
||||
the rc file.
|
||||
|
||||
* `GL_WILDREPOS_DEFPERMS` -- if you need this, add a `POST_CREATE` script
|
||||
that does it. Or email me and I will write it for you.
|
||||
|
||||
* `UPDATE_CHAINS_TO` -- use a [vref][] instead. You can directly use the
|
||||
chained-to script as a VREF; it'll work.
|
||||
|
||||
* `ADMIN_POST_UPDATE_CHAINS_TO` -- add your script to the `POST_COMPILE`
|
||||
trigger chain. You won't be getting any arguments but for the admin repo
|
||||
the only argument that ever comes in is "refs/heads/master" anyway.
|
||||
|
||||
* `GL_ADC_PATH` -- obsolete; use [commands][] or add [your own][dev-notes].
|
||||
|
||||
* `GL_ALL_INCLUDES_SPECIAL` -- obsolete; @all always includes gitweb and
|
||||
daemon now. Use [deny-rules][] if you want to say `R = @all` but not have
|
||||
it be visible to gitweb or daemon.
|
||||
|
||||
* `GL_PERFLOGT` -- see the entry for "gl-time" in the [alternative
|
||||
implementations][g2alt] page.
|
||||
|
||||
**DROPPED** variables (no impact/low impact): these variables should not
|
||||
actually affect anything anyway, so even if you had them set you should not
|
||||
feel their loss.
|
||||
|
||||
* `GL_CONF`, `GL_KEYDIR`, and `GL_CONF_COMPILED` -- you had no business
|
||||
touching these anyway; if you did, move them into the expected default
|
||||
locations before attempting to run `gitolite setup`
|
||||
* `GL_PACKAGE_HOOKS` -- not needed anymore, but check if you had any custom
|
||||
hooks set there and copy them across.
|
||||
* `GL_WILDREPOS` -- dropped; this feature is default now.
|
||||
* `GL_BIG_CONFIG` -- dropped; this feature is default now.
|
||||
|
||||
**RENAMED** variables (no impact): these are functionally the same but are
|
||||
renamed.
|
||||
|
||||
* `REPO_UMASK` is now `UMASK`
|
||||
* `GL_GITCONFIG_KEYS` is now `GITCONFIG_KEYS`
|
||||
* `GL_WILDREPOS_PERM_CATS` is now the ROLES hash in the rc file
|
||||
* `GL_SITE_INFO` is now `SITE_INFO`
|
|
@ -1,97 +0,0 @@
|
|||
# why a completely new version?
|
||||
|
||||
Gitolite started life as 400 lines of perl written on a weekend because I was
|
||||
quickly losing control of my projects at work, exacerbated by git newbies
|
||||
doing all the wrong things. I really needed it!
|
||||
|
||||
That little 400 line thing is now a huge bunch of programs that do all sorts
|
||||
of things (I mean, rsync access control in a git related program? WTF!),
|
||||
because it kinda just *grew* while I wasn't looking.
|
||||
|
||||
So, briefly, here are the advantages of g3:
|
||||
|
||||
* compile versus everything else
|
||||
|
||||
g2's "compile" script was doing way, way too much. For example, dealing
|
||||
with gitweb and git-daemon was a good chunk of code in g2. In contrast,
|
||||
here's how g3 generates gitweb's projects.list file:
|
||||
|
||||
(
|
||||
gitolite list-phy-repos | gitolite access % gitweb R any | grep -v DENIED
|
||||
gitolite list-phy-repos | gitolite git-config -r % gitweb\\.
|
||||
) |
|
||||
cut -f1 | sort -u | sed -e 's/$/.git/' > $plf
|
||||
|
||||
* core versus non-core
|
||||
|
||||
That's just the tip of the iceberg. The commands above run from a script
|
||||
that is itself outside gitolite, and can be enabled and disabled from the
|
||||
rc file. There are six different "events" within gitolite that can
|
||||
trigger external programs, with specific arguments passed to them, much
|
||||
like git's own hooks. The example you saw is called from the
|
||||
"POST_COMPILE" trigger.
|
||||
|
||||
And as you can see, these programs be in any language.
|
||||
|
||||
* get/set perms/desc, and ADCs
|
||||
|
||||
I've always wanted to kick setperms out of core and make it an ADC.
|
||||
Sadly, it couldn't be done because when you update your repo's permissions
|
||||
using setperms, that can affect gitweb/daemon access, which -- you guessed
|
||||
right -- feeds back into the main code in complex ways. It *had* to be an
|
||||
"inside job".
|
||||
|
||||
But now, the new 'perms' program is quite external to gitolite. And how
|
||||
does it fix up gitweb/daemon permissions after it is done updating the
|
||||
"gl-perms" file?
|
||||
|
||||
system("gitolite", "trigger", "POST_CREATE");
|
||||
|
||||
* syntax versus semantics
|
||||
|
||||
I got tired of people asking things like "why can't I have
|
||||
backslash-escaped continuation lines?" I designed it differently because
|
||||
I don't like them but perhaps it's reasonable for some people.
|
||||
|
||||
Someone else wanted to use subdirectories of 'keydir' as group names. Why
|
||||
not?
|
||||
|
||||
G3 comes with a stackable set of "syntactic sugar" helpers. And you can
|
||||
write your own, though they do have to be in perl (because they're not
|
||||
standalone programs).
|
||||
|
||||
Once the code is written and placed in the right place, all a site has to
|
||||
do to enable it is to uncomment some lines in the rc file:
|
||||
|
||||
# these will run in sequence during the conf file parse
|
||||
SYNTACTIC_SUGAR =>
|
||||
[
|
||||
# 'continuation-lines',
|
||||
# 'keysubdirs-as-groups',
|
||||
<etc>
|
||||
|
||||
* roll your own
|
||||
|
||||
Having a decent shell API helps enormously. You saw an example above but
|
||||
how about if your boss asks you "I need a list of everyone who *currently*
|
||||
has read access to the 'foo' repo"?
|
||||
|
||||
Sure you could look in conf/gitolite.conf, all its include files (if you
|
||||
have any), and if the repo is user-created, then in its gl-perms.
|
||||
|
||||
Or you could do something like this:
|
||||
|
||||
gitolite list-users | gitolite access foo % R any | cut -f1
|
||||
|
||||
* over-engineered
|
||||
|
||||
g2 was, to some extent, over-engineered. One of the best examples is the
|
||||
documentation on hook-propagation in g2, which required even a *picture*
|
||||
to make clear (always a bad sign). In g3, the [hooks][] section is 4
|
||||
sentences.
|
||||
|
||||
Anyway you get the idea.
|
||||
|
||||
The point is not that you can do all these cool tricks. The point is they are
|
||||
possible because of the redesign. There is no way on God's green earth I
|
||||
could have done this with the old code.
|
|
@ -1,32 +0,0 @@
|
|||
# parts of the conf file
|
||||
|
||||
## #group group definitions
|
||||
|
||||
You can group repos or users for convenience. The syntax is the same for both
|
||||
and does not distinguish; until you *use* the group name it could really be
|
||||
either.
|
||||
|
||||
Here's an example:
|
||||
|
||||
@developers = dilbert alice wally
|
||||
|
||||
Group definitions accumulate; this is the same as the above:
|
||||
|
||||
@developers = dilbert
|
||||
@developers = alice
|
||||
@developers = wally
|
||||
|
||||
You can use one group in another group definition; the values will be expanded
|
||||
right there (meaning later additions will not appear in the second group).
|
||||
|
||||
@developers = dilbert alice
|
||||
@interns = ashok
|
||||
@staff = @interns @developers
|
||||
@developers = wally
|
||||
|
||||
# wally is NOT part of @staff
|
||||
|
||||
### special group `@all`
|
||||
|
||||
`@all` is a special group name that is often convenient to use if you really
|
||||
mean "all repos" or "all users".
|
|
@ -1,67 +0,0 @@
|
|||
# Hosting git repositories
|
||||
|
||||
Gitolite allows you to setup git hosting on a central server, with
|
||||
fine-grained access control and many more powerful features.
|
||||
|
||||
Here's more on [what][] it is and [why][] you might need it.
|
||||
|
||||
For current gitolite (call it "g2" for convenience) users,
|
||||
|
||||
* [Why][g3why] I rewrote gitolite.
|
||||
* Development [status][dev-status] (**should change often for a while**)
|
||||
* Specific migration [issues and steps][g2migr].
|
||||
|
||||
Quick links:
|
||||
|
||||
* [Minimum requirements][minreq].
|
||||
* Here's how to [get started][qi] installing and setting it up
|
||||
* Don't know ssh well enough? [Learn][ssh]. It's **IMPORTANT**.
|
||||
* [Add users and repos][add].
|
||||
* Learn about fine-grained access control with the [conf][] file
|
||||
* Explain gitolite to your [users][].
|
||||
|
||||
Not so "quick" links:
|
||||
|
||||
* The "master table of contents" link at the top of each page is the
|
||||
**first** place you should check when looking for anything.
|
||||
|
||||
* Want to do more than just add users and repos and control their access?
|
||||
Here's how to [customise][cust] your server installation.
|
||||
|
||||
Additional reading for Unix newbies:
|
||||
|
||||
* [Regular Expressions][regex]
|
||||
|
||||
## #what What is gitolite?
|
||||
|
||||
Gitolite is an access control layer on top of git. Here are the features that
|
||||
most people see:
|
||||
|
||||
* use a single unix user ("real" user) on the server
|
||||
* provide access to many gitolite users
|
||||
* they are not "real" users
|
||||
* they do not get shell access
|
||||
* control access to many git repositories
|
||||
* read access controlled at the repo level
|
||||
* write access controlled at the branch/tag/file/directory level,
|
||||
including who can rewind, create, and delete branches/tags
|
||||
* can be installed without root access, assuming git and perl are already
|
||||
installed
|
||||
* authentication is most commonly done using sshd, but you can also use
|
||||
httpd if you prefer (this may require root access).
|
||||
|
||||
## #contact contact
|
||||
|
||||
* author: sitaramc@gmail.com, sitaram@atc.tcs.com
|
||||
* mailing list: gitolite@googlegroups.com
|
||||
* list subscribe address : gitolite+subscribe@googlegroups.com
|
||||
* IRC: #git and #gitolite on freenode. Note that I live in India (UTC+0530
|
||||
time zone).
|
||||
|
||||
## #license license
|
||||
|
||||
The gitolite *code* is released under GPL v2. See COPYING for details.
|
||||
|
||||
The gitolite documentation is provided under a [Creative Commons
|
||||
Attribution-NonCommercial-ShareAlike 3.0 Unported
|
||||
License](http://creativecommons.org/licenses/by-nc-sa/3.0/).
|
|
@ -1,63 +0,0 @@
|
|||
# different ways to install gitolite
|
||||
|
||||
Gitolite has only one server side "command" now, much like git itself. And
|
||||
it's been designed so that you don't even really have to *install* it, as you
|
||||
will see.
|
||||
|
||||
**NOTE**: if you're migrating from g2, there are some settings that MUST be
|
||||
dealt with **before** running `gitolite setup`; please read the [g2
|
||||
migration][g2migr] page and linked pages, and especially the one on
|
||||
'presetting the rc file][rc-preset].
|
||||
|
||||
## simplest
|
||||
|
||||
1. Put all of `src` in one place, doesn't matter where; let's call it
|
||||
/foo/bar.
|
||||
|
||||
2. Use the full path to run any gitolite commands, for example:
|
||||
|
||||
/foo/bar/gitolite setup -pk sitaram.pub
|
||||
|
||||
## almost as simple
|
||||
|
||||
1. (same as above)
|
||||
|
||||
2. Symlink /foo/bar/gitolite to some directory that is on your PATH. For
|
||||
example:
|
||||
|
||||
ln -sf /foo/bar/gitolite ~/bin
|
||||
|
||||
Now you can just say
|
||||
|
||||
gitolite setup -pk sitaram.pub
|
||||
|
||||
## packagers
|
||||
|
||||
1. Put src/Gitolite in `/usr/share/perl5/vendor_perl` or some such place.
|
||||
|
||||
2. Put the rest of src anywhere your distro policy allows. (Fedora keeps
|
||||
git's 150 executables in /usr/libexec/git-core, so maybe
|
||||
/usr/libexec/gitolite?)
|
||||
|
||||
3. Symlink 'gitolite' to /usr/bin or something, similar to step 2 above,
|
||||
|
||||
OR
|
||||
|
||||
Put it directly in /usr/bin, and hardcode `GL_BINDIR` into it to tell it
|
||||
where the others are. I'd prefer it if you did not do this but you can.
|
||||
|
||||
----
|
||||
|
||||
Bottom line:
|
||||
|
||||
* `GL_BINDIR` must point to a place that contains `commands`, `VREF`, and
|
||||
`syntactic-sugar` (so they must all be sibling directories).
|
||||
* The `Gitolite` directory can also be there, or it can be anywhere in
|
||||
perl's `@INC` path.
|
||||
|
||||
## upgrading
|
||||
|
||||
Just put the new version on top of wherever you kept the old one. That's it.
|
||||
|
||||
If you feel it should require a little more effort, pretend I said "you have
|
||||
to then run `gitolite setup`". Can't hurt...
|
45
doc/list
45
doc/list
|
@ -1,45 +0,0 @@
|
|||
|
||||
index.mkd
|
||||
why.mkd
|
||||
|
||||
g3why.mkd
|
||||
dev-status.mkd
|
||||
g2migr.mkd
|
||||
g2rcdiff.mkd
|
||||
g2incompat.mkd
|
||||
g2dropped.mkd
|
||||
g2alt.mkd
|
||||
|
||||
minreq.mkd
|
||||
qi.mkd
|
||||
install.mkd
|
||||
add.mkd
|
||||
users.mkd
|
||||
|
||||
conf.mkd
|
||||
|
||||
group.mkd
|
||||
repo.mkd
|
||||
rules.mkd
|
||||
refex.mkd
|
||||
write-types.mkd
|
||||
|
||||
rc.mkd
|
||||
|
||||
cust.mkd
|
||||
triggers.mkd
|
||||
vref.mkd
|
||||
|
||||
non-core.mkd
|
||||
|
||||
dev-notes.mkd
|
||||
|
||||
misc.mkd
|
||||
pw.mkd
|
||||
testing.mkd
|
||||
extras/auth.mkd
|
||||
extras/nagp.mkd
|
||||
extras/regex.mkd
|
||||
extras/ssh.mkd
|
||||
extras/gitolite-and-ssh.mkd
|
||||
extras/ssh-troubleshooting.mkd
|
|
@ -1,32 +0,0 @@
|
|||
# minimum requirements for gitolite
|
||||
|
||||
**Client**:
|
||||
|
||||
* git 1.6.6 or greater
|
||||
* an ssh client that can talk to an openssh server, and can generate keys in
|
||||
openssh's default format (the pubkey is just one long line). Gitolite
|
||||
will [not currently][pw1] convert such keys.
|
||||
|
||||
For people still using Windows, msysgit works fine. If you're using
|
||||
[putty/plink][ens], God bless you. (It'll work, but I still want him to bless
|
||||
you).
|
||||
|
||||
TODO: when smart http support works, ssh will no longer be a *requirement*,
|
||||
merely a *strong* suggestion :-)
|
||||
|
||||
**Server**
|
||||
|
||||
* git 1.6.6 or greater
|
||||
* perl 5.8.8 or greater
|
||||
* an ssh server compatible with openssh, especially it's authorized keys
|
||||
file format and features.
|
||||
* any Unix or Unix like OS. That said, I've occasionally had some weird
|
||||
reports from [Mac OSX servers][ens]; good luck.
|
||||
* a single, dedicated, userid to host it (usually 'git' or 'gitolite').
|
||||
|
||||
These version numbers are subject to fine-tuning as I get feedback and make
|
||||
fixes where possible and needed.
|
||||
|
||||
Sshd must be configured so that each users authkeys file is in the user's
|
||||
`$HOME`, inside `.ssh/authorized_keys`, and not in some central
|
||||
/var/something.
|
132
doc/misc.mkd
132
doc/misc.mkd
|
@ -1,132 +0,0 @@
|
|||
# odds and ends
|
||||
|
||||
Most of these items don't fit anywhere or fit in more than one place or are of
|
||||
the nature of background information.
|
||||
|
||||
## #include include files
|
||||
|
||||
Gitolite allows you to break up the configuration into multiple files and
|
||||
include them in the main file for convenience.
|
||||
|
||||
include "foo.conf"
|
||||
|
||||
will include the contents of the file "foo.conf" from the "conf" directory.
|
||||
|
||||
Details:
|
||||
|
||||
* You can also use a glob (`include "*.conf"`), or put your include files
|
||||
into subdirectories of "conf" (`include "foo/bar.conf"`), or both
|
||||
(`include "repos/*.conf"`).
|
||||
|
||||
* Included files are always searched relative to the gitolite-admin repo's
|
||||
"conf/" directory.
|
||||
|
||||
* If you ended up recursing, files that have been already processed once are
|
||||
skipped, with a warning.
|
||||
|
||||
<font color="gray">Advanced users: `subconf`, a command that is very closely
|
||||
related to `include`, is documented [here][subconf].</font>
|
||||
|
||||
## #deny-rules applying deny rules at the pre-git access check
|
||||
|
||||
The access [rules][] rules section describes the problem. To recap, you want
|
||||
this:
|
||||
|
||||
@staff = alice bob wally ashok
|
||||
|
||||
repo foo
|
||||
RW+ = alice # line 1
|
||||
RW+ dev = bob # line 2
|
||||
- = wally # line 3
|
||||
RW temp/ = @staff # line 4
|
||||
|
||||
to deny Wally even *read* access.
|
||||
|
||||
The way to do this is to add this line to the repo:
|
||||
|
||||
option deny-rules = 1
|
||||
|
||||
If you want this for all your repos, just add this somewhere at the top of
|
||||
your conf file
|
||||
|
||||
repo @all
|
||||
option deny-rules = 1
|
||||
|
||||
## #rule-accum rule accumulation
|
||||
|
||||
Gitolite was meant to collect rules from multiple places and apply them all.
|
||||
For example, this:
|
||||
|
||||
repo foo
|
||||
RW = u1
|
||||
|
||||
@gr1 = foo bar
|
||||
|
||||
repo @gr1
|
||||
RW = u2
|
||||
R = u3
|
||||
|
||||
repo @all
|
||||
R = gitweb
|
||||
|
||||
is effectively the same as this, for repo foo:
|
||||
|
||||
repo foo
|
||||
RW = u1
|
||||
RW = u2
|
||||
R = u3
|
||||
R = gitweb
|
||||
|
||||
This extends to patterns also, but I'll leave an example for later.
|
||||
|
||||
## #subconf the subconf command
|
||||
|
||||
This is just like the include command:
|
||||
|
||||
subconf "foo/bar.conf" # example 1
|
||||
subconf "foo/*.conf" # example 2
|
||||
|
||||
with the difference that, for the duration of the file(s) being included, a
|
||||
subconf restriction is in effect. This restrictions limits the repos that can
|
||||
be access controlled by the lines within the included file(s).
|
||||
|
||||
Here's how it works. First, a subconf *name* is derived from the filename
|
||||
being included, which is basically the basename of the file. For example 1
|
||||
that is "bar". For example 2, assuming foo contains "a.conf" and "b.conf",
|
||||
the subconf name is "a" while processing "a.conf", and "b" while processing
|
||||
"b.conf".
|
||||
|
||||
A variation of the subconf command allows you to specify the subconf *name*
|
||||
explicitly, while including files as before:
|
||||
|
||||
subconf frob "foo/bar.conf # example 3
|
||||
subconf frob "foo/*.conf # example 4
|
||||
|
||||
In this case the subconf name is "frob" in both cases.
|
||||
|
||||
A subconf restricts the repos that can be named in 'repo' lines while the
|
||||
subconf is in effect. If the subconf name is "foo", the conf lines parsed
|
||||
while under the subconf restriction can only refer to
|
||||
|
||||
* a repo called 'foo'
|
||||
* a repo group called '@foo'
|
||||
* the members of the same repo group '@foo'
|
||||
* match a regex that is a member of the repo group '@foo'
|
||||
|
||||
In the last 3 cases, the repo group '@foo' must be defined in the main conf
|
||||
file (i.e., outside the subconf restriction).
|
||||
|
||||
For example, if you have this in the main conf file:
|
||||
|
||||
@foo = bar baz frob/..*
|
||||
|
||||
subconf "foo.conf"
|
||||
|
||||
then foo.conf can only refer to 'foo', '@foo', 'bar', 'baz', or any repo name
|
||||
matching the pattern `frob/..*`.
|
||||
|
||||
## #HOSTNAME HOSTNAME substitution
|
||||
|
||||
Wherever gitolite sees the word `%HOSTNAME`, it will replace it with the
|
||||
HOSTNAME supplied in the rc file, if one was supplied. This is mainly useful
|
||||
in [mirroring][].
|
115
doc/mkdoc
115
doc/mkdoc
|
@ -1,115 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# cd <g3 clone>/doc; ./mkdoc # --> creates ../html/*.html
|
||||
|
||||
my $MKD = "./Markdown.pl";
|
||||
|
||||
use 5.10.0;
|
||||
use strict;
|
||||
use warnings;
|
||||
use lib '../src/Gitolite/Test';
|
||||
use Tsh;
|
||||
|
||||
$ENV{TSH_ERREXIT} = 1;
|
||||
|
||||
try "
|
||||
mkdir ../html; ok
|
||||
git status -s -uno; !/./
|
||||
git log --oneline -1
|
||||
" or die 1;
|
||||
|
||||
my $head = (lines())[0];
|
||||
|
||||
main();
|
||||
|
||||
try "
|
||||
git checkout gh-pages; ok
|
||||
git reset --hard github/gh-pages; ok
|
||||
cd ..; ok
|
||||
git rm g3/*.html; ok
|
||||
mkdir g3; ok
|
||||
mv html/*.html g3; ok
|
||||
git add g3; ok
|
||||
git commit -m '$head'; ok
|
||||
git checkout g3; ok
|
||||
rmdir html; ok
|
||||
" or die 2;
|
||||
|
||||
sub main {
|
||||
chomp(@ARGV = `cat list`) if not @ARGV;
|
||||
@ARGV = grep { $_ ne 'master-toc.mkd' and /./ } @ARGV;
|
||||
my @save = @ARGV;
|
||||
my $css = join("", <DATA>);
|
||||
|
||||
my $mt = "# gitolite master table of contents/index\n";
|
||||
my $mf = '';
|
||||
my $fh;
|
||||
|
||||
while (<>) {
|
||||
$ARGV =~ /^(?:.*\/)?([^\/]+)\.mkd$/;
|
||||
my $b = $1;
|
||||
|
||||
if (/^(#+) (?:#(\S+) )?(.*)/) {
|
||||
if ( length($1) == 1 ) {
|
||||
$mt .= "\n";
|
||||
$mt .= " * [$3][$b]\n";
|
||||
$mf .= "[$b]: $b.html\n";
|
||||
} else {
|
||||
$mt .= " " x ( 4 * ( length($1) - 1 ) );
|
||||
$mt .= " * ";
|
||||
$mt .= (
|
||||
$2
|
||||
? "[$3][$2]"
|
||||
: "$3"
|
||||
);
|
||||
$mt .= "\n";
|
||||
$mf .= "[$2]: $b.html" . ($2 ne $b ? "#$2" : "") . "\n" if $2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open($fh, ">", "master-toc.mkd")
|
||||
and print $fh $mt
|
||||
and close $fh;
|
||||
|
||||
# after this, do this for every mkd (including the master-toc.mkd)
|
||||
|
||||
# cat $css_block > $base.html
|
||||
# cat $base.mkd $mf | $MKD >> $base.html
|
||||
|
||||
for my $mkd ("master-toc.mkd", @save) {
|
||||
$mkd =~ /^(?:.*\/)?([^\/]+)\.mkd$/;
|
||||
my $b = $1;
|
||||
|
||||
open($fh, ">", "../html/$b.html")
|
||||
and print $fh $css
|
||||
and close $fh;
|
||||
|
||||
my $mkt = `cat $mkd`;
|
||||
$mkt =~ s/^(#+) #(\S+) /$1 <a name="$2"><\/a> /mg;
|
||||
open($fh, "|-", "$MKD >> ../html/$b.html")
|
||||
and print $fh $mkt, $mf
|
||||
and close $fh;
|
||||
}
|
||||
}
|
||||
|
||||
__DATA__
|
||||
|
||||
<head><style>
|
||||
body { background: #fff; margin-left: 40px; font-size: 0.9em; font-family: sans-serif; max-width: 800px; }
|
||||
h1 { background: #ffb; margin-left: -30px; border-top: 5px solid #ccc; }
|
||||
h2 { background: #ffb; margin-left: -20px; border-top: 3px solid #ddd; }
|
||||
h3 { background: #ffb; margin-left: -10px; }
|
||||
h4 { background: #ffb; }
|
||||
code { font-size: 1.1em; background: #ddf; }
|
||||
pre { margin-left: 2em; background: #ddf; }
|
||||
pre code { font-size: 1.1em; background: #ddf; }
|
||||
</style></head>
|
||||
|
||||
<p style="text-align:center">
|
||||
<a href="master-toc.html">master TOC</a>
|
||||
|
|
||||
<a href="index.html">main page</a>
|
||||
|
|
||||
<a href="index.html#license">license</a>
|
||||
</p>
|
160
doc/non-core.mkd
160
doc/non-core.mkd
|
@ -1,160 +0,0 @@
|
|||
# non-core programs shipped with gitolite
|
||||
|
||||
## commands
|
||||
|
||||
A list of these commands can be obtained by running `gitolite help` on the
|
||||
server. A different (and probably much smaller) list can be obtained by a
|
||||
remote user running `ssh git@host help`.
|
||||
|
||||
All the commands will respond to `-h`; please report a bug to me if they
|
||||
don't.
|
||||
|
||||
## syntactic sugar
|
||||
|
||||
The following "sugar" programs are available:
|
||||
|
||||
* continuation-lines -- allow the use of C-style backslash escaped
|
||||
continuation lines in the conf file. I don't like it but some people do,
|
||||
and now I can support them without bulking up the "core" conf parser!
|
||||
|
||||
* keysubdirs-as-groups -- someone wanted the sub-directory name (of
|
||||
"keydir/") in which the pubkey was placed to be a group to which the user
|
||||
automatically belonged. A very unusual requirement, and one which would
|
||||
*never* have seen the light of day in g2, but in g3 it's easy, and doesn't
|
||||
affect anyone else!
|
||||
|
||||
(Note: the last component of the directory path is used if there are more
|
||||
than one level between "keydir/" and the actual file).
|
||||
|
||||
## triggers
|
||||
|
||||
The `PRE_GIT` triggers are:
|
||||
|
||||
* partial-copy -- this has its own section later in this page
|
||||
|
||||
* renice -- this renices the entire job to whatever value you specify
|
||||
|
||||
The `POST_GIT` triggers are:
|
||||
|
||||
* cpu-time -- post-git triggers, if you check the [triggers][] doc, receive
|
||||
4 CPU time numbers from the main shell program. Treat this code as sample
|
||||
and do do with them as you please to do with as you please.
|
||||
|
||||
The `POST_COMPILE` triggers are:
|
||||
|
||||
* post-compile/ssh-authkeys -- takes the pubkeys in keydir and populates
|
||||
`~/.ssh/authorized_keys`
|
||||
|
||||
* post-compile/update-git-configs -- updates individual 'repo.git/config'
|
||||
files (using the 'git config ...' command) from settings supplied in the
|
||||
conf file. All sections except 'gitolite-options' are processed. (The
|
||||
'gitolite-options' section is considered internal to gitolite).
|
||||
|
||||
* post-compile/update-git-daemon-access-list -- create/delete
|
||||
'git-daemon-export-ok' files in each repo based on whether the conf says
|
||||
'daemon' can read the repo or not
|
||||
|
||||
* post-compile/update-gitweb-access-list -- populates the file named in
|
||||
`GITWEB_PROJECTS_LIST` in the rc file (default: `$HOME/projects.list`)
|
||||
with the list of repos that gitweb is allowed to access. This could be
|
||||
more than just "R = gitweb"; any repo that has any config setting with the
|
||||
section name 'gitweb' (like 'gitweb.owner', 'gitweb.description', etc) is
|
||||
considered readable by gitweb, so the final list is a union of these two
|
||||
methods
|
||||
|
||||
The `POST_CREATE` triggers are:
|
||||
|
||||
* the last 3 in the `POST_COMPILE` list also run from `POST_CREATE`, for
|
||||
obvious reasons.
|
||||
|
||||
## VREFs
|
||||
|
||||
You should read about [vref][]s in detail first; this won't make sense
|
||||
otherwise. For a brief recap, note that there are 2 kinds of VREFs: those
|
||||
that require arguments and those that behave just like any other `update`
|
||||
hook.
|
||||
|
||||
COUNT is an example of the former (hence the long-ish description). DUPKEYS
|
||||
and EMAIL-CHECK are both examples of the latter.
|
||||
|
||||
* COUNT
|
||||
|
||||
The COUNT VREF is used like this:
|
||||
|
||||
- VREF/COUNT/9 = @junior-developers
|
||||
|
||||
In response, if anyone in the user list pushes a commit series that
|
||||
changes more than 9 files, a vref of "VREF/COUNT/9" is returned. Gitolite
|
||||
uses that as a "ref" to match against all the rules, hit the same rule
|
||||
that invoked it, and deny the request.
|
||||
|
||||
If the user did not push more than 9 files, the VREF code returns nothing,
|
||||
and nothing happens.
|
||||
|
||||
COUNT can take one more argument:
|
||||
|
||||
- VREF/COUNT/9/NEWFILES = @junior-developers
|
||||
|
||||
This is the same as before, but have to be more than 9 *new* files not
|
||||
just changed files.
|
||||
|
||||
* DUPKEYS -- this checks keydir/ for duplicate keys and aborts the push if
|
||||
it finds any. You should use this only on the gitolite-admin repo.
|
||||
|
||||
repo gitolite-admin
|
||||
- VREF/DUPKEYS = @all
|
||||
|
||||
* EMAIL-CHECK -- read the comments in the code for this one. Like DUPKEYS,
|
||||
it does not take any arguments.
|
||||
|
||||
* FILETYPE -- this is sample code for a very site-specific purpose; you'll
|
||||
have to read the code
|
||||
|
||||
* MERGE-CHECK -- this is sample code to illustrate how one of the gitolite
|
||||
built-in functions *could* have been handled, although there are some
|
||||
differences
|
||||
|
||||
* partial-copy -- this has its own section later in this page
|
||||
|
||||
## special cases
|
||||
|
||||
### partial-copy
|
||||
|
||||
Git (and therefore gitolite) cannot do selective read control -- allowing
|
||||
someone to read branch A but not branch B. It's the entire repo or nothing.
|
||||
|
||||
<font color="gray"> [Side note: Gerrit Code Review can do that, but that is
|
||||
because they have their own git stack (and their own sshd, and so on) all in
|
||||
one big Java program. Gerrit is *really* useful if you want code review to be
|
||||
part of the access control decision] </font>
|
||||
|
||||
Gitolite can now help you do this, as follows:
|
||||
|
||||
1. enable 'partial-copy' in the `PRE_GIT` section in the rc file.
|
||||
|
||||
2. for each repo "foo" which has secret branches that a certain set of
|
||||
developers (we'll use a group called `@temp-emp` as an example) are not
|
||||
supposed to see, do this:
|
||||
|
||||
repo foo
|
||||
# rules should allow @temp-emp NO ACCESS
|
||||
|
||||
repo foo-partialcopy-1
|
||||
- secret-branch = @temp-emp
|
||||
# other rules should ensure ONLY @temp-emp has ANY ACCESS
|
||||
# NO other user should have access
|
||||
|
||||
- VREF/partial-copy = @all
|
||||
config gitolite.partialCopyOf = foo
|
||||
|
||||
And that should be it. **Please test it and let me know if it doesn't work!**
|
||||
|
||||
WARNINGS:
|
||||
|
||||
* if you change the config to disallow something that used to be allowed,
|
||||
you should delete the partial repo on the server and then run 'gitolite
|
||||
compile' to let it build again. See t/partial-copy.t for details.
|
||||
|
||||
* not tested with smart http; probabl won't work
|
||||
|
||||
* also not tested with mirroring, or with wild card repos.
|
45
doc/pw.mkd
45
doc/pw.mkd
|
@ -1,45 +0,0 @@
|
|||
# patches welcome :-)
|
||||
|
||||
These are places where I could use some help, with hints about how you would
|
||||
go about it. In addition, any of the items in [dev-status][] are up for
|
||||
grabs, if you wish to jump in. But let me know before you start, and how you
|
||||
plan to go about it.
|
||||
|
||||
## #pw2 rsync
|
||||
|
||||
The crux of the old rsync ADC was an access check to a repo whose name starts
|
||||
with EXTCMD. g2 would treat repos whose names start with EXTCMD as "don't
|
||||
create this repo". g3 makes no such distinctions, but you can get the same
|
||||
effect by using a repo group name that does not exist:
|
||||
|
||||
repo @rsync-set1
|
||||
...rules...
|
||||
|
||||
and check that in the script. (It might mean adding one more function to
|
||||
Gitolite::Easy but that's... easy!)
|
||||
|
||||
## #pw1 converting non-openssh pubkeys automatically
|
||||
|
||||
Gitolite's [triggers][] has can be used to fix this. Here's pseudo-code:
|
||||
|
||||
chdir $(gitolite query-rc -n GL_ADMIN_BASE)/keydir
|
||||
for each *.pub file in this directory and its subdirectories
|
||||
detect its format and convert it
|
||||
# just overwrite it; the original is saved in git anyway
|
||||
|
||||
Let's say the program is called "convert-non-openssh-keys". You send it to me
|
||||
and I add it to src/commands/post-compile/. Then I update the default rc file
|
||||
to look at least like this instead
|
||||
|
||||
POST_COMPILE =>
|
||||
[
|
||||
# 'post-compile/convert-non-openssh-keys',
|
||||
'post-compile/ssh-authkeys',
|
||||
...
|
||||
...
|
||||
],
|
||||
|
||||
making sure to place it *before* ssh-authkeys, and anyone who needs it can
|
||||
just uncomment it.
|
||||
|
||||
Done!
|
26
doc/qi.mkd
26
doc/qi.mkd
|
@ -1,26 +0,0 @@
|
|||
# getting started
|
||||
|
||||
The quickest install, assuming your `$PATH` contains `$HOME/bin`, is:
|
||||
|
||||
* get the software
|
||||
|
||||
git clone git://github.com/sitaramc/gitolite
|
||||
|
||||
# (until this becomes "master")
|
||||
cd gitolite
|
||||
git checkout -f g3
|
||||
|
||||
* install it
|
||||
|
||||
ln -sf $PWD/src/gitolite $HOME/bin
|
||||
|
||||
If you don't like that, there are [other install methods][install].
|
||||
|
||||
Once the install is done, setup:
|
||||
|
||||
gitolite setup -pk your-name.pub
|
||||
|
||||
And that's it.
|
||||
|
||||
Next steps are usually [adding][add] users and repos and learning about
|
||||
[access control][conf].
|
20
doc/rc.mkd
20
doc/rc.mkd
|
@ -1,20 +0,0 @@
|
|||
# the "rc" file ($HOME/.gitolite.rc)
|
||||
|
||||
The rc file for g3 is quite different from that of g2. It has been designed
|
||||
to be (a) the only thing unique to your site, for most installations and (b)
|
||||
easy to extend when new needs show up, without having to touch core gitolite.
|
||||
|
||||
g2 had a nasty rc file where every variable had to be declared. As a result,
|
||||
ADCs that needed their own settings could not use it.
|
||||
|
||||
Now it's a perl hash, and you can add any keys you want.
|
||||
|
||||
Please look at the rc file that gets installed when you setup gitolite. As
|
||||
you can see there are 3 types of variables in it:
|
||||
|
||||
* simple variables (like UMASK)
|
||||
* lists (like `POST_COMPILE`, `POST_CREATE`)
|
||||
* hashes (like `ROLES`, `COMMANDS`)
|
||||
|
||||
Their purposes are to be found in each of their individual documentation files
|
||||
around; start with [customising gitolite][cust].
|
|
@ -1,30 +0,0 @@
|
|||
## #refex matching a ref and a refex
|
||||
|
||||
A refex is a word I made up to mean "a regex that matches a ref". If you know
|
||||
[regular expressions][regex] you're halfway there.
|
||||
|
||||
The only extra info you need is:
|
||||
|
||||
* for convenience, a refex not starting with `refs/` is assumed to start
|
||||
with `refs/heads/`. This means normal branches can be referred to like
|
||||
this:
|
||||
|
||||
RW master = alice
|
||||
# becomes 'refs/heads/master' internally
|
||||
|
||||
while tags will need to be fully qualified
|
||||
|
||||
RW refs/tags/v[0-9] = bob
|
||||
|
||||
* a refex is implicitly anchored at the start, but not at the end. In
|
||||
regular expression lingo, a `^` is assumed at the start (but no `$` at the
|
||||
end is assumed). So a refex of `master` will allow all these:
|
||||
|
||||
refs/heads/master
|
||||
refs/heads/master1
|
||||
refs/heads/master2
|
||||
refs/heads/master/full
|
||||
|
||||
If you want to restrict to just the one specific ref, use
|
||||
|
||||
RW master$ = alice
|
23
doc/repo.mkd
23
doc/repo.mkd
|
@ -1,23 +0,0 @@
|
|||
## #repo repo definitions
|
||||
|
||||
Example:
|
||||
|
||||
repo gitolite tsh gitpod
|
||||
RW+ = sitaram
|
||||
RW dev = alice bob
|
||||
R = @all
|
||||
|
||||
The "repo" line can have any number of repo names or repo group names in it.
|
||||
However, it can only be one line; this will not work
|
||||
|
||||
repo foo
|
||||
repo bar # WRONG; 'foo' is now forgotten
|
||||
RW = alice
|
||||
|
||||
If you have too many, use a group name:
|
||||
|
||||
@myrepos = foo
|
||||
@myrepos = bar
|
||||
|
||||
repo @myrepos
|
||||
RW = alice
|
|
@ -1,82 +0,0 @@
|
|||
## #rules access rules
|
||||
|
||||
This is arguably the most complex part of day-to-day gitolite. There are
|
||||
several interconnected ideas that make this hard to lay out easily if you're
|
||||
totally new to this, so read carefully.
|
||||
|
||||
We will use this as a running example:
|
||||
|
||||
@staff = dilbert alice wally bob
|
||||
|
||||
repo foo
|
||||
RW+ = dilbert # line 1
|
||||
RW+ dev = alice # line 2
|
||||
- = wally # line 3
|
||||
RW temp/ = @staff # line 4
|
||||
R = ashok # line 5
|
||||
|
||||
### when does gitolite check access
|
||||
|
||||
The "pre-git" check is before git is invoked. Gitolite knows the repo name,
|
||||
user name, and attempted access (R or W), but no ref name.
|
||||
|
||||
The "update" check is only for write operations, and it is just before git
|
||||
updates a ref. This time gitolite knows the refname also.
|
||||
|
||||
### how is a particular rule line matched
|
||||
|
||||
For the **pre-git check**, any permission that contains "R" matches a read
|
||||
operation, and any permission that contains "W" matches a write operation.
|
||||
This is because we simply don't know enough to make finer distinctions at this
|
||||
point.
|
||||
|
||||
In addition, *gitolite ignores deny rules during the pre-git check*. <font
|
||||
color="gray">(You can [change this][deny-rules] if you wish, though it's
|
||||
rarely needed)</font>. This means line 3 is ignored, and so Wally in our
|
||||
example will pass the pre-git check.
|
||||
|
||||
For the **update check**, git gives us all the information we need. Then:
|
||||
|
||||
* all the rules for a repo are [accumulated][rule-accum]
|
||||
|
||||
* then the rules pertaining to this repo *and* this user (or to a group to
|
||||
which they belong, respectively) are kept; the rest are ignored
|
||||
|
||||
* these rules are examined *in the sequence they appeared in the conf file*.
|
||||
For each rule:
|
||||
|
||||
* if the ref does not match the [refex][], the rule is skipped
|
||||
* if it's a deny rule (the permissions field is a `-`), access is
|
||||
**rejected** and the matching stops
|
||||
* if the permission field matches the specific [type of
|
||||
write][write-types] operation, access is **allowed** and the matching
|
||||
stops
|
||||
|
||||
* if no rule ends with a decision, ("fallthru"), access is **rejected**.
|
||||
|
||||
Now you need to understand how [refex][] matching happens and how the
|
||||
permissions match the various [types of write operations][write-types].
|
||||
|
||||
Using these, you can see, in our example, that:
|
||||
|
||||
* everyone, even wally, can read the repo.
|
||||
* dilbert can push, rewind, or delete any ref.
|
||||
* alice can push, rewind, or delete any ref whose name starts with 'dev';
|
||||
see [refex][] for details.
|
||||
* alice can also push (but not rewind or delete) any ref whose name starts
|
||||
with 'temp/'. This applies to bob also.
|
||||
* if it weren't for line 3, the previous statement would apply to wally
|
||||
also.
|
||||
|
||||
Interestingly, wally can get past the pre-git check because gitolite ignores
|
||||
deny rules for pre-git, but having got past it, he can't actually do anything.
|
||||
That's by design, and as I said if you don't like it you can ask gitolite to
|
||||
[deny at pre-git][deny-rules].
|
||||
|
||||
### summary of permissions
|
||||
|
||||
The full set of permissions, in regex syntax: `-|R|RW+?C?D?M?`. This expands
|
||||
to one of `-`, `R`, `RW`, `RW+`, `RWC`, `RW+C`, `RWD`, `RW+D`, `RWCD`, or
|
||||
`RW+CD`, all but the first one optionally followed by an `M`. And by now you
|
||||
know what they all mean.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
# testing gitolite
|
||||
|
||||
Here's how to *run* the tests. **WARNING: they will clobber lots of things in
|
||||
your `$HOME`, so be sure to use a throwaway userid**.
|
||||
|
||||
git clone git://github.com/sitaramc/gitolite
|
||||
cd gitolite
|
||||
git checkout -f g3
|
||||
prove
|
||||
|
||||
Gitolite's test suite is mostly written using [tsh][] -- the "testing shell".
|
||||
Take a look at some of the scripts and you will see what it looks like. It
|
||||
has a few quirks and nuances; if you really care, email me.
|
||||
|
||||
[tsh]: http://github.com/sitaramc/tsh
|
||||
|
||||
The tests also use a somewhat convoluted system of environment variables in
|
||||
order to run *entirely* as a local user, without going through ssh at all.
|
||||
This lets a complete test suite run in about a fifth or less of the time it
|
||||
would otherwise take.
|
||||
|
||||
If you think that defeats the purpose of the testing, you haven't read
|
||||
[this][auth] yet.
|
||||
|
||||
There are 2 specific tests that deal with ssh though, which are run only on
|
||||
request, as you can see above, because they clobber your `~/.ssh`. You have
|
||||
been warned.
|
108
doc/triggers.mkd
108
doc/triggers.mkd
|
@ -1,108 +0,0 @@
|
|||
# gitolite triggers
|
||||
|
||||
## intro and sample rc excerpt
|
||||
|
||||
Gitolite fires off external commands at 7 different times. The [rc][] file
|
||||
specifies what commands to run at each trigger point, but for illustration,
|
||||
here's an excerpt:
|
||||
|
||||
%RC = (
|
||||
|
||||
<...several lines later...>
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence after post-update
|
||||
POST_COMPILE =>
|
||||
[
|
||||
'post-compile/ssh-authkeys',
|
||||
'post-compile/update-git-configs',
|
||||
'post-compile/update-gitweb-access-list',
|
||||
'post-compile/update-git-daemon-access-list',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence after a new wild repo is created
|
||||
POST_CREATE =>
|
||||
[
|
||||
'post-compile/update-git-configs',
|
||||
'post-compile/update-gitweb-access-list',
|
||||
'post-compile/update-git-daemon-access-list',
|
||||
],
|
||||
|
||||
(As you can see, post-create runs 3 programs that also run from post-compile.
|
||||
This is perfectly fine, by the way)
|
||||
|
||||
## manually firing triggers
|
||||
|
||||
...from the server command line is easy. For example:
|
||||
|
||||
gitolite trigger POST_COMPILE
|
||||
|
||||
However if the triggered code depends on arguments (see next section) this
|
||||
won't work. (The `POST_COMPILE` trigger programs all just happen to not
|
||||
require any arguments, so it works).
|
||||
|
||||
## common arguments
|
||||
|
||||
Triggers receive the following arguments:
|
||||
|
||||
1. any arguments mentioned in the rc file (for an example, see the renice
|
||||
command in the PRE_GIT trigger sequence),
|
||||
|
||||
2. the name of the trigger as a string (example, `"POST_COMPILE"`), so you
|
||||
can call the same program from multiple triggers and know where it was
|
||||
called from,
|
||||
|
||||
3. followed by zero or more arguments specific to the trigger, as given in
|
||||
the next section.
|
||||
|
||||
## trigger-specific details
|
||||
|
||||
Here's a brief "when" and "with what arguments" for each trigger.
|
||||
|
||||
* `ACCESS_1` runs after the first access check. Arguments:
|
||||
* repo
|
||||
* user
|
||||
* 'R' or 'W'
|
||||
* 'any'
|
||||
* result: this is the result of the access() function. If it contains
|
||||
the uppercase word "DENIED", the access was rejected. Otherwise
|
||||
result contains the refex that caused the access to succeed.
|
||||
|
||||
* `ACCESS_2` runs after the second access check, in the update hook.
|
||||
Arguments:
|
||||
* repo
|
||||
* user
|
||||
* any of W, +, C, D, WM, +M, CM, DM
|
||||
* the ref being updated (e.g., 'refs/heads/master')
|
||||
* result (see above)
|
||||
|
||||
* `PRE_GIT` runs just before running the git command. Arguments:
|
||||
* repo
|
||||
* user
|
||||
* 'R' or 'W'
|
||||
* 'any'
|
||||
* the git command ('git-receive-pack', 'git-upload-pack', or
|
||||
'git-upload-archive') being invoked.
|
||||
|
||||
* `POST_GIT` runs after the git command returns. Arguments:
|
||||
* repo
|
||||
* user
|
||||
* 'R' or 'W'
|
||||
* 'any'
|
||||
* the git command ('git-receive-pack', 'git-upload-pack', or
|
||||
|
||||
These are followed by the output of the perl function "times" (i.e., 4 CPU
|
||||
times: user, system, cumulative user, cumulative system) so that's 9
|
||||
arguments in total
|
||||
|
||||
* `PRE_CREATE` and `POST_CREATE` run just before and after a new "[wild][]"
|
||||
repo is created by user action. Arguments:
|
||||
* repo
|
||||
* user
|
||||
|
||||
* `POST_COMPILE` runs after an admin push has successfully "compiled" the
|
||||
config file. By default, the next thing is to update the ssh authkeys
|
||||
file, then all the 'git-config's, gitweb access, and daemon access.
|
||||
|
||||
No arguments.
|
|
@ -1,96 +0,0 @@
|
|||
# what users (not admins) need to know about gitolite
|
||||
|
||||
...written for the one guy in the world no one will think of as "just a normal
|
||||
user" ;-)
|
||||
|
||||
This document has some text, and a lot of links. Most of this info *is*
|
||||
available in the rest of the documentation, but it's scattered and sparse.
|
||||
Collecting all of it, or at least links to it, in one place sounds useful.
|
||||
|
||||
## accessing gitolite
|
||||
|
||||
The most common setup is based on ssh, where your admin asks you to send him
|
||||
your public key, and uses that to setup your access.
|
||||
|
||||
Your actual access is either a git command (like `git clone
|
||||
git@server:reponame`, and we won't be discussing these any more in this
|
||||
document), or an ssh command (like `ssh git@server info`).
|
||||
|
||||
Note that you do *not* get a shell on the server -- the whole point of
|
||||
gitolite is to prevent that!
|
||||
|
||||
## #info the info command
|
||||
|
||||
The only command that is *always* available to every user is the `info`
|
||||
command (run `ssh git@host info -h` for help), which tells you what version of
|
||||
gitolite and git are on the server, and what repositories you have access to.
|
||||
The list of repos is very useful if you have doubts about the spelling of some
|
||||
new repo that you know was setup.
|
||||
|
||||
## digression: two kinds of repos
|
||||
|
||||
Gitolite has two kinds of repos. Normal repos are specified by their full
|
||||
names in the config file. "Wildcard" repos are specified by a regex in the
|
||||
config file. Try the [`info` command][info] and see if it shows any lines
|
||||
that look like regex patterns, (with a "C" permission in addition to the "R"
|
||||
and the "W").
|
||||
|
||||
If you see any, it means you are allowed to create brand new repos whose names
|
||||
fit that pattern. When you create such a repo, your "ownership" of it (as far
|
||||
as gitolite is concerned) is *automatically* recorded by gitolite.
|
||||
|
||||
## other commands
|
||||
|
||||
### #perms set/get additional permissions for repos you created
|
||||
|
||||
The gitolite config may have several permissions lines for your repo, like so:
|
||||
|
||||
repo pub/CREATOR/..*
|
||||
RW+ = CREATOR
|
||||
RW = user1 user2
|
||||
R = user3
|
||||
|
||||
If that's all it had, you really can't do much. Any changes to access must be
|
||||
done by the administrator. (Note that "CREATOR" is a reserved word that gets
|
||||
expanded to your userid in some way, so the admin can literally add just the
|
||||
first two lines, and *every* authenticated user now has his own personal repo
|
||||
namespace, starting with `pub/<username>/`).
|
||||
|
||||
To give some flexibility to users, the admin could add rules like this:
|
||||
|
||||
RW = WRITERS
|
||||
R = READERS
|
||||
|
||||
(he could also add other roles but then he needs to read the documentation).
|
||||
|
||||
Once he does this, you can then use the `perms` command (run `ssh git@host
|
||||
perms -h` for help) to set permissions for other users by specifying which
|
||||
users are in the list of "READERS", and which in "WRITERS".
|
||||
|
||||
If you think of READERS and WRITERS as "roles", it will help. You can't
|
||||
change what access a role has, but you *can* say which users have that role.
|
||||
|
||||
**Note**: there isn't a way for you to see the actual rule set unless you're
|
||||
given read access to the special 'gitolite-admin' repo. Sorry. The idea is
|
||||
that your admin will tell you what "roles" he added into rules for your repos,
|
||||
and what permissions those roles have.
|
||||
|
||||
### #desc adding a description to repos you created
|
||||
|
||||
The `desc` command is extremely simple. Run `ssh git@host desc -h` for help.
|
||||
|
||||
## "site-local" commands
|
||||
|
||||
The main purpose of gitolite is to prevent you from getting a shell. But
|
||||
there are commands that you often need to run on the server (i.e., cannot be
|
||||
done by pushing something to a repo).
|
||||
|
||||
To enable this, gitolite allows the admin to setup scripts in a special
|
||||
directory that users can then run. Gitolite comes with a set of working
|
||||
scripts that your admin may install, or may use as a starting point for his
|
||||
own, if he chooses.
|
||||
|
||||
Think of these commands as equivalent to those in `COMMAND_DIR` in `man
|
||||
git-shell`.
|
||||
|
||||
You can get a list of available commands by running `ssh git@host help`.
|
254
doc/vref.mkd
254
doc/vref.mkd
|
@ -1,254 +0,0 @@
|
|||
# virtual refs
|
||||
|
||||
Here's an example to start you off.
|
||||
|
||||
repo r1
|
||||
RW+ = lead_dev dev2 dev3
|
||||
- VREF/COUNT/9 = dev2 dev3
|
||||
- VREF/COUNT/3/NEWFILES = dev2 dev3
|
||||
|
||||
Now dev2 and dev3 cannot push changes that affect more than 9 files at a time,
|
||||
nor those that have more than 3 new files.
|
||||
|
||||
Another example is detecting duplicate pubkeys in a push to the admin repo:
|
||||
|
||||
repo gitolite-admin
|
||||
# ... normal rules ...
|
||||
- VREF/DUPKEYS = @all
|
||||
|
||||
----
|
||||
|
||||
## rule matching recap
|
||||
|
||||
You won't get any joy out of this if you don't understand at least
|
||||
[refex][]es and how [rules][] are processed.
|
||||
|
||||
But VREFs have one **very important difference** from normal rules. With
|
||||
VREFs, a **fallthru results in success**. You'll see why this is more
|
||||
convenient as you read on.
|
||||
|
||||
----
|
||||
|
||||
## what is a virtual ref
|
||||
|
||||
A ref like `refs/heads/master` is the main property of a push that gitolite
|
||||
uses to make its yes/no decision. I call this a "real" ref.
|
||||
|
||||
Any *other* property of the push that you want to use to help in the decision
|
||||
is therefore a *virtual* ref. This could be a property that git knows about,
|
||||
like in the example above, or comes from outside git like, say, the current
|
||||
time; see examples section later for some ideas.
|
||||
|
||||
## fallthru is success here
|
||||
|
||||
Notice that you didn't need to add an `RW+ VREF/...` rule for user `lead_dev`
|
||||
in our example. This section explains why.
|
||||
|
||||
**Virtual refs are best used as additional "deny" rules**, performing extra
|
||||
checks that core gitolite cannot.
|
||||
|
||||
Making fallthru be a "fail" forces you to add rules for all users, instead of
|
||||
just the ones who should have those extra checks. Worse, since every virtual
|
||||
ref involves calling an external program, many of these calls may be wasted.
|
||||
|
||||
There's another advantage to doing it this way: a VREF can choose to simply
|
||||
die if things look bad, and it will have the same effect, assuming you used
|
||||
the VREF only in [deny][] rules.
|
||||
|
||||
This in turn means any existing update hook can be used as a VREF *as-is*, as
|
||||
long as it (a) prints nothing on success and (b) dies on failure. See the
|
||||
email-check and dupkeys examples later.
|
||||
|
||||
## how it works -- overview
|
||||
|
||||
Briefly, a refex starting with `VREF/FOO` triggers a call to a program called
|
||||
`FOO` in `$GL_BINDIR/VREF`.
|
||||
|
||||
That program is expected to print zero or more lines to its STDOUT; each line
|
||||
is taken by gitolite as a new "ref" to be matched against all the refexes for
|
||||
this user in the config. Including the refex that caused the vref call, of
|
||||
course.
|
||||
|
||||
Normally, you send back the refex itself, if the test determines that the rule
|
||||
should be matched, otherwise nothing. So, in our example, we print
|
||||
`VREF/COUNT/9` if the count was indeed greater than 9. Otherwise we just
|
||||
exit.
|
||||
|
||||
## how it works -- details
|
||||
|
||||
* the VREF code is only called if there are any VREF rules for the user,
|
||||
which means when the lead developer pushes, the VREF is not called at all.
|
||||
|
||||
Side note: this is enormously more efficient than adding additional
|
||||
`update` hooks, which will get executed whether they are needed or not,
|
||||
for every repo and every user!
|
||||
|
||||
* when dev2 or dev3 push, gitolite first checks the real ref
|
||||
(`ref/heads/master` or whatever). After this it looks at VREF rules, and
|
||||
calls an external program for every one it finds. Specifically, in a line
|
||||
like
|
||||
|
||||
- VREF/COUNT/3/NEWFILES = user
|
||||
|
||||
COUNT is the vref name, so the program called is
|
||||
`$GL_BINDIR/VREF/COUNT`.
|
||||
|
||||
The program is passed **nine arguments** in this case (see next section
|
||||
for details).
|
||||
|
||||
* the script can print anything it wants to STDOUT; the first word in each
|
||||
such line will be treated as a virtual ref to be matched against all the
|
||||
rules, while the rest, if any, is a message to be added to the standard
|
||||
"...DENIED..." message that gitolite prints if that refex matches.
|
||||
|
||||
Usually it only makes sense to either
|
||||
|
||||
* print nothing -- if you don't want the rule that triggered it to match
|
||||
(ie., whatever condition being tested was not violated; like if the
|
||||
count of changed files did not exceed 9, in our earlier example)
|
||||
* print the refex itself (plus an optional message), so that it matches
|
||||
the line which invoked it
|
||||
|
||||
### arguments passed to the vref code
|
||||
|
||||
* arguments **1, 2, 3**: the 'ref', 'oldsha', and 'newsha' that git passed
|
||||
to the update hook (see 'man githooks')
|
||||
|
||||
This, combined with the fact that non-zero exits are detected, mean that
|
||||
you can simply use an existing update.secondary as a new VREF as-is, no
|
||||
changes needed.
|
||||
|
||||
* arguments **4 and 5**: the 'oldtree' and 'newtree' SHAs. These are the
|
||||
same as the oldsha and newsha values, except if one of them is all-0.
|
||||
(indicating a ref creation or deletion). In that case the corresponding
|
||||
'tree' SHA is set (by gitolite, as a courtesy) to the special SHA
|
||||
`4b825dc642cb6eb9a060e54bf8d69288fbee4904`, which is the hash of an empty
|
||||
tree.
|
||||
|
||||
(None of these shenanigans would have been needed if `git diff $oldsha
|
||||
$newsha` would not error out when passed an all-0 SHA.)
|
||||
|
||||
* argument **6**: the attempted access flag. Typically `W` or `+`, but
|
||||
could also be `C`, `D`, or any of these 4 followed by `M`. If you have to
|
||||
ask what they mean, you haven't read enough gitolite documentation to be
|
||||
able to make virtual refs work.
|
||||
|
||||
* argument **7**: is the entire refex; in our example
|
||||
`VREF/COUNT/3/NEWFILES`.
|
||||
|
||||
* arguments **8 onward**: are the split out (by `/`) portions of the refex,
|
||||
excluding the first two components. In our example they would be `3`
|
||||
followed by `NEWFILES`.
|
||||
|
||||
Yes, argument 7 is redundant if you have 8 and 9. It's meant to make it easy
|
||||
to write vref scripts in any language. See script examples in source.
|
||||
|
||||
## what (else) can the vref code pass back
|
||||
|
||||
Actually, the vref code can pass anything back; each line in its output will
|
||||
be matched against all the rules as usual (with the exception that fallthru is
|
||||
not failure).
|
||||
|
||||
For example, you could have a ruleset like this:
|
||||
|
||||
repo r1
|
||||
# ... normal rules ...
|
||||
|
||||
- VREF/TIME/WEEKEND = @interns
|
||||
- VREF/TIME/WEEKNIGHT = @interns
|
||||
- VREF/TIME/HOLIDAY = @interns
|
||||
|
||||
and you could write the TIME vref code to passback any or all
|
||||
of the times that match. Then if an intern tried to access the system, each
|
||||
rule would trigger a call to gl-bindir/VREF/TIME.
|
||||
|
||||
The script should send back any of the applicable times (even more than one,
|
||||
or none at all, as the case may be). So even if it was invoked using the
|
||||
first rule, it might pass back (to gitolite) a virtual ref saying
|
||||
'VREF/TIME/HOLIDAY', which would promptly cause the request to be denied.
|
||||
|
||||
## VREFs shipped with gitolite
|
||||
|
||||
### number of new files
|
||||
|
||||
If a dev pushes more than 2 *new* files, the top commit needs to have a
|
||||
signed-off by line in its commit message. For example if he has 4 new files
|
||||
this text should be:
|
||||
|
||||
4 new files signed-off by: <top commit author's email>
|
||||
|
||||
The config entry for this is below (`NO_SIGNOFF` applies only to, and thus
|
||||
implies, `NEWFILES`):
|
||||
|
||||
RW+ VREF/COUNT/2/NO_SIGNOFF = sitaram
|
||||
- VREF/COUNT/2/NO_SIGNOFF = @all
|
||||
|
||||
Notice how the refex in both cases is *exactly* the same. If you make it
|
||||
different (even change the number on my access line), things won't work.
|
||||
|
||||
Junior devs can't push more than 10 new files, even with a signed-off by line:
|
||||
|
||||
- VREF/COUNT/10/NEWFILES = @junior_devs
|
||||
|
||||
### advanced filetype detection
|
||||
|
||||
Note: this is more for illustration than use; it's rather specific to one of
|
||||
the projects I manage but the idea is the important thing.
|
||||
|
||||
Sometimes a file has a standard extension (that cannot be 'gitignore'd), but
|
||||
it is actually automatically generated. Here's one way to catch it:
|
||||
|
||||
- VREF/FILETYPE/AUTOGENERATED = @all
|
||||
|
||||
You can look at `src/VREF/FILETYPE` to see how it handles the
|
||||
'AUTOGENERATED' option. You could also have a more generic option, like
|
||||
perhaps BINARY, and handle that in the FILETYPE vref too.
|
||||
|
||||
### checking author email
|
||||
|
||||
Some people want to ensure that "you can only push your own commits".
|
||||
|
||||
If you force it on everyone, this is a very silly idea (see "Philosophical
|
||||
Notes" section of `src/VREF/EMAIL-CHECK`).
|
||||
|
||||
But there may be value in enforcing it just for the junior developers.
|
||||
|
||||
The neat thing is that the existing `contrib/update.email-check` was just
|
||||
copied to `src/VREF/EMAIL-CHECK` and it works, because VREFs get
|
||||
the same first 3 arguments and those are all that it cares about. (Note: you
|
||||
have to change one subroutine in that script if you want to use it)
|
||||
|
||||
### catching duplicate pubkeys
|
||||
|
||||
We covered this as a teaser example at the start.
|
||||
|
||||
## other ideas -- code welcome!
|
||||
|
||||
### "no non-merge first-parents"
|
||||
|
||||
Shruggar on #gitolite wanted this. Possible code to implement it would be
|
||||
something like this (untested)
|
||||
|
||||
[ -z "$(git rev-list --first-parent --no-merges $2..$3)" ]
|
||||
|
||||
This can be implemented using `src/VREF/MERGE-CHECK` as a model.
|
||||
That script does what the 'in core' feature called [merge check][mergecheck]
|
||||
does, although the syntax to be used in conf/gitolite will be quite different.
|
||||
|
||||
### other ideas for VREFs
|
||||
|
||||
Here are some more ideas:
|
||||
|
||||
* number of commits (`git rev-list --count $old $new`)
|
||||
* number of binary files in commit (currently I only know to count
|
||||
occurrences of ` Bin ` in the output of `git diff --stat`
|
||||
* number of *new* binary files (count ` Bin 0 ->` in `git diff --stat`
|
||||
output)
|
||||
* time of day/day of week (see example snippet somewhere above)
|
||||
* IP address
|
||||
* phase of the moon
|
||||
|
||||
Note that pretty much anything that involves `$oldsha..$newsha` will have to
|
||||
deal with the issue that when you push a new tag or branch, the "old" part
|
||||
is all 0's, and unless you consider `--all` existing branches and tags it
|
||||
becomes meaningless in terms of "number of new files" etc.
|
46
doc/why.mkd
46
doc/why.mkd
|
@ -1,46 +0,0 @@
|
|||
# Why is gitolite needed?
|
||||
|
||||
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 some people read-only
|
||||
access while some others have read-write access to a repo (unless you make
|
||||
it world-readable). Group access just doesn't have enough granularity
|
||||
* 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.
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
## #write-types different types of write operations
|
||||
|
||||
Git supplies enough information to the update hook to be able to distinguish
|
||||
several types of writes.
|
||||
|
||||
The most common are:
|
||||
|
||||
* `RW` -- create a ref or fast-forward push a ref. No rewinds or deletes.
|
||||
* `RW+` -- create, fast-forward push, rewind push, or delete a ref.
|
||||
|
||||
Sometimes you want to allow people to push, but not *create* a ref. Or
|
||||
rewind, but not *delete* a ref. The `C` and `D` qualifiers help here.
|
||||
|
||||
* when a rule specifies `RWC` or `RW+C`, then *rules that do NOT have the C
|
||||
qualifier will no longer permit **creating** a ref*
|
||||
|
||||
<font color="gray">Please do not confuse this with the standalone `C`
|
||||
permission that allows someone to [create][] a **repo**</font>
|
||||
|
||||
* when a rule specifies `RWD` or `RW+D`, then *rules that do NOT have the C
|
||||
qualifier will no longer permit **deleting** a ref*
|
||||
|
||||
Note: These two can be combined, so you can have `RWCD` and `RW+CD` as well.
|
||||
|
||||
One very rare need is to reject merge commits (a commit series that is not a
|
||||
straight line of commits). The `M` qualifier helps here:
|
||||
|
||||
* When a rule has `M` appended to the permissions, *rules that do NOT have
|
||||
it will reject a commit sequence that contains a merge commit* (i.e., they
|
||||
only accept a straight line series of commits).
|
||||
|
31
dot.pl
31
dot.pl
|
@ -1,31 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my @a = `grep -r use.Gitolite . | grep -i '^./gitolite'`;
|
||||
|
||||
# chomp(@a);
|
||||
open( my $fh, "|-", "tee module-tree.gv | dot -Tpng | tee module-tree.png | display" );
|
||||
|
||||
@a = map {
|
||||
print $fh "#$_";
|
||||
s/^\.\/gitolite\///i;
|
||||
s/-/_/g;
|
||||
s/\.\///;
|
||||
s/\//_/g;
|
||||
s/\.pm:/ -> /;
|
||||
s/use Gitolite:://;
|
||||
s/::/_/g;
|
||||
s/:/ -> /;
|
||||
s/;//;
|
||||
s/^(\S+) -> \1$//;
|
||||
s/.* -> Rc//;
|
||||
s/.* -> Common//;
|
||||
$_;
|
||||
} @a;
|
||||
|
||||
# open(my $fh, "|-", "cat > /tmp/junkg3");
|
||||
print $fh "digraph G {\n";
|
||||
print $fh $_ for @a;
|
||||
print $fh "}\n";
|
||||
close $fh;
|
|
@ -0,0 +1,77 @@
|
|||
#!/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,5 +1,4 @@
|
|||
#!/bin/bash
|
||||
# TODO: convert to perl!
|
||||
#!/bin/sh
|
||||
|
||||
# gitolite VREF to count number of changed/new files in a push
|
||||
|
||||
|
@ -32,9 +31,9 @@ nf=
|
|||
# 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 | wc -l`
|
||||
count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | perl -ne '}{print "$."'`
|
||||
|
||||
[[ $count -gt $max ]] && {
|
||||
[ $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" ] && {
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# gitolite VREF to detect duplicate public keys
|
||||
|
||||
# see gitolite doc/vref.mkd for what the arguments are
|
||||
sha=$3
|
||||
|
||||
# git sets this; and we don't want it at this point...
|
||||
unset GIT_DIR
|
||||
|
||||
# paranoia
|
||||
set -e
|
||||
|
||||
# setup the temp area
|
||||
export TMPDIR=$GL_REPO_BASE_ABS
|
||||
export tmp=$(mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX);
|
||||
trap "rm -rf $tmp" EXIT;
|
||||
|
||||
git archive $sha keydir | tar -C $tmp -xf -
|
||||
# DO NOT try, say, 'GIT_WORK_TREE=$tmp git checkout $sha'. It'll screw up
|
||||
# both the 'index' and 'HEAD' of the repo.git. Screwing up the index is
|
||||
# BAD because now it goes out of sync with $GL_ADMINDIR. Think of a push
|
||||
# that had a deleted pubkey but failed a hooklet for some reason. A
|
||||
# subsequent push that fixes the error will now result in a $GL_ADMINDIR
|
||||
# that still *has* that deleted pubkey!!
|
||||
|
||||
# And this is equally applicable to cases where you're using a
|
||||
# post-receive or similar hook to live update a web site or something,
|
||||
# which is a pretty common usage, I am given to understand.
|
||||
|
||||
cd $tmp
|
||||
|
||||
for f in `find keydir -name "*.pub"`
|
||||
do
|
||||
ssh-keygen -l -f "$f"
|
||||
done | perl -ane '
|
||||
die "FATAL: $F[2] is a duplicate of $seen{$F[1]}\n" if $seen{$F[1]};
|
||||
$seen{$F[1]} = $F[2];
|
||||
'
|
||||
|
||||
# as you can see, a vref can also 'die' if it wishes to, and it'll take the
|
||||
# whole update with it if it does. No messing around with sending back a
|
||||
# vref, having it run through the matches, and printing the DENIED message,
|
||||
# etc. However, if your push is running from a script, and that script is
|
||||
# looking for the word "DENIED" or something, then this won't work...
|
|
@ -1,5 +1,4 @@
|
|||
#!/bin/bash
|
||||
# TODO: convert to perl!
|
||||
#!/bin/sh
|
||||
|
||||
# gitolite VREF to find autogenerated files
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#!/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
|
|
@ -0,0 +1,80 @@
|
|||
#!/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
|
|
@ -0,0 +1,36 @@
|
|||
#!/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
|
|
@ -20,13 +20,22 @@ exec >&2
|
|||
main=`git config --file $GL_REPO_BASE/$repo.git/config --get gitolite.partialCopyOf`;
|
||||
[ -z "$main" ] && exit 0
|
||||
|
||||
rand=$RANDOM
|
||||
rand=$$
|
||||
export GL_BYPASS_ACCESS_CHECKS=1
|
||||
|
||||
git push -f $GL_REPO_BASE/$main.git $new:refs/heads/br-$rand || die "FATAL: failed to send $new"
|
||||
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/heads/br-$rand
|
||||
git update-ref -d refs/partial/br-$rand
|
||||
git update-ref $ref $new $old || die "FATAL: update-ref for $ref failed"
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
#!/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.
|
|
@ -0,0 +1,131 @@
|
|||
#!/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
|
|
@ -2,7 +2,7 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
@ -42,15 +42,13 @@ 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)$/ );
|
||||
_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
|
||||
_die "invalid repo name" if not( $repo and $repo =~ $REPONAME_PATT );
|
||||
_die "invalid user name" if not( $user and $user =~ $USERNAME_PATT );
|
||||
$ret = access( $repo, $user, $aa, $ref );
|
||||
|
||||
if ( $ret =~ /DENIED/ ) {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#!/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
|
|
@ -2,7 +2,7 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
#!/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
|
|
@ -3,7 +3,7 @@ use strict;
|
|||
use warnings;
|
||||
|
||||
use Getopt::Long;
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
@ -23,8 +23,7 @@ Examples:
|
|||
gitolite git-config -q repo gitweb.owner
|
||||
gitolite git-config -r repo gitweb
|
||||
|
||||
When the key is treated as a pattern, prints one key+value per line, tab
|
||||
separated:
|
||||
When the key is treated as a pattern, prints:
|
||||
|
||||
reponame<tab>key<tab>value<newline>
|
||||
|
||||
|
@ -57,10 +56,17 @@ if ( $repo ne '%' and $key ne '%' ) {
|
|||
|
||||
$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;
|
||||
|
||||
map { print "$repo\t$_\t" . $ret->{$_} . "\n" } sort keys %$ret unless $quiet;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
|
@ -20,20 +20,23 @@ usage() if @ARGV;
|
|||
my $user = $ENV{GL_USER} || '';
|
||||
print "hello" . ( $user ? " $user" : "" ) . ", this is gitolite3 " . version() . " on git " . substr( `git --version`, 12 ) . "\n";
|
||||
|
||||
_chdir("$ENV{GL_BINDIR}/commands");
|
||||
|
||||
print "list of " . ( $user ? "remote" : "gitolite" ) . " commands available:\n\n";
|
||||
|
||||
for my $c (`find . -type f|sort`) {
|
||||
chomp($c);
|
||||
$c =~ s(^./)();
|
||||
next unless -x $c;
|
||||
|
||||
# if it's from a remote client, show only what he is allowed
|
||||
next if $user and not $rc{COMMANDS}{$c};
|
||||
|
||||
print "\t$c\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`;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#!/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;
|
|
@ -4,17 +4,19 @@ use warnings;
|
|||
|
||||
use Getopt::Long;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for args
|
||||
Usage: gitolite info [-lc] [<repo name pattern>]
|
||||
Usage: gitolite info [-lc] [-ld] [<repo name pattern>]
|
||||
|
||||
List all repos/repo groups you can access.
|
||||
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
|
||||
|
@ -22,7 +24,7 @@ than a few thousand repos.
|
|||
=cut
|
||||
|
||||
# these two are globals
|
||||
my ( $lc, $patt ) = args();
|
||||
my ( $lc, $ld, $patt ) = args();
|
||||
|
||||
print_version();
|
||||
|
||||
|
@ -33,25 +35,26 @@ print "\n$rc{SITE_INFO}\n" if $rc{SITE_INFO};
|
|||
# ----------------------------------------------------------------------
|
||||
|
||||
sub args {
|
||||
my ( $lc, $patt ) = ( '', '' );
|
||||
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, $patt );
|
||||
return ( $lc, $ld, $patt );
|
||||
}
|
||||
|
||||
sub print_version {
|
||||
chomp( my $hn = `hostname -s` );
|
||||
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}\@$hn running gitolite3 " . version() . " on git $gv\n";
|
||||
print "hello $ENV{GL_USER}, this is " . ($ENV{USER} || "httpd") . "\@$hn running gitolite3 " . version() . " on git $gv\n";
|
||||
}
|
||||
|
||||
sub print_patterns {
|
||||
|
@ -60,8 +63,8 @@ sub print_patterns {
|
|||
# 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' flag for these
|
||||
listem( $repos, '', '', @aa );
|
||||
# but squelch the 'lc' and 'ld' flags for these
|
||||
}
|
||||
|
||||
sub print_phy_repos {
|
||||
|
@ -71,17 +74,24 @@ sub print_phy_repos {
|
|||
_chdir( $rc{GL_REPO_BASE} );
|
||||
$repos = list_phy_repos(1);
|
||||
@aa = qw(R W);
|
||||
listem( $repos, $lc, @aa );
|
||||
listem( $repos, $lc, $ld, @aa );
|
||||
}
|
||||
|
||||
sub listem {
|
||||
my ( $repos, $lc, @aa ) = @_;
|
||||
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" );
|
||||
|
@ -90,6 +100,7 @@ sub listem {
|
|||
next unless $perm =~ /\S/;
|
||||
print "$perm\t$repo";
|
||||
print "\t$creator" if $lc;
|
||||
print "\t$desc" if $ld;
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#!/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";
|
|
@ -0,0 +1,124 @@
|
|||
#!/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 );
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#!/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};
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
@ -21,12 +21,21 @@ Examples:
|
|||
ssh git@host perms foo + READERS user2
|
||||
ssh git@host perms foo + READERS user3
|
||||
|
||||
(Note: a legacy mode of piping in the entire permissions text directly is also
|
||||
supported. If you want to use it, don't mix it with the new "+/-" modes).
|
||||
----
|
||||
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++;
|
||||
|
@ -34,14 +43,34 @@ if ( $ARGV[0] eq '-l' ) {
|
|||
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" );
|
||||
_system( "gitolite", "trigger", "POST_CREATE", $repo, $ENV{GL_USER}, 'perms' );
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub getperms {
|
||||
my $repo = shift;
|
||||
_die "sorry you are not authorised" if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
|
||||
_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;
|
||||
|
@ -50,18 +79,20 @@ sub getperms {
|
|||
}
|
||||
|
||||
sub setperms {
|
||||
my $repo = shift;
|
||||
_die "sorry you are not authorised" if repo_missing($repo) or creator($repo) ne $ENV{GL_USER};
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
|
||||
print glrc('default-text');
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
export GL_BYPASS_ACCESS_CHECKS=1
|
||||
|
||||
git push "$@"
|
|
@ -0,0 +1,149 @@
|
|||
#!/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(@_);
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
#!/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;
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
#!/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";
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#!/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 "$@"
|
|
@ -0,0 +1,17 @@
|
|||
#!/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";
|
|
@ -0,0 +1,31 @@
|
|||
#!/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 "$@"
|
|
@ -0,0 +1,57 @@
|
|||
#!/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'");
|
||||
}
|
||||
}
|
|
@ -2,12 +2,15 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
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
|
||||
|
@ -22,9 +25,9 @@ my $repo = shift;
|
|||
my $on = ( shift eq 'on' );
|
||||
|
||||
if ( $repo eq '@all' ) {
|
||||
die "you are not authorized\n" if $ENV{GL_USER} and not is_admin();
|
||||
_die "you are not authorized" if $ENV{GL_USER} and not is_admin();
|
||||
} else {
|
||||
die "you are not authorized\n" if $ENV{GL_USER} and not owns($repo);
|
||||
_die "you are not authorized" if $ENV{GL_USER} and not owns($repo);
|
||||
}
|
||||
|
||||
my $msg = join( " ", @ARGV );
|
||||
|
|
18
src/gitolite
18
src/gitolite
|
@ -36,7 +36,8 @@ written.
|
|||
use FindBin;
|
||||
|
||||
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; }
|
||||
use lib $ENV{GL_BINDIR};
|
||||
BEGIN { $ENV{GL_LIBDIR} = "$ENV{GL_BINDIR}/lib"; }
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
|
@ -72,9 +73,10 @@ if ( $command eq 'setup' ) {
|
|||
} elsif ( $command eq 'trigger' ) {
|
||||
trigger(@args);
|
||||
|
||||
} elsif ( -x "$rc{GL_BINDIR}/commands/$command" ) {
|
||||
trace( 2, "attempting gitolite command $command" );
|
||||
run_command( $command, @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} );
|
||||
|
@ -98,11 +100,3 @@ sub args {
|
|||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub run_command {
|
||||
my $pgm = shift;
|
||||
my $fullpath = "$ENV{GL_BINDIR}/commands/$pgm";
|
||||
_die "$pgm not found or not executable" if not -x $fullpath;
|
||||
_system( $fullpath, @_ );
|
||||
exit 0;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
use FindBin;
|
||||
|
||||
BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; }
|
||||
use lib $ENV{GL_BINDIR};
|
||||
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;
|
||||
|
@ -26,6 +31,12 @@ if ( exists $ENV{G3T_USER} ) {
|
|||
_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);
|
||||
|
@ -47,19 +58,29 @@ sub in_file {
|
|||
}
|
||||
|
||||
sub in_http {
|
||||
_die 'http not yet implemented...';
|
||||
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" );
|
||||
gl_log( 'ssh', "ARGV=" . join( ",", @ARGV ), "SOC=" . ( $ENV{SSH_ORIGINAL_COMMAND} || '' ), "FROM=$ip" );
|
||||
|
||||
$ENV{SSH_ORIGINAL_COMMAND} ||= '';
|
||||
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;
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
@ -86,8 +107,8 @@ sub main {
|
|||
if ( repo_missing($repo) and access( $repo, $user, '^C', 'any' ) !~ /DENIED/ ) {
|
||||
require Gitolite::Conf::Store;
|
||||
Gitolite::Conf::Store->import;
|
||||
new_wild_repo( $repo, $user );
|
||||
gl_log( 'create', $repo, $user );
|
||||
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
|
||||
|
@ -104,9 +125,13 @@ sub main {
|
|||
}
|
||||
|
||||
trigger( 'PRE_GIT', $repo, $user, $aa, 'any', $verb );
|
||||
my $repodir = "'$rc{GL_REPO_BASE}/$repo.git'";
|
||||
_system( "git", "shell", "-c", "$verb $repodir" );
|
||||
trigger( 'POST_GIT', $repo, $user, $aa, 'any', $verb, times() );
|
||||
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 );
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
@ -136,12 +161,79 @@ sub parse_soc {
|
|||
exit 0;
|
||||
}
|
||||
|
||||
_die "unknown git/gitolite command: $soc";
|
||||
_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(\.\.$);
|
||||
_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";
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ package Gitolite::Common;
|
|||
usage tsh_run
|
||||
gen_lfn
|
||||
gl_log
|
||||
|
||||
dd
|
||||
t_start
|
||||
t_lap
|
||||
);
|
||||
#>>>
|
||||
use Exporter 'import';
|
||||
|
@ -63,7 +67,28 @@ sub dbg {
|
|||
}
|
||||
}
|
||||
|
||||
sub dd {
|
||||
local $ENV{D} = 1;
|
||||
dbg(@_);
|
||||
}
|
||||
|
||||
{
|
||||
use Time::HiRes;
|
||||
my %start_times;
|
||||
|
||||
sub t_start {
|
||||
my $name = shift || 'default';
|
||||
$start_times{$name} = [ Time::HiRes::gettimeofday() ];
|
||||
}
|
||||
|
||||
sub t_lap {
|
||||
my $name = shift || 'default';
|
||||
return Time::HiRes::tv_interval( $start_times{$name} );
|
||||
}
|
||||
}
|
||||
|
||||
sub _warn {
|
||||
gl_log( 'warn', @_ );
|
||||
if ( $ENV{D} and $ENV{D} >= 3 ) {
|
||||
cluck "WARNING: ", @_, "\n";
|
||||
} elsif ( defined( $ENV{D} ) ) {
|
||||
|
@ -72,6 +97,7 @@ sub _warn {
|
|||
warn "WARNING: ", @_, "\n";
|
||||
}
|
||||
}
|
||||
$SIG{__WARN__} = \&_warn;
|
||||
|
||||
sub _die {
|
||||
gl_log( 'die', @_ );
|
||||
|
@ -83,6 +109,7 @@ sub _die {
|
|||
die "FATAL: " . join( ",", @_ ) . "\n";
|
||||
}
|
||||
}
|
||||
$SIG{__DIE__} = \&_die;
|
||||
|
||||
sub usage {
|
||||
_warn(shift) if @_;
|
||||
|
@ -248,17 +275,18 @@ sub gl_log {
|
|||
my $tid = $ENV{GL_TID} ||= $$;
|
||||
|
||||
my $fh;
|
||||
logger_plus_stderr( "$ts no GL_LOGFILE env var", "$ts $msg" ) if not $ENV{GL_LOGFILE};
|
||||
open my $lfh, ">>", $ENV{GL_LOGFILE} or logger_plus_stderr( "open log failed: $!", $msg );
|
||||
logger_plus_stderr( "errors found before logging could be setup", "$msg" ) if not $ENV{GL_LOGFILE};
|
||||
open my $lfh, ">>", $ENV{GL_LOGFILE}
|
||||
or logger_plus_stderr( "errors found but logfile could not be created", "$ENV{GL_LOGFILE}: $!", "$msg" );
|
||||
print $lfh "$ts\t$tid\t$msg\n";
|
||||
close $lfh;
|
||||
}
|
||||
|
||||
sub logger_plus_stderr {
|
||||
open my $fh, "|-", "logger" or confess "it's really not my day is it...?\n";
|
||||
for ( "FATAL: have errors but logging failed!\n", @_ ) {
|
||||
print STDERR "$_\n";
|
||||
print $fh "$_\n";
|
||||
for ( @_ ) {
|
||||
print STDERR "FATAL: $_\n";
|
||||
print $fh "FATAL: $_\n";
|
||||
}
|
||||
exit 1;
|
||||
}
|
||||
|
@ -274,7 +302,7 @@ sub logger_plus_stderr {
|
|||
|
||||
sub tsh_try {
|
||||
my $cmd = shift; die "try: expects only one argument" if @_;
|
||||
$text = `( $cmd ) 2>&1; echo -n RC=\$?`;
|
||||
$text = `( $cmd ) 2>&1; printf RC=\$?`;
|
||||
if ( $text =~ s/RC=(\d+)$// ) {
|
||||
$rc = $1;
|
||||
trace( 3, $text );
|
|
@ -34,6 +34,10 @@ sub compile {
|
|||
# place to put the individual gl-conf files
|
||||
new_repos();
|
||||
store();
|
||||
|
||||
for my $repo ( @{ $rc{NEW_REPOS_CREATED} } ) {
|
||||
trigger( 'POST_CREATE', $repo );
|
||||
}
|
||||
}
|
||||
|
||||
sub parse {
|
||||
|
@ -58,10 +62,11 @@ sub parse {
|
|||
}
|
||||
} elsif ( $line =~ /^config (.+) = ?(.*)/ ) {
|
||||
my ( $key, $value ) = ( $1, $2 );
|
||||
$value =~ s/^['"](.*)["']$/$1/;
|
||||
my @validkeys = split( ' ', ( $rc{GIT_CONFIG_KEYS} || '' ) );
|
||||
push @validkeys, "gitolite-options\\..*";
|
||||
my @matched = grep { $key =~ /^$_$/ } @validkeys;
|
||||
_die "git config $key not allowed\ncheck GIT_CONFIG_KEYS in the rc file" if ( @matched < 1 );
|
||||
_die "git config '$key' not allowed\ncheck GIT_CONFIG_KEYS in the rc file" if ( @matched < 1 );
|
||||
_die "bad value '$value'" if $value =~ $UNSAFE_PATT;
|
||||
add_config( 1, $key, $value );
|
||||
} elsif ( $line =~ /^subconf (\S+)$/ ) {
|
|
@ -27,7 +27,7 @@ sub explode {
|
|||
my ( $file, $subconf, $out ) = @_;
|
||||
|
||||
# seed the 'seen' list if it's empty
|
||||
$included{ device_inode("conf/gitolite.conf") }++ unless %included;
|
||||
$included{ device_inode("gitolite.conf") }++ unless %included;
|
||||
|
||||
my $fh = _open( "<", $file );
|
||||
while (<$fh>) {
|
||||
|
@ -52,7 +52,7 @@ sub incsub {
|
|||
my $is_subconf = ( +shift eq 'subconf' );
|
||||
my ( $new_subconf, $include_glob, $current_subconf, $out ) = @_;
|
||||
|
||||
_die "subconf $current_subconf attempting to run 'subconf'\n" if $is_subconf and $current_subconf ne 'master';
|
||||
_die "subconf '$current_subconf' attempting to run 'subconf'\n" if $is_subconf and $current_subconf ne 'master';
|
||||
|
||||
_die "invalid include/subconf file/glob '$include_glob'"
|
||||
unless $include_glob =~ /^"(.+)"$/
|
||||
|
@ -63,7 +63,7 @@ sub incsub {
|
|||
|
||||
for my $file ( glob($include_glob) ) {
|
||||
_warn("included file not found: '$file'"), next unless -f $file;
|
||||
_die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$);
|
||||
_die "invalid include/subconf filename '$file'" unless $file =~ m(([^/]+).conf$);
|
||||
my $basename = $1;
|
||||
|
||||
next if already_included($file);
|
|
@ -32,6 +32,7 @@ our $data_version = '';
|
|||
our %repos;
|
||||
our %one_repo;
|
||||
our %groups;
|
||||
our %patterns;
|
||||
our %configs;
|
||||
our %one_config;
|
||||
our %split_conf;
|
||||
|
@ -67,11 +68,18 @@ my $last_repo = '';
|
|||
|
||||
sub access {
|
||||
my ( $repo, $user, $aa, $ref ) = @_;
|
||||
my $deny_rules = option( $repo, 'deny-rules' );
|
||||
_die "invalid user '$user'" if not( $user and $user =~ $USERNAME_PATT );
|
||||
sanity($repo);
|
||||
|
||||
my @rules;
|
||||
my $deny_rules;
|
||||
|
||||
load($repo);
|
||||
@rules = rules( $repo, $user );
|
||||
$deny_rules = option( $repo, 'deny-rules' );
|
||||
|
||||
# sanity check the only piece the user can control
|
||||
_die "invalid characters in ref or filename: $ref\n" unless $ref =~ $REF_OR_FILENAME_PATT;
|
||||
_die "invalid characters in ref or filename: '$ref'\n" unless $ref =~ $REF_OR_FILENAME_PATT;
|
||||
|
||||
# when a real repo doesn't exist, ^C is a pre-requisite for any other
|
||||
# check to give valid results.
|
||||
|
@ -80,8 +88,12 @@ sub access {
|
|||
$iret =~ s/\^C/$aa/;
|
||||
return $iret if $iret =~ /DENIED/;
|
||||
}
|
||||
# similarly, ^C must be denied if the repo exists
|
||||
if ( $aa eq '^C' and not repo_missing($repo) ) {
|
||||
trace( 2, "DENIED by existence" );
|
||||
return "$aa $ref $repo $user DENIED by existence";
|
||||
}
|
||||
|
||||
my @rules = rules( $repo, $user );
|
||||
trace( 2, scalar(@rules) . " rules found" );
|
||||
for my $r (@rules) {
|
||||
my $perm = $r->[1];
|
||||
|
@ -109,11 +121,14 @@ sub access {
|
|||
}
|
||||
|
||||
sub git_config {
|
||||
my ( $repo, $key ) = @_;
|
||||
my ( $repo, $key, $empty_values_OK ) = @_;
|
||||
$key ||= '.';
|
||||
|
||||
return {} if repo_missing($repo);
|
||||
load($repo);
|
||||
if (repo_missing($repo)) {
|
||||
load_common();
|
||||
} else {
|
||||
load($repo);
|
||||
}
|
||||
|
||||
# read comments bottom up
|
||||
my %ret =
|
||||
|
@ -144,6 +159,23 @@ sub git_config {
|
|||
# and the final map does this:
|
||||
# 'foo.bar'=>'repo' , 'foodbar'=>'repoD'
|
||||
|
||||
# now some of these will have an empty key; we need to delete them unless
|
||||
# we're told empty values are OK
|
||||
unless ($empty_values_OK) {
|
||||
my($k, $v);
|
||||
while (($k, $v) = each %ret) {
|
||||
delete $ret{$k} if not $v;
|
||||
}
|
||||
}
|
||||
|
||||
my($k, $v);
|
||||
my $creator = creator($repo);
|
||||
while (($k, $v) = each %ret) {
|
||||
$v =~ s/%GL_REPO/$repo/g;
|
||||
$v =~ s/%GL_CREATOR/$creator/g if $creator;
|
||||
$ret{$k} = $v;
|
||||
}
|
||||
|
||||
trace( 3, map { ( "$_" => "-> $ret{$_}" ) } ( sort keys %ret ) );
|
||||
return \%ret;
|
||||
}
|
||||
|
@ -156,8 +188,18 @@ sub option {
|
|||
return $ret->{$option};
|
||||
}
|
||||
|
||||
sub sanity {
|
||||
my $repo = shift;
|
||||
|
||||
_die "invalid repo '$repo'" if not( $repo and $repo =~ $REPOPATT_PATT );
|
||||
_die "'$repo' ends with a '/'" if $repo =~ m(/$);
|
||||
_die "'$repo' contains '..'" if $repo =~ $REPONAME_PATT and $repo =~ m(\.\.);
|
||||
}
|
||||
|
||||
sub repo_missing {
|
||||
my $repo = shift;
|
||||
sanity($repo);
|
||||
|
||||
return not -d "$rc{GL_REPO_BASE}/$repo.git";
|
||||
}
|
||||
|
||||
|
@ -177,11 +219,11 @@ sub load_common {
|
|||
|
||||
my $cc = "conf/gitolite.conf-compiled.pm";
|
||||
|
||||
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
|
||||
_die "parse '$cc' failed: " . ( $! or $@ ) unless do $cc;
|
||||
|
||||
if ( data_version_mismatch() ) {
|
||||
_system("gitolite setup");
|
||||
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
|
||||
_die "parse '$cc' failed: " . ( $! or $@ ) unless do $cc;
|
||||
_die "data version update failed; this is serious" if data_version_mismatch();
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +234,7 @@ sub load_1 {
|
|||
trace( 3, $repo );
|
||||
|
||||
if ( repo_missing($repo) ) {
|
||||
trace( 1, "repo '$repo' missing" );
|
||||
trace( 1, "repo '$repo' missing" ) if $repo =~ $REPONAME_PATT;
|
||||
return;
|
||||
}
|
||||
_chdir("$rc{GL_REPO_BASE}/$repo.git");
|
||||
|
@ -204,16 +246,16 @@ sub load_1 {
|
|||
}
|
||||
|
||||
if ( -f "gl-conf" ) {
|
||||
_warn "split conf not set, gl-conf present for $repo" if not $split_conf{$repo};
|
||||
return if not $split_conf{$repo};
|
||||
|
||||
my $cc = "gl-conf";
|
||||
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
|
||||
my $cc = "./gl-conf";
|
||||
_die "parse '$cc' failed: " . ( $! or $@ ) unless do $cc;
|
||||
|
||||
$last_repo = $repo;
|
||||
$repos{$repo} = $one_repo{$repo};
|
||||
$configs{$repo} = $one_config{$repo} if $one_config{$repo};
|
||||
} else {
|
||||
_die "split conf set, gl-conf not present for $repo" if $split_conf{$repo};
|
||||
_die "split conf set, gl-conf not present for '$repo'" if $split_conf{$repo};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,7 +278,7 @@ sub load_1 {
|
|||
|
||||
for my $r (@repos) {
|
||||
for my $u (@users) {
|
||||
push @rules, @{ $repos{$r}{$u} } if exists $repos{$r}{$u};
|
||||
push @rules, @{ $repos{$r}{$u} } if exists $repos{$r} and exists $repos{$r}{$u};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,9 +308,11 @@ sub load_1 {
|
|||
sub memberships {
|
||||
trace( 3, @_ );
|
||||
my ( $type, $base, $repo ) = @_;
|
||||
$repo ||= '';
|
||||
my @ret;
|
||||
my $base2 = '';
|
||||
|
||||
my @ret = ( $base, '@all' );
|
||||
@ret = ( $base, '@all' );
|
||||
|
||||
if ( $type eq 'repo' ) {
|
||||
# first, if a repo, say, pub/sitaram/project, has a gl-creator file
|
||||
|
@ -283,12 +327,16 @@ sub memberships {
|
|||
}
|
||||
}
|
||||
|
||||
for my $i ( keys %groups ) {
|
||||
if ( $base eq $i or $base =~ /^$i$/ or $base2 and ( $base2 eq $i or $base2 =~ /^$i$/ ) ) {
|
||||
push @ret, @{ $groups{$base} } if exists $groups{$base};
|
||||
push @ret, @{ $groups{$base2} } if $base2 and exists $groups{$base2};
|
||||
for my $i ( keys %{ $patterns{groups} } ) {
|
||||
if ( $base =~ /^$i$/ or $base2 and ( $base2 =~ /^$i$/ ) ) {
|
||||
push @ret, @{ $groups{$i} };
|
||||
}
|
||||
}
|
||||
|
||||
push @ret, @{ ext_grouplist($base) } if $type eq 'user' and $rc{GROUPLIST_PGM};
|
||||
|
||||
if ( $type eq 'user' and $repo and not repo_missing($repo) ) {
|
||||
# find the roles this user has when accessing this repo and add those
|
||||
# in as groupnames he is a member of. You need the already existing
|
||||
|
@ -338,6 +386,8 @@ sub user_roles {
|
|||
for (@roles) {
|
||||
# READERS u3 u4 @g1
|
||||
s/^\s+//; s/ +$//; s/=/ /; s/\s+/ /g; s/^\@//;
|
||||
next if /^#/;
|
||||
next unless /\S/;
|
||||
my ( $role, @members ) = split;
|
||||
# role = READERS, members = u3, u4, @g1
|
||||
if ( $role ne 'CREATOR' and not $rc{ROLES}{$role} ) {
|
||||
|
@ -365,12 +415,12 @@ sub generic_name {
|
|||
# get the creator name. For not-yet-born repos this is $ENV{GL_USER},
|
||||
# which should be set in all cases that we care about, viz., where we are
|
||||
# checking ^C permissions before new_wild_repo(), and the info command.
|
||||
# In particular, 'gitolite access' can't be used to check ^C perms.
|
||||
# In particular, 'gitolite access' can't be used to check ^C perms on wild
|
||||
# repos that contain "CREATOR" if GL_USER is not set.
|
||||
$creator = creator($base);
|
||||
|
||||
$base2 = $base;
|
||||
$base2 =~ s(/$creator/)(/CREATOR/) if $creator;
|
||||
$base2 =~ s(^$creator/)(CREATOR/) if $creator;
|
||||
$base2 =~ s(\b$creator\b)(CREATOR) if $creator;
|
||||
$base2 = '' if $base2 eq $base; # if there was no change
|
||||
|
||||
return $base2;
|
||||
|
@ -378,6 +428,8 @@ sub generic_name {
|
|||
|
||||
sub creator {
|
||||
my $repo = shift;
|
||||
sanity($repo);
|
||||
|
||||
return ( $ENV{GL_USER} || '' ) if repo_missing($repo);
|
||||
my $f = "$rc{GL_REPO_BASE}/$repo.git/gl-creator";
|
||||
my $creator = '';
|
||||
|
@ -385,6 +437,20 @@ sub creator {
|
|||
return $creator;
|
||||
}
|
||||
|
||||
{
|
||||
my %cache = ();
|
||||
|
||||
sub ext_grouplist {
|
||||
my $user = shift;
|
||||
my $pgm = $rc{GROUPLIST_PGM};
|
||||
return [] if not $pgm;
|
||||
|
||||
return $cache{$user} if $cache{$user};
|
||||
my @extgroups = map { s/^@?/@/; $_; } split ' ', `$rc{GROUPLIST_PGM} $user`;
|
||||
return ( $cache{$user} = \@extgroups );
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# api functions
|
||||
# ----------------------------------------------------------------------
|
|
@ -63,12 +63,20 @@ sub add_to_group {
|
|||
}
|
||||
|
||||
sub set_repolist {
|
||||
@repolist = @_;
|
||||
|
||||
@repolist = ();
|
||||
# ...sanity checks
|
||||
for (@repolist) {
|
||||
for (@_) {
|
||||
if ( check_subconf_repo_disallowed( $subconf, $_ ) ) {
|
||||
(my $repo = $_) =~ s/^\@$subconf\./locally modified \@/;
|
||||
$ignored{$subconf}{$repo} = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
_warn "explicit '.git' extension ignored for $_.git" if s/\.git$//;
|
||||
_die "bad reponame '$_'" if $_ !~ $REPOPATT_PATT;
|
||||
|
||||
push @repolist, $_;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,13 +111,6 @@ sub add_rule {
|
|||
|
||||
$nextseq++;
|
||||
for my $repo (@repolist) {
|
||||
if ( check_subconf_repo_disallowed( $subconf, $repo ) ) {
|
||||
my $repo = $repo;
|
||||
$repo =~ s/^\@$subconf\./locally modified \@/;
|
||||
$ignored{$subconf}{$repo} = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
push @{ $repos{$repo}{$user} }, [ $nextseq, $perm, $ref ];
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +138,7 @@ sub expand_list {
|
|||
for my $item (@list) {
|
||||
if ( $item =~ /^@/ and $item ne '@all' ) # nested group
|
||||
{
|
||||
_die "undefined group $item" unless $groups{$item};
|
||||
_die "undefined group '$item'" unless $groups{$item};
|
||||
# add those names to the list
|
||||
push @new_list, sort keys %{ $groups{$item} };
|
||||
} else {
|
||||
|
@ -155,13 +156,20 @@ sub new_repos {
|
|||
# normal repos
|
||||
my @repos = grep { $_ =~ $REPONAME_PATT and not /^@/ } sort keys %repos;
|
||||
# add in members of repo groups
|
||||
map { push @repos, keys %{ $groups{$_} } } grep { /^@/ } keys %repos;
|
||||
map { push @repos, keys %{ $groups{$_} } } grep { /^@/ and $_ ne '@all' } keys %repos;
|
||||
|
||||
for my $repo ( @{ sort_u( \@repos ) } ) {
|
||||
next unless $repo =~ $REPONAME_PATT; # skip repo patterns
|
||||
next if $repo =~ m(^\@|EXTCMD/); # skip groups and fake repos
|
||||
|
||||
new_repo($repo) if not -d "$repo.git";
|
||||
# use gl-conf as a sentinel
|
||||
hook_1($repo) if -d "$repo.git" and not -f "$repo.git/gl-conf";
|
||||
|
||||
if (not -d "$repo.git") {
|
||||
push @{ $rc{NEW_REPOS_CREATED} }, $repo;
|
||||
trigger( 'PRE_CREATE', $repo );
|
||||
new_repo($repo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,14 +185,14 @@ sub new_repo {
|
|||
}
|
||||
|
||||
sub new_wild_repo {
|
||||
my ( $repo, $user ) = @_;
|
||||
my ( $repo, $user, $aa ) = @_;
|
||||
_chdir( $rc{GL_REPO_BASE} );
|
||||
|
||||
trigger( 'PRE_CREATE', $repo, $user );
|
||||
trigger( 'PRE_CREATE', $repo, $user, $aa );
|
||||
new_repo($repo);
|
||||
_print( "$repo.git/gl-creator", $user );
|
||||
_print( "$repo.git/gl-perms", "$rc{DEFAULT_ROLE_PERMS}\n" ) if $rc{DEFAULT_ROLE_PERMS};
|
||||
trigger( 'POST_CREATE', $repo, $user );
|
||||
_print( "$repo.git/gl-perms", ( $rc{DEFAULT_ROLE_PERMS} ? "$rc{DEFAULT_ROLE_PERMS}\n" : "" ) );
|
||||
trigger( 'POST_CREATE', $repo, $user, $aa );
|
||||
|
||||
_chdir( $rc{GL_ADMIN_BASE} );
|
||||
}
|
||||
|
@ -250,15 +258,18 @@ sub store_1 {
|
|||
# warning: writes and *deletes* it from %repos and %configs
|
||||
my ($repo) = shift;
|
||||
trace( 3, $repo );
|
||||
return unless $repos{$repo} and -d "$repo.git";
|
||||
return unless ( $repos{$repo} or $configs{$repo} ) and -d "$repo.git";
|
||||
|
||||
my ( %one_repo, %one_config );
|
||||
|
||||
open( my $compiled_fh, ">", "$repo.git/gl-conf" ) or return;
|
||||
|
||||
$one_repo{$repo} = $repos{$repo};
|
||||
delete $repos{$repo};
|
||||
my $dumped_data = Data::Dumper->Dump( [ \%one_repo ], [qw(*one_repo)] );
|
||||
my $dumped_data = '';
|
||||
if ($repos{$repo}) {
|
||||
$one_repo{$repo} = $repos{$repo};
|
||||
delete $repos{$repo};
|
||||
$dumped_data = Data::Dumper->Dump( [ \%one_repo ], [qw(*one_repo)] );
|
||||
}
|
||||
|
||||
if ( $configs{$repo} ) {
|
||||
$one_config{$repo} = $configs{$repo};
|
||||
|
@ -277,6 +288,8 @@ sub store_common {
|
|||
my $cc = "conf/gitolite.conf-compiled.pm";
|
||||
my $compiled_fh = _open( ">", "$cc.new" );
|
||||
|
||||
my %patterns = ();
|
||||
|
||||
my $data_version = glrc('current-data-version');
|
||||
trace( 3, "data_version = $data_version" );
|
||||
print $compiled_fh Data::Dumper->Dump( [$data_version], [qw(*data_version)] );
|
||||
|
@ -290,7 +303,16 @@ sub store_common {
|
|||
my %groups = %{ inside_out( \%groups ) };
|
||||
$dumped_data = Data::Dumper->Dump( [ \%groups ], [qw(*groups)] );
|
||||
print $compiled_fh $dumped_data;
|
||||
|
||||
# save patterns in %groups for faster handling of multiple repos, such
|
||||
# as happens in the various POST_COMPILE scripts
|
||||
for my $k (keys %groups) {
|
||||
$patterns{groups}{$k} = 1 unless $k =~ $REPONAME_PATT;
|
||||
}
|
||||
}
|
||||
|
||||
print $compiled_fh Data::Dumper->Dump( [ \%patterns ], [qw(*patterns)] ) if %patterns;
|
||||
|
||||
print $compiled_fh Data::Dumper->Dump( [ \%split_conf ], [qw(*split_conf)] ) if %split_conf;
|
||||
|
||||
close $compiled_fh or _die "close compiled-conf failed: $!\n";
|
||||
|
@ -316,14 +338,13 @@ sub store_common {
|
|||
$hook_reset++;
|
||||
}
|
||||
|
||||
# propagate user hooks
|
||||
# propagate user-defined (custom) hooks to all repos
|
||||
ln_sf( "$rc{LOCAL_CODE}/hooks/common", "*", "$repo.git/hooks" ) if $rc{LOCAL_CODE};
|
||||
|
||||
# override/propagate gitolite defined hooks for all repos
|
||||
ln_sf( "$rc{GL_ADMIN_BASE}/hooks/common", "*", "$repo.git/hooks" );
|
||||
|
||||
# propagate admin hook
|
||||
# override/propagate gitolite defined hooks for the admin repo
|
||||
ln_sf( "$rc{GL_ADMIN_BASE}/hooks/gitolite-admin", "*", "$repo.git/hooks" ) if $repo eq 'gitolite-admin';
|
||||
|
||||
# g2 diff: no "site-wide" hooks (the stuff in between gitolite hooks
|
||||
# and user hooks) anymore. I don't think anyone used them anyway...
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ package SugarBox;
|
|||
|
||||
sub run_sugar_script {
|
||||
my ( $ss, $lref ) = @_;
|
||||
do $ss if -x $ss;
|
||||
do $ss if -r $ss;
|
||||
$lref = sugar_script($lref);
|
||||
return $lref;
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ sub sugar {
|
|||
# perl-ism; apart from keeping the full path separate from the
|
||||
# simple name, this also protects %rc from change by implicit
|
||||
# aliasing, which would happen if you touched $s itself
|
||||
my $sfp = "$ENV{GL_BINDIR}/syntactic-sugar/$s";
|
||||
my $sfp = _which("syntactic-sugar/$s", 'r');
|
||||
|
||||
_warn("skipped sugar script '$s'"), next if not -x $sfp;
|
||||
_warn("skipped sugar script '$s'"), next if not -r $sfp;
|
||||
$lines = SugarBox::run_sugar_script( $sfp, $lines );
|
||||
$lines = [ grep /\S/, map { cleanup_conf_line($_) } @$lines ];
|
||||
}
|
||||
|
@ -119,29 +119,29 @@ sub owner_desc {
|
|||
|
||||
# owner = "owner name"
|
||||
# -> config gitweb.owner = owner name
|
||||
# description = "some long description"
|
||||
# desc = "some long description"
|
||||
# -> config gitweb.description = some long description
|
||||
# category = "whatever..."
|
||||
# -> config gitweb.category = whatever...
|
||||
|
||||
# older formats:
|
||||
# repo = "some long description"
|
||||
# repo = "owner name" = "some long description"
|
||||
# repo "owner name" = "some long description"
|
||||
# -> config gitweb.owner = owner name
|
||||
# -> config gitweb.description = some long description
|
||||
|
||||
for my $line (@$lines) {
|
||||
if ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) {
|
||||
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
|
||||
push @ret, "repo $repo";
|
||||
push @ret, "config gitweb.description = $desc";
|
||||
push @ret, "config gitweb.owner = $owner" if $owner;
|
||||
} elsif ( $line =~ /^desc = (\S.*)/ ) {
|
||||
if ( $line =~ /^desc = (\S.*)/ ) {
|
||||
push @ret, "config gitweb.description = $1";
|
||||
} elsif ( $line =~ /^owner = (\S.*)/ ) {
|
||||
push @ret, "config gitweb.owner = $1";
|
||||
} elsif ( $line =~ /^category = (\S.*)/ ) {
|
||||
push @ret, "config gitweb.category = $1";
|
||||
} elsif ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) {
|
||||
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
|
||||
push @ret, "repo $repo";
|
||||
push @ret, "config gitweb.description = $desc";
|
||||
push @ret, "config gitweb.owner = $owner" if $owner;
|
||||
} else {
|
||||
push @ret, $line;
|
||||
}
|
|
@ -5,6 +5,25 @@ package Gitolite::Easy;
|
|||
# most/all functions in this module test $ENV{GL_USER}'s rights and
|
||||
# permissions so it needs to be set.
|
||||
|
||||
# "use"-ing this module
|
||||
# ----------------------------------------------------------------------
|
||||
# Using this module from within a gitolite trigger or command is easy; you
|
||||
# just need 'use lib $ENV{GL_LIBDIR};' before the 'use Gitolite::Easy;'.
|
||||
#
|
||||
# Using it from something completely outside gitolite requires a bit more
|
||||
# work. First, run 'gitolite query-rc -a' to find the correct values for
|
||||
# GL_BINDIR and GL_LIBDIR in your installation. Then use this code in your
|
||||
# external program, using the paths you just found:
|
||||
#
|
||||
# BEGIN {
|
||||
# $ENV{GL_BINDIR} = "/full/path/to/gitolite/src";
|
||||
# $ENV{GL_LIBDIR} = "/full/path/to/gitolite/src/lib";
|
||||
# }
|
||||
# use lib $ENV{GL_LIBDIR};
|
||||
# use Gitolite::Easy;
|
||||
|
||||
# API documentation
|
||||
# ----------------------------------------------------------------------
|
||||
# documentation for each function is at the top of the function.
|
||||
# Documentation is NOT in pod format; just read the source with a nice syntax
|
||||
# coloring text editor and you'll be happy enough. (I do not like POD; please
|
||||
|
@ -25,6 +44,8 @@ package Gitolite::Easy;
|
|||
%rc
|
||||
say
|
||||
say2
|
||||
_die
|
||||
_warn
|
||||
_print
|
||||
usage
|
||||
);
|
||||
|
@ -110,14 +131,18 @@ sub can_read {
|
|||
}
|
||||
|
||||
# can_write()
|
||||
# return true if $ENV{GL_USER} is set and can write to the given repo
|
||||
# return true if $ENV{GL_USER} is set and can write to the given repo.
|
||||
# Optional second argument can be '+' to check that instead of 'W'. Optional
|
||||
# third argument can be a full ref name instead of 'any'.
|
||||
|
||||
# shell equivalent
|
||||
# if gitolite access -q $REPONAME $GL_USER W; then ...
|
||||
sub can_write {
|
||||
valid_user();
|
||||
my $r = shift;
|
||||
return not( access( $r, $user, 'W', 'any' ) =~ /DENIED/ );
|
||||
my ($r, $aa, $ref) = @_;
|
||||
$aa ||= 'W';
|
||||
$ref ||= 'any';
|
||||
return not( access( $r, $user, $aa, $ref ) =~ /DENIED/ );
|
||||
}
|
||||
|
||||
# config()
|
|
@ -23,14 +23,23 @@ sub post_update {
|
|||
# this is the *real* post_update hook for gitolite
|
||||
|
||||
tsh_try("git ls-tree --name-only master");
|
||||
_die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/;
|
||||
_die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/m;
|
||||
|
||||
my $hooks_changed = 0;
|
||||
{
|
||||
local $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE};
|
||||
|
||||
tsh_try("git diff --name-only master");
|
||||
$hooks_changed++ if tsh_text() =~ m(/hooks/common/);
|
||||
# the leading slash ensure that this hooks/common directory is below
|
||||
# some top level directory, not *at* the top. That's LOCAL_CODE, and
|
||||
# it's actual name could be anything but it doesn't matter to us.
|
||||
|
||||
tsh_try("git checkout -f --quiet master");
|
||||
}
|
||||
_system("$ENV{GL_BINDIR}/gitolite compile");
|
||||
_system("$ENV{GL_BINDIR}/gitolite trigger POST_COMPILE");
|
||||
_system("gitolite compile");
|
||||
_system("gitolite setup --hooks-only") if $hooks_changed;
|
||||
_system("gitolite trigger POST_COMPILE");
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
@ -55,10 +64,7 @@ __DATA__
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
BEGIN {
|
||||
die "GL_BINDIR not set; aborting\n" unless $ENV{GL_BINDIR};
|
||||
}
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Hooks::PostUpdate;
|
||||
|
||||
# gitolite post-update hook (only for the admin repo)
|
|
@ -22,12 +22,14 @@ use warnings;
|
|||
sub update {
|
||||
# this is the *real* update hook for gitolite
|
||||
|
||||
bypass() if $ENV{GL_BYPASS_ACCESS_CHECKS};
|
||||
|
||||
my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = args(@ARGV);
|
||||
|
||||
trace( 1, 'update', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, @ARGV );
|
||||
|
||||
my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
|
||||
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret );
|
||||
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret, $oldsha, $newsha );
|
||||
_die $ret if $ret =~ /DENIED/;
|
||||
|
||||
check_vrefs( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa );
|
||||
|
@ -37,6 +39,13 @@ sub update {
|
|||
exit 0;
|
||||
}
|
||||
|
||||
sub bypass {
|
||||
require Cwd;
|
||||
Cwd->import;
|
||||
gl_log( 'update', getcwd(), '(' . ( $ENV{USER} || '?' ) . ')', 'bypass', @ARGV );
|
||||
exit 0;
|
||||
}
|
||||
|
||||
sub check_vrefs {
|
||||
my ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ) = @_;
|
||||
my $name_seen = 0;
|
||||
|
@ -52,15 +61,21 @@ sub check_vrefs {
|
|||
}
|
||||
} else {
|
||||
my ( $dummy, $pgm, @args ) = split '/', $vref;
|
||||
$pgm = "$ENV{GL_BINDIR}/VREF/$pgm";
|
||||
-x $pgm or die "$vref: helper program missing or unexecutable\n";
|
||||
$pgm = _which("VREF/$pgm", 'x');
|
||||
$pgm or _die "'$vref': helper program missing or unexecutable";
|
||||
|
||||
open( my $fh, "-|", $pgm, @_, $vref, @args ) or die "$vref: can't spawn helper program: $!\n";
|
||||
open( my $fh, "-|", $pgm, @_, $vref, @args ) or _die "'$vref': can't spawn helper program: $!";
|
||||
while (<$fh>) {
|
||||
# print non-vref lines and skip processing (for example,
|
||||
# normal STDOUT by a normal update hook)
|
||||
unless (m(^VREF/)) {
|
||||
print;
|
||||
next;
|
||||
}
|
||||
my ( $ref, $deny_message ) = split( ' ', $_, 2 );
|
||||
check_vref( $aa, $ref, $deny_message );
|
||||
}
|
||||
close($fh) or die $!
|
||||
close($fh) or _die $!
|
||||
? "Error closing sort pipe: $!"
|
||||
: "$vref: helper program exit status $?";
|
||||
}
|
||||
|
@ -73,6 +88,7 @@ sub check_vref {
|
|||
|
||||
my $ret = access( $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref );
|
||||
trace( 2, "access($ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref)", "-> $ret" );
|
||||
trigger( 'ACCESS_2', $ENV{GL_REPO}, $ENV{GL_USER}, $aa, $ref, $ret );
|
||||
_die "$ret" . ( $deny_message ? "\n$deny_message" : '' )
|
||||
if $ret =~ /DENIED/ and $ret !~ /by fallthru/;
|
||||
trace( 2, "remember, fallthru is success here!" ) if $ret =~ /by fallthru/;
|
||||
|
@ -143,11 +159,7 @@ __DATA__
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
BEGIN {
|
||||
exit 0 if $ENV{GL_BYPASS_ACCESS_CHECKS};
|
||||
die "GL_BINDIR not set; aborting\n" unless $ENV{GL_BINDIR};
|
||||
}
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use lib $ENV{GL_LIBDIR};
|
||||
use Gitolite::Hooks::Update;
|
||||
|
||||
# gitolite update hook
|
|
@ -9,6 +9,7 @@ package Gitolite::Rc;
|
|||
query_rc
|
||||
version
|
||||
trigger
|
||||
_which
|
||||
|
||||
$REMOTE_COMMAND_PATT
|
||||
$REF_OR_FILENAME_PATT
|
||||
|
@ -29,36 +30,43 @@ our %rc;
|
|||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# variables that could be overridden by the rc file
|
||||
# pre-populate some important rc keys
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
$rc{GL_BINDIR} = $ENV{GL_BINDIR};
|
||||
$rc{GL_REPO_BASE} = "$ENV{HOME}/repositories";
|
||||
$rc{GL_BINDIR} = $ENV{GL_BINDIR};
|
||||
$rc{GL_LIBDIR} = $ENV{GL_LIBDIR};
|
||||
|
||||
# these keys could be overridden by the rc file later
|
||||
$rc{GL_REPO_BASE} = "$ENV{HOME}/repositories";
|
||||
$rc{GL_ADMIN_BASE} = "$ENV{HOME}/.gitolite";
|
||||
$rc{LOG_TEMPLATE} = "$ENV{HOME}/.gitolite/logs/gitolite-%y-%m.log";
|
||||
|
||||
# variables that should probably never be changed but someone will want to, I'll bet...
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
$REMOTE_COMMAND_PATT = qr(^[- 0-9a-zA-Z\@\%_=+:,./]*$);
|
||||
$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][0-9a-zA-Z._\@/+ :,-]*$);
|
||||
$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@/+-]*$);
|
||||
$REPOPATT_PATT = qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/,-]*$);
|
||||
$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$);
|
||||
#<<<
|
||||
$REMOTE_COMMAND_PATT = qr(^[-0-9a-zA-Z._\@/+ :,\%=]*$);
|
||||
$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][-0-9a-zA-Z._\@/+ :,]*$);
|
||||
$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][-0-9a-zA-Z._\@/+]*$);
|
||||
$REPOPATT_PATT = qr(^\@?[[0-9a-zA-Z][-0-9a-zA-Z._\@/+\\^$|()[\]*?{},]*$);
|
||||
$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][-0-9a-zA-Z._\@+]*$);
|
||||
|
||||
$UNSAFE_PATT = qr([`~#\$\&()|;<>]);
|
||||
#>>>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# find the rc file and 'do' it
|
||||
# ----------------------------------------------------------------------
|
||||
my $current_data_version = "3.0";
|
||||
my $current_data_version = "3.2";
|
||||
|
||||
my $rc = glrc('filename');
|
||||
do $rc if -r $rc;
|
||||
if (-r $rc and -s $rc) {
|
||||
do $rc or die $@;
|
||||
}
|
||||
if ( defined($GL_ADMINDIR) ) {
|
||||
say2 "";
|
||||
say2 "FATAL: $rc seems to be for older gitolite; please see doc/g2migr.mkd\n" . "(online at http://sitaramc.github.com/gitolite/g3/g2migr.html)";
|
||||
say2 "FATAL: '$rc' seems to be for older gitolite; please see doc/g2migr.mkd\n" . "(online at http://sitaramc.github.com/gitolite/g2migr.html)";
|
||||
|
||||
exit 1;
|
||||
}
|
||||
|
@ -71,17 +79,19 @@ if ( defined($GL_ADMINDIR) ) {
|
|||
# ----------------------------------------------------------------------
|
||||
|
||||
# is the server/repo in a writable state (i.e., not down for maintenance etc)
|
||||
unshift @{ $rc{ACCESS_1} }, 'Writable::writable';
|
||||
unshift @{ $rc{ACCESS_1} }, 'Writable::access_1';
|
||||
|
||||
# (testing only) override the rc file silently
|
||||
# ----------------------------------------------------------------------
|
||||
# use an env var that is highly unlikely to appear in real life :)
|
||||
do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC};
|
||||
|
||||
# fix some env vars, setup gitolite internal "env" vars (aka rc vars)
|
||||
# setup some perl/rc/env vars
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}";
|
||||
unshift @INC, "$rc{LOCAL_CODE}/lib" if $rc{LOCAL_CODE};
|
||||
|
||||
$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}" unless $ENV{PATH} =~ /^$ENV{GL_BINDIR}:/;
|
||||
|
||||
{
|
||||
$rc{GL_TID} = $ENV{GL_TID} ||= $$;
|
||||
|
@ -127,7 +137,7 @@ sub glrc {
|
|||
} elsif ( $cmd eq 'current-data-version' ) {
|
||||
return $current_data_version;
|
||||
} else {
|
||||
_die "unknown argument to glrc: $cmd";
|
||||
_die "unknown argument to glrc: '$cmd'";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,17 +161,42 @@ sub query_rc {
|
|||
exit 0;
|
||||
}
|
||||
|
||||
my @res = map { $rc{$_} } grep { $rc{$_} } @vars;
|
||||
print join( "\t", @res ) . ( $nonl ? '' : "\n" ) if not $quiet and @res;
|
||||
# shell truth
|
||||
exit 0 if @res;
|
||||
exit 1;
|
||||
my $cv = \%rc; # current "value"
|
||||
while (@vars) {
|
||||
my $v = shift @vars;
|
||||
|
||||
# dig into the rc hash, using each var as a component
|
||||
if (not ref($cv)) {
|
||||
_warn "unused arguments...";
|
||||
last;
|
||||
} elsif (ref($cv) eq 'HASH') {
|
||||
$cv = $cv->{$v} || '';
|
||||
} elsif (ref($cv) eq 'ARRAY') {
|
||||
$cv = $cv->[$v] || '';
|
||||
} else {
|
||||
_die "dont know what to do with " . ref($cv) . " item in the rc file";
|
||||
}
|
||||
}
|
||||
|
||||
# we've run out of arguments so $cv is what we have. If we're supposed to
|
||||
# be quiet, we don't have to print anything so let's get that done first:
|
||||
exit ( $cv ? 0 : 1 ) if $quiet; # shell truth
|
||||
|
||||
# print values (notice we ignore the '-n' option if it's a ref)
|
||||
if (ref($cv) eq 'HASH') {
|
||||
print join("\n", sort keys %$cv), "\n" if %$cv;
|
||||
} elsif (ref($cv) eq 'ARRAY') {
|
||||
print join("\n", @$cv), "\n" if @$cv;
|
||||
} else {
|
||||
print $cv . ( $nonl ? '' : "\n" ) if $cv;
|
||||
}
|
||||
exit ( $cv ? 0 : 1 ); # shell truth
|
||||
}
|
||||
|
||||
sub version {
|
||||
my $version = '';
|
||||
$version = '(unknown)';
|
||||
for ("$rc{GL_ADMIN_BASE}/VERSION") {
|
||||
for ("$ENV{GL_BINDIR}/VERSION") {
|
||||
$version = slurp($_) if -r $_;
|
||||
}
|
||||
chomp($version);
|
||||
|
@ -173,7 +208,7 @@ sub trigger {
|
|||
|
||||
if ( exists $rc{$rc_section} ) {
|
||||
if ( ref( $rc{$rc_section} ) ne 'ARRAY' ) {
|
||||
_die "$rc_section section in rc file is not a perl list";
|
||||
_die "'$rc_section' section in rc file is not a perl list";
|
||||
} else {
|
||||
for my $s ( @{ $rc{$rc_section} } ) {
|
||||
my ( $pgm, @args ) = split ' ', $s;
|
||||
|
@ -181,13 +216,13 @@ sub trigger {
|
|||
if ( my ( $module, $sub ) = ( $pgm =~ /^(.*)::(\w+)$/ ) ) {
|
||||
|
||||
require Gitolite::Triggers;
|
||||
trace(1, 'trigger', $module, $sub, @args, $rc_section, @_ );
|
||||
trace( 1, 'trigger', $module, $sub, @args, $rc_section, @_ );
|
||||
Gitolite::Triggers::run( $module, $sub, @args, $rc_section, @_ );
|
||||
|
||||
} else {
|
||||
$pgm = "$ENV{GL_BINDIR}/triggers/$pgm";
|
||||
$pgm = _which("triggers/$pgm", 'x');
|
||||
|
||||
_warn("skipped command '$s'"), next if not -x $pgm;
|
||||
_warn("skipped command '$s'"), next if not $pgm;
|
||||
trace( 2, "command: $s" );
|
||||
_system( $pgm, @args, $rc_section, @_ ); # they better all return with 0 exit codes!
|
||||
}
|
||||
|
@ -198,25 +233,56 @@ sub trigger {
|
|||
trace( 2, "'$rc_section' not found in rc" );
|
||||
}
|
||||
|
||||
sub _which {
|
||||
# looks for a file in LOCAL_CODE or GL_BINDIR. Returns whichever exists
|
||||
# (LOCAL_CODE preferred if defined) or 0 if not found.
|
||||
my $file = shift;
|
||||
my $mode = shift; # could be 'x' or 'r'
|
||||
|
||||
my @files = ("$rc{GL_BINDIR}/$file");
|
||||
unshift @files, ("$rc{LOCAL_CODE}/$file") if $rc{LOCAL_CODE};
|
||||
|
||||
for my $f ( @files ) {
|
||||
return $f if -x $f;
|
||||
return $f if -r $f and $mode eq 'r';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
=for args
|
||||
Usage: gitolite query-rc -a
|
||||
gitolite query-rc [-n] <list of rc variables>
|
||||
gitolite query-rc [-n] [-q] rc-variable
|
||||
|
||||
-a print all variables and values
|
||||
-n do not append a newline
|
||||
-a print all variables and values (first level only)
|
||||
-n do not append a newline if variable is scalar
|
||||
-q exit code only (shell truth; 0 is success)
|
||||
|
||||
Example:
|
||||
Query the rc hash. Second and subsequent arguments dig deeper into the hash.
|
||||
The examples are for the default configuration; yours may be different.
|
||||
|
||||
gitolite query-rc GL_ADMIN_BASE UMASK
|
||||
# prints "/home/git/.gitolite<tab>0077" or similar
|
||||
Single values:
|
||||
gitolite query-rc GL_ADMIN_BASE # prints "/home/git/.gitolite" or similar
|
||||
gitolite query-rc UMASK # prints "63" (that's 0077 in decimal!)
|
||||
|
||||
Hashes:
|
||||
gitolite query-rc COMMANDS
|
||||
# prints "desc", "help", "info", "perms", "writable", one per line
|
||||
gitolite query-rc COMMANDS help # prints 1
|
||||
gitolite query-rc -q COMMANDS help # prints nothing; exit code is 0
|
||||
gitolite query-rc COMMANDS fork # prints nothing; exit code is 1
|
||||
|
||||
Arrays (somewhat less useful):
|
||||
gitolite query-rc POST_GIT # prints nothing; exit code is 0
|
||||
gitolite query-rc POST_COMPILE # prints 4 lines
|
||||
gitolite query-rc POST_COMPILE 0 # prints the first of those 4 lines
|
||||
|
||||
Explore:
|
||||
gitolite query-rc -a
|
||||
# prints all known variables and values, one per line
|
||||
|
||||
Note: '-q' is best used with only one variable.
|
||||
# prints all first level variables and values, one per line. Any that are
|
||||
# listed as HASH or ARRAY can be explored further in subsequent commands.
|
||||
=cut
|
||||
|
||||
sub args {
|
||||
|
@ -247,8 +313,21 @@ __DATA__
|
|||
|
||||
# (Tip: perl allows a comma after the last item in a list also!)
|
||||
|
||||
# HELP for commands (see COMMANDS list below) can be had by running the
|
||||
# command with "-h" as the sole argument.
|
||||
|
||||
# HELP for all the other external programs (the syntactic sugar helpers and
|
||||
# the various programs/functions in the 8 trigger lists), can be found in
|
||||
# doc/non-core.mkd (http://sitaramc.github.com/gitolite/non-core.html) or in
|
||||
# the corresponding source file itself.
|
||||
|
||||
%RC = (
|
||||
# if you're using mirroring, you need a hostname. This is *one* simple
|
||||
# word, not a full domain name. See documentation if in doubt
|
||||
# HOSTNAME => 'darkstar',
|
||||
UMASK => 0077,
|
||||
|
||||
# look in the "GIT-CONFIG" section in the README for what to do
|
||||
GIT_CONFIG_KEYS => '',
|
||||
|
||||
# comment out if you don't need all the extra detail in the logfile
|
||||
|
@ -256,9 +335,9 @@ __DATA__
|
|||
|
||||
# settings used by external programs; uncomment and change as needed. You
|
||||
# can add your own variables for use in your own external programs; take a
|
||||
# look at the cpu-time and desc commands for perl and shell samples.
|
||||
# look at the info and desc commands for perl and shell samples.
|
||||
|
||||
# used by the cpu-time command
|
||||
# used by the CpuTime trigger
|
||||
# DISPLAY_CPU_TIME => 1,
|
||||
# CPU_TIME_WARN_LIMIT => 0.1,
|
||||
# used by the desc command
|
||||
|
@ -282,10 +361,14 @@ __DATA__
|
|||
COMMANDS =>
|
||||
{
|
||||
'help' => 1,
|
||||
'info' => 1,
|
||||
'desc' => 1,
|
||||
# 'fork' => 1,
|
||||
'info' => 1,
|
||||
# 'mirror' => 1,
|
||||
'perms' => 1,
|
||||
# 'sskm' => 1,
|
||||
'writable' => 1,
|
||||
# 'D' => 1,
|
||||
},
|
||||
|
||||
# comment out or uncomment as needed
|
||||
|
@ -293,6 +376,17 @@ __DATA__
|
|||
SYNTACTIC_SUGAR =>
|
||||
[
|
||||
# 'continuation-lines',
|
||||
# 'keysubdirs-as-groups',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence to modify the input (arguments and environment)
|
||||
INPUT =>
|
||||
[
|
||||
# 'CpuTime::input',
|
||||
# 'Shell::input',
|
||||
# 'Alias::input',
|
||||
# 'Mirroring::input',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
|
@ -302,13 +396,11 @@ __DATA__
|
|||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence at the start, before a git operation has started
|
||||
# these will run in sequence just before the actual git command is invoked
|
||||
PRE_GIT =>
|
||||
[
|
||||
# if you use this, make this the first item in the list
|
||||
# 'renice 10',
|
||||
|
||||
# see docs ("list of non-core programs shipped") for details
|
||||
# 'Mirroring::pre_git',
|
||||
# 'partial-copy',
|
||||
],
|
||||
|
||||
|
@ -319,15 +411,21 @@ __DATA__
|
|||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence at the end, after a git operation has ended
|
||||
# these will run in sequence after the git command returns
|
||||
POST_GIT =>
|
||||
[
|
||||
# if you use this, make this the last item in the list
|
||||
# 'cpu-time',
|
||||
# 'Mirroring::post_git',
|
||||
# 'CpuTime::post_git',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence after a new wild repo is created
|
||||
# these will run in sequence before a new wild repo is created
|
||||
PRE_CREATE =>
|
||||
[
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence after a new repo is created
|
||||
POST_CREATE =>
|
||||
[
|
||||
'post-compile/update-git-configs',
|
|
@ -6,19 +6,30 @@ package Gitolite::Setup;
|
|||
=for args
|
||||
Usage: gitolite setup [<option>]
|
||||
|
||||
-pk, --pubkey <file> pubkey file name
|
||||
Setup gitolite, compile conf, run the POST_COMPILE trigger (see rc file) and
|
||||
propagate hooks.
|
||||
|
||||
Setup gitolite, compile conf, and fixup hooks. The pubkey is required on the
|
||||
first run.
|
||||
-a, --admin <name> admin name
|
||||
-pk, --pubkey <file> pubkey file name
|
||||
-ho, --hooks-only skip other steps and just propagate hooks
|
||||
|
||||
First run: either the pubkey or the admin name is *required*, depending on
|
||||
whether you're using ssh mode or http mode.
|
||||
|
||||
Subsequent runs:
|
||||
|
||||
- 'gitolite setup': fix up hooks if you brought in repos from outside, or if
|
||||
someone has been playing around with the hooks and may have deleted some.
|
||||
- Without options, 'gitolite setup' is a general "fix up everything" command
|
||||
(for example, if you brought in repos from outside, or someone messed
|
||||
around with the hooks, or you made an rc file change that affects access
|
||||
rules, etc.)
|
||||
|
||||
- '-pk' can be used to replace the admin key; useful if you lost the admin's
|
||||
private key but do have shell access to the server.
|
||||
|
||||
- '-ho' is mainly for scripting use. Do not combine with other options.
|
||||
|
||||
- '-a' is ignored
|
||||
|
||||
- 'gitolite setup -pk YourName.pub': replace keydir/YourName.pub and
|
||||
recompile/push. Useful if you lost your key. In fact you can do this for
|
||||
any key in keydir (but not in subdirectories).
|
||||
=cut
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
@ -40,12 +51,15 @@ use warnings;
|
|||
# ----------------------------------------------------------------------
|
||||
|
||||
sub setup {
|
||||
my ( $admin, $pubkey, $argv ) = args();
|
||||
setup_glrc();
|
||||
setup_gladmin( $admin, $pubkey, $argv );
|
||||
my ( $admin, $pubkey, $h_only, $argv ) = args();
|
||||
|
||||
_system("$ENV{GL_BINDIR}/gitolite compile");
|
||||
_system("$ENV{GL_BINDIR}/gitolite trigger POST_COMPILE");
|
||||
unless ($h_only) {
|
||||
setup_glrc();
|
||||
setup_gladmin( $admin, $pubkey, $argv );
|
||||
|
||||
_system("gitolite compile");
|
||||
_system("gitolite trigger POST_COMPILE");
|
||||
}
|
||||
|
||||
hook_repos(); # all of them, just to be sure
|
||||
}
|
||||
|
@ -55,30 +69,33 @@ sub setup {
|
|||
sub args {
|
||||
my $admin = '';
|
||||
my $pubkey = '';
|
||||
my $h_only = 0;
|
||||
my $help = 0;
|
||||
my $argv = join( " ", @ARGV );
|
||||
|
||||
GetOptions(
|
||||
'admin|a=s' => \$admin,
|
||||
'pubkey|pk=s' => \$pubkey,
|
||||
'help|h' => \$help,
|
||||
'admin|a=s' => \$admin,
|
||||
'pubkey|pk=s' => \$pubkey,
|
||||
'hooks-only|ho' => \$h_only,
|
||||
'help|h' => \$help,
|
||||
) or usage();
|
||||
|
||||
usage() if $help or ( $pubkey and $admin );
|
||||
usage() if $h_only and ($admin or $pubkey);
|
||||
|
||||
if ($pubkey) {
|
||||
$pubkey =~ /\.pub$/ or _die "$pubkey name does not end in .pub";
|
||||
$pubkey =~ /\@/ and _die "$pubkey name contains '\@'";
|
||||
tsh_try("cat $pubkey") or _die "$pubkey not a readable file";
|
||||
tsh_lines() == 1 or _die "$pubkey must have exactly one line";
|
||||
tsh_try("ssh-keygen -l -f $pubkey") or _die "$pubkey does not seem to be a valid ssh pubkey file";
|
||||
$pubkey =~ /\.pub$/ or _die "'$pubkey' name does not end in .pub";
|
||||
$pubkey =~ /\@/ and _die "'$pubkey' name contains '\@'";
|
||||
tsh_try("cat $pubkey") or _die "'$pubkey' not a readable file";
|
||||
tsh_lines() == 1 or _die "'$pubkey' must have exactly one line";
|
||||
tsh_try("ssh-keygen -l -f $pubkey") or _die "'$pubkey' does not seem to be a valid ssh pubkey file";
|
||||
|
||||
$admin = $pubkey;
|
||||
$admin =~ s(.*/)();
|
||||
$admin =~ s/\.pub$//;
|
||||
}
|
||||
|
||||
return ( $admin || '', $pubkey || '', $argv );
|
||||
return ( $admin || '', $pubkey || '', $h_only || 0, $argv );
|
||||
}
|
||||
|
||||
sub setup_glrc {
|
||||
|
@ -87,7 +104,7 @@ sub setup_glrc {
|
|||
|
||||
sub setup_gladmin {
|
||||
my ( $admin, $pubkey, $argv ) = @_;
|
||||
_die "no existing conf file found, '-a' required"
|
||||
_die "'-pk' or '-a' required; see 'gitolite setup -h' for more"
|
||||
if not $admin and not -f "$rc{GL_ADMIN_BASE}/conf/gitolite.conf";
|
||||
|
||||
# reminder: 'admin files' are in ~/.gitolite, 'admin repo' is
|
||||
|
@ -102,9 +119,6 @@ sub setup_gladmin {
|
|||
_mkdir( $rc{GL_ADMIN_BASE} );
|
||||
_chdir( $rc{GL_ADMIN_BASE} );
|
||||
|
||||
tsh_try("cd \$GL_BINDIR; git describe --tags --long --dirty=-dt 2>/dev/null")
|
||||
and _print( "VERSION", tsh_text() );
|
||||
|
||||
_mkdir("conf");
|
||||
_mkdir("logs");
|
||||
my $conf;
|
||||
|
@ -138,8 +152,8 @@ sub setup_gladmin {
|
|||
tsh_try("git config --get user.email") or tsh_run( "git config user.email $ENV{USER}\@" . `hostname` );
|
||||
tsh_try("git config --get user.name") or tsh_run( "git config user.name '$ENV{USER} on '" . `hostname` );
|
||||
tsh_try("git diff --cached --quiet")
|
||||
or tsh_try("git commit -am 'gl-setup $argv'")
|
||||
or die "setup failed to commit to the admin repo";
|
||||
or tsh_try("git commit -am 'gitolite setup $argv'")
|
||||
or _die "setup failed to commit to the admin repo";
|
||||
delete $ENV{GIT_WORK_TREE};
|
||||
}
|
||||
|
|
@ -8,15 +8,18 @@ package Gitolite::Test;
|
|||
try
|
||||
put
|
||||
text
|
||||
lines
|
||||
dump
|
||||
confreset
|
||||
confadd
|
||||
cmp
|
||||
md5sum
|
||||
);
|
||||
#>>>
|
||||
use Exporter 'import';
|
||||
use File::Path qw(mkpath);
|
||||
use Carp qw(carp cluck croak confess);
|
||||
use Digest::MD5 qw(md5_hex);
|
||||
|
||||
use Gitolite::Common;
|
||||
|
||||
|
@ -25,6 +28,7 @@ BEGIN {
|
|||
*{'try'} = \&Tsh::try;
|
||||
*{'put'} = \&Tsh::put;
|
||||
*{'text'} = \&Tsh::text;
|
||||
*{'lines'} = \&Tsh::lines;
|
||||
*{'cmp'} = \&Tsh::cmp;
|
||||
}
|
||||
|
||||
|
@ -33,6 +37,12 @@ use warnings;
|
|||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# make sure the user is ready for it
|
||||
if (not $ENV{GITOLITE_TEST} or $ENV{GITOLITE_TEST} ne 'y') {
|
||||
print "Bail out! See t/README for information on how to run the tests.\n";
|
||||
exit 255;
|
||||
}
|
||||
|
||||
# required preamble for all tests
|
||||
try "
|
||||
DEF gsh = /TRACE: gsh.SOC=/
|
||||
|
@ -43,14 +53,15 @@ try "
|
|||
DEF ADMIN_PUSH = AP_2 %1; glt push admin origin; ok; gsh; /master -> master/
|
||||
|
||||
DEF CS_1 = pwd; //tmp/tsh_tempdir.*gitolite-admin/; git remote -v; ok; /file://gitolite-admin/
|
||||
DEF CHECK_SETUP = CS_1; git log; ok; /6b18ec2ab0f765122ec133959b36c57f77d4565c/
|
||||
DEF CHECK_SETUP = CS_1; git log; ok; /fa7564c1b903ea3dce49314753f25b34b9e0cea0/
|
||||
|
||||
DEF CLONE = glt clone %1 file:///%2
|
||||
DEF PUSH = glt push %1 origin
|
||||
|
||||
# clean install
|
||||
mkdir -p $ENV{HOME}/bin
|
||||
ln -sf $ENV{PWD}/src/gitolite $ENV{PWD}/t/glt ~/bin
|
||||
ln -sf $ENV{PWD}/t/glt ~/bin
|
||||
./install -ln
|
||||
cd; rm -vrf .gito* repositories
|
||||
git config --global user.name \"gitolite tester\"
|
||||
git config --global user.email \"tester\@example.com\"
|
||||
|
@ -99,4 +110,12 @@ sub confadd {
|
|||
put "|cat >> conf/$file", $string;
|
||||
}
|
||||
|
||||
sub md5sum {
|
||||
my $out = '';
|
||||
for my $file (@_) {
|
||||
$out .= md5_hex(slurp($file)) . " $file\n";
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
1;
|
|
@ -261,7 +261,12 @@ sub rc_lines {
|
|||
$cmd = shift @cmds;
|
||||
|
||||
# is the current command a "testing" command?
|
||||
my $testing_cmd = ( $cmd =~ m(^ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$) or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$) or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$) );
|
||||
my $testing_cmd = (
|
||||
$cmd =~ m(^ok(?:\s+or\s+(.*))?$)
|
||||
or $cmd =~ m(^!ok(?:\s+or\s+(.*))?$)
|
||||
or $cmd =~ m(^/(.*?)/(?:\s+or\s+(.*))?$)
|
||||
or $cmd =~ m(^!/(.*?)/(?:\s+or\s+(.*))?$)
|
||||
);
|
||||
|
||||
# warn if the previous command failed but rc is not being checked
|
||||
if ( $rc and not $testing_cmd ) {
|
||||
|
@ -333,7 +338,7 @@ sub _sh {
|
|||
# TODO: switch to IPC::Open3 or something...?
|
||||
|
||||
dbg( 4, " running: ( $cmd ) 2>&1" );
|
||||
$text = `( $cmd ) 2>&1; echo -n RC=\$?`;
|
||||
$text = `( $cmd ) 2>&1; /bin/echo -n RC=\$?`;
|
||||
$lec = $cmd;
|
||||
dbg( 4, " results:\n$text" );
|
||||
|
||||
|
@ -473,9 +478,9 @@ sub fail {
|
|||
}
|
||||
|
||||
sub cmp {
|
||||
# compare input string with text()
|
||||
my $text = text();
|
||||
my $in = shift;
|
||||
# compare input string with second input string or text()
|
||||
my $in = shift;
|
||||
my $text = ( @_ ? +shift : text() );
|
||||
|
||||
if ( $text eq $in ) {
|
||||
ok();
|
||||
|
@ -583,7 +588,7 @@ sub dummy_commits {
|
|||
test_tick();
|
||||
next;
|
||||
}
|
||||
my $ts = ( $tick ? localtime($tick) : localtime() );
|
||||
my $ts = ( $tick ? gmtime( $tick + 19800 ) : gmtime() );
|
||||
_sh("echo $f at $ts >> $f && git add $f && git commit -m '$f at $ts'");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package Gitolite::Triggers::Alias;
|
||||
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# aliasing a repo to another
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
=for usage
|
||||
|
||||
Why:
|
||||
|
||||
We had an existing repo "foo" that lots of people use. We wanted to
|
||||
rename it to "foo/code", so that related repos "foo/upstream" and
|
||||
"foo/docs" (both containing stuff we did not want to put in "foo") could
|
||||
also be made and then the whole thing would be structured nicely.
|
||||
|
||||
At the same time we did not want to *force* all the users to change the
|
||||
name. At least git operations should still work with the old name,
|
||||
although it is OK for "info" and other "commands" to display/require the
|
||||
proper name (i.e., the new name).
|
||||
|
||||
How:
|
||||
|
||||
* add a new variable REPO_ALIASES to the rc file, with entries like:
|
||||
|
||||
REPO_ALIASES =>
|
||||
{
|
||||
'foo' => 'foo/code',
|
||||
}
|
||||
|
||||
* add the following line to the INPUT section in the rc file:
|
||||
|
||||
'Alias::input',
|
||||
|
||||
Notes:
|
||||
|
||||
* only git operations (clone/fetch/push) are alias aware. Nothing else in
|
||||
gitolite, such as all the gitolite commands etc., are alias-aware and will
|
||||
always use/require the proper repo name.
|
||||
|
||||
* http mode has not been tested and will not be. If someone has the time to
|
||||
test it and make it work please let me know.
|
||||
|
||||
* funnily enough, this even works with mirroring! That is, a master can
|
||||
push a repo "foo" to a slave per its configuration, while the slave thinks
|
||||
it is getting repo "bar" from the master per its configuration.
|
||||
|
||||
Just make sure to put the Alias::input line *before* the Mirroring::input
|
||||
line in the rc file on the slave.
|
||||
|
||||
However, it will probably not work with redirected pushes unless you setup
|
||||
the opposite alias ("bar" -> "foo") on master.
|
||||
=cut
|
||||
|
||||
sub input {
|
||||
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
|
||||
my $user = $ARGV[0] || '@all'; # user name is undocumented for now
|
||||
|
||||
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /(?:$git_commands) '\/?(\S+)'$/ ) {
|
||||
my $repo = $1;
|
||||
( my $norm = $repo ) =~ s/\.git$//; # normalised repo name
|
||||
|
||||
my $target;
|
||||
|
||||
return unless $target = $rc{REPO_ALIASES}{$norm};
|
||||
$target = $target->{$user} if ref($target) eq 'HASH';
|
||||
return unless $target;
|
||||
|
||||
_warn "'$norm' is an alias for '$target'";
|
||||
|
||||
$ENV{SSH_ORIGINAL_COMMAND} =~ s/'\/?$repo'/'$target'/;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,24 @@
|
|||
package Gitolite::Triggers::AutoCreate;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# perl trigger set for stuff to do with auto-creating repos
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# to deny auto-create on read access, add 'AutoCreate::deny_R' to the
|
||||
# PRE_CREATE trigger list
|
||||
sub deny_R {
|
||||
die "autocreate denied\n" if $_[3] and $_[3] eq 'R';
|
||||
return;
|
||||
}
|
||||
|
||||
# to deny auto-create on read *and* write access, add 'AutoCreate::deny_RW' to
|
||||
# the PRE_CREATE trigger list. This means you can only create repos using the
|
||||
# 'create' command, (which needs to be enabled in the COMMANDS list).
|
||||
sub deny_RW {
|
||||
die "autocreate denied\n" if $_[3] and ( $_[3] eq 'R' or $_[3] eq 'W' );
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,52 @@
|
|||
package Gitolite::Triggers::CpuTime;
|
||||
|
||||
use Time::HiRes;
|
||||
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# cpu and elapsed times for gitolite+git operations
|
||||
# ----------------------------------------------------------------------
|
||||
# uncomment the appropriate lines in the rc file to enable this
|
||||
|
||||
# Ideally, you will (a) write your own code with a different filename so later
|
||||
# gitolite upgrades won't overwrite your copy, (b) add appropriate variables
|
||||
# to the rc file, and (c) change your rc file to call your program instead.
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
my $start_time;
|
||||
|
||||
sub input {
|
||||
_warn "something wrong with the invocation of CpuTime::input" if $ENV{GL_TID} ne $$;
|
||||
$start_time = [ Time::HiRes::gettimeofday() ];
|
||||
}
|
||||
|
||||
sub post_git {
|
||||
_warn "something wrong with the invocation of CpuTime::post_git" if $ENV{GL_TID} ne $$;
|
||||
|
||||
my ( $trigger, $repo, $user, $aa, $ref, $verb ) = @_;
|
||||
my ( $utime, $stime, $cutime, $cstime ) = times();
|
||||
my $elapsed = Time::HiRes::tv_interval($start_time);
|
||||
|
||||
gl_log( 'times', $utime, $stime, $cutime, $cstime, $elapsed );
|
||||
|
||||
# now do whatever you want with the data; the following is just an example.
|
||||
|
||||
if ( my $limit = $rc{CPU_TIME_WARN_LIMIT} ) {
|
||||
my $total = $utime + $cutime + $stime + $cstime;
|
||||
# some code to send an email or whatever...
|
||||
say2 "limit = $limit, actual = $total" if $total > $limit;
|
||||
}
|
||||
|
||||
if ( $rc{DISPLAY_CPU_TIME} ) {
|
||||
say2 "perf stats for $verb on repo '$repo':";
|
||||
say2 " user CPU time: " . ( $utime + $cutime );
|
||||
say2 " sys CPU time: " . ( $stime + $cstime );
|
||||
say2 " elapsed time: " . $elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,232 @@
|
|||
package Gitolite::Triggers::Mirroring;
|
||||
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
|
||||
my $hn = $rc{HOSTNAME};
|
||||
|
||||
my ( $mode, $master, %slaves, %trusted_slaves );
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub input {
|
||||
unless ($ARGV[0] =~ /^server-(\S+)$/) {
|
||||
_die "'$ARGV[0]' is not a valid server name" if $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/;
|
||||
return;
|
||||
}
|
||||
|
||||
# note: we treat %rc as our own internal "poor man's %ENV"
|
||||
$rc{FROM_SERVER} = $1;
|
||||
trace( 3, "from_server: $1" );
|
||||
my $sender = $rc{FROM_SERVER} || '';
|
||||
|
||||
# custom peer-to-peer commands. At present the only one is 'perms -c',
|
||||
# sent from a mirror command
|
||||
if ($ENV{SSH_ORIGINAL_COMMAND} =~ /^CREATOR=(\S+) perms -c '(\S+)'$/) {
|
||||
$ENV{GL_USER} = $1;
|
||||
|
||||
my $repo = $2;
|
||||
details($repo);
|
||||
_die "$hn: '$repo' is local" if $mode eq 'local';
|
||||
_die "$hn: '$repo' is native" if $mode eq 'master';
|
||||
_die "$hn: '$sender' is not the master for '$repo'" if $master ne $sender;
|
||||
|
||||
# this expects valid perms content on STDIN
|
||||
_system("gitolite perms -c $repo");
|
||||
|
||||
# we're done. Yes, really...
|
||||
exit 0;
|
||||
}
|
||||
|
||||
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/ ) {
|
||||
# my ($user, $newsoc, $repo) = ($1, $2, $3);
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = $2;
|
||||
@ARGV = ($1);
|
||||
$rc{REDIRECTED_PUSH} = 1;
|
||||
trace( 3, "redirected_push for user $1" );
|
||||
} else {
|
||||
# master -> slave push, no access checks needed
|
||||
$ENV{GL_BYPASS_ACCESS_CHECKS} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub pre_git {
|
||||
return unless $hn;
|
||||
# nothing, and I mean NOTHING, happens if HOSTNAME is not set
|
||||
trace( 1, "pre_git() on $hn" );
|
||||
|
||||
my ( $repo, $user, $aa ) = @_[ 1, 2, 3 ];
|
||||
|
||||
my $sender = $rc{FROM_SERVER} || '';
|
||||
$user = '' if $sender and not exists $rc{REDIRECTED_PUSH};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# now you know the repo, get its mirroring details
|
||||
details($repo);
|
||||
|
||||
# we don't deal with any reads. Note that for pre-git this check must
|
||||
# happen *after* getting details, to give mode() a chance to die on "known
|
||||
# unknown" repos (repos that are in the config, but mirror settings
|
||||
# exclude this host from both the master and slave lists)
|
||||
return if $aa eq 'R';
|
||||
|
||||
trace( 1, "mirror", "pre_git", $repo, "user=$user", "sender=$sender", "mode=$mode", ( $rc{REDIRECTED_PUSH} ? ("redirected") : () ) );
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 1: we're master or slave, normal user pushing to us
|
||||
if ( $user and not $rc{REDIRECTED_PUSH} ) {
|
||||
trace( 3, "case 1, user push" );
|
||||
return if $mode eq 'local' or $mode eq 'master';
|
||||
if ( $trusted_slaves{$hn} ) {
|
||||
trace( 3, "redirecting to $master" );
|
||||
trace( 1, "redirect to $master" );
|
||||
exec( "ssh", $master, "USER=$user", "SOC=$ENV{SSH_ORIGINAL_COMMAND}" );
|
||||
} else {
|
||||
_die "$hn: pushing '$repo' to slave '$hn' not allowed";
|
||||
}
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 2: we're slave, master pushing to us
|
||||
if ( $sender and not $rc{REDIRECTED_PUSH} ) {
|
||||
trace( 3, "case 2, master push" );
|
||||
_die "$hn: '$repo' is local" if $mode eq 'local';
|
||||
_die "$hn: '$repo' is native" if $mode eq 'master';
|
||||
_die "$hn: '$sender' is not the master for '$repo'" if $master ne $sender;
|
||||
return;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 3: we're master, slave sending a redirected push to us
|
||||
if ( $sender and $rc{REDIRECTED_PUSH} ) {
|
||||
trace( 3, "case 2, slave redirect" );
|
||||
_die "$hn: '$repo' is local" if $mode eq 'local';
|
||||
_die "$hn: '$repo' is not native" if $mode eq 'slave';
|
||||
_die "$hn: '$sender' is not a valid slave for '$repo'" if not $slaves{$sender};
|
||||
_die "$hn: redirection not allowed from '$sender'" if not $trusted_slaves{$sender};
|
||||
return;
|
||||
}
|
||||
|
||||
_die "$hn: should not reach this line";
|
||||
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub post_git {
|
||||
return unless $hn;
|
||||
# nothing, and I mean NOTHING, happens if HOSTNAME is not set
|
||||
trace( 1, "post_git() on $hn" );
|
||||
|
||||
my ( $repo, $user, $aa ) = @_[ 1, 2, 3 ];
|
||||
# we don't deal with any reads
|
||||
return if $aa eq 'R';
|
||||
|
||||
my $sender = $rc{FROM_SERVER} || '';
|
||||
$user = '' if $sender;
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# now you know the repo, get its mirroring details
|
||||
details($repo);
|
||||
|
||||
trace( 1, "mirror", "post_git", $repo, "user=$user", "sender=$sender", "mode=$mode", ( $rc{REDIRECTED_PUSH} ? ("redirected") : () ) );
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 1: we're master or slave, normal user pushing to us
|
||||
if ( $user and not $rc{REDIRECTED_PUSH} ) {
|
||||
trace( 3, "case 1, user push" );
|
||||
return if $mode eq 'local';
|
||||
# slave was eliminated earlier anyway, so that leaves 'master'
|
||||
|
||||
# find all slaves and push to each of them
|
||||
push_to_slaves($repo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 2: we're slave, master pushing to us
|
||||
if ( $sender and not $rc{REDIRECTED_PUSH} ) {
|
||||
trace( 3, "case 2, master push" );
|
||||
# nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 3: we're master, slave sending a redirected push to us
|
||||
if ( $sender and $rc{REDIRECTED_PUSH} ) {
|
||||
trace( 3, "case 2, slave redirect" );
|
||||
|
||||
# find all slaves and push to each of them
|
||||
push_to_slaves($repo);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $lastrepo = '';
|
||||
|
||||
sub details {
|
||||
my $repo = shift;
|
||||
return if $lastrepo eq $repo;
|
||||
|
||||
$master = master($repo);
|
||||
%slaves = slaves($repo);
|
||||
$mode = mode($repo);
|
||||
%trusted_slaves = trusted_slaves($repo);
|
||||
trace( 3, $master, $mode, join( ",", sort keys %slaves ), join( ",", sort keys %trusted_slaves ) );
|
||||
}
|
||||
|
||||
sub master {
|
||||
return option( +shift, 'mirror.master' );
|
||||
}
|
||||
|
||||
sub slaves {
|
||||
my $ref = git_config( +shift, "^gitolite-options\\.mirror\\.slaves.*" );
|
||||
my %out = map { $_ => 1 } map { split } values %$ref;
|
||||
return %out;
|
||||
}
|
||||
|
||||
sub trusted_slaves {
|
||||
my $ref = git_config( +shift, "^gitolite-options\\.mirror\\.redirectOK.*" );
|
||||
# the list of trusted slaves (where we accept redirected pushes from)
|
||||
# is either explicitly given...
|
||||
my @out = map { split } values %$ref;
|
||||
my %out = map { $_ => 1 } @out;
|
||||
# ...or it's all the slaves mentioned if the list is just a "all"
|
||||
%out = %slaves if ( @out == 1 and $out[0] eq 'all' );
|
||||
return %out;
|
||||
}
|
||||
|
||||
sub mode {
|
||||
my $repo = shift;
|
||||
return 'local' if not $hn;
|
||||
return 'master' if $master eq $hn;
|
||||
return 'slave' if $slaves{$hn};
|
||||
return 'local' if not $master and not %slaves;
|
||||
_die "$hn: '$repo' is mirrored but not here";
|
||||
}
|
||||
}
|
||||
|
||||
sub push_to_slaves {
|
||||
my $repo = shift;
|
||||
|
||||
my $u = $ENV{GL_USER};
|
||||
delete $ENV{GL_USER}; # why? see src/commands/mirror
|
||||
|
||||
for my $s ( sort keys %slaves ) {
|
||||
system("gitolite mirror push $s $repo &");
|
||||
}
|
||||
|
||||
$ENV{GL_USER} = $u;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,80 @@
|
|||
package Gitolite::Triggers::RefexExpr;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# track refexes passed and evaluate expressions on them
|
||||
# ----------------------------------------------------------------------
|
||||
# see instructions for use at the bottom of src/VREF/refex-expr
|
||||
|
||||
use Gitolite::Easy;
|
||||
|
||||
my %passed;
|
||||
my %rules;
|
||||
my $init_done = 0;
|
||||
|
||||
sub access_2 {
|
||||
# get out quick for repos that don't have any rules
|
||||
return if $init_done and not %rules;
|
||||
|
||||
# but we don't really know that the first time, heh!
|
||||
if (not $init_done) {
|
||||
my $repo = $_[1];
|
||||
init($repo);
|
||||
return unless %rules;
|
||||
}
|
||||
|
||||
my $refex = $_[5];
|
||||
return if $refex =~ /DENIED/;
|
||||
|
||||
$passed{$refex}++;
|
||||
|
||||
# evaluate the rules each time; it's not very expensive
|
||||
for my $k (sort keys %rules) {
|
||||
$ENV{"GL_REFEX_EXPR_" . $k} = eval_rule($rules{$k});
|
||||
}
|
||||
}
|
||||
|
||||
sub eval_rule {
|
||||
my $rule = shift;
|
||||
|
||||
my $e;
|
||||
$e = join " ", map { convert($_) } split ' ', $rule;
|
||||
|
||||
my $ret = eval $e;
|
||||
_die "eval '$e' -> '$@'" if $@;
|
||||
Gitolite::Common::trace(1, "RefexExpr", "'$rule' -> '$e' -> '$ret'");
|
||||
|
||||
return "'$rule' -> '$e'" if $ret;
|
||||
}
|
||||
|
||||
my %constant;
|
||||
%constant = map { $_ => $_ } qw(1 not and or xor + - ==);
|
||||
$constant{'-lt'} = '<';
|
||||
$constant{'-gt'} = '>';
|
||||
$constant{'-eq'} = '==';
|
||||
$constant{'-le'} = '<=';
|
||||
$constant{'-ge'} = '>=';
|
||||
$constant{'-ne'} = '!=';
|
||||
|
||||
sub convert {
|
||||
my $i = shift;
|
||||
return $i if $i =~ /^-?\d+$/;
|
||||
return $constant{$i} || $passed{$i} || $passed{"refs/heads/$i"} || 0;
|
||||
}
|
||||
|
||||
# called only once
|
||||
sub init {
|
||||
$init_done = 1;
|
||||
my $repo = shift;
|
||||
|
||||
# find all the rule expressions
|
||||
my %t = config($repo, "^gitolite-options\\.refex-expr\\.");
|
||||
my ($k, $v);
|
||||
# get rid of the cruft and store just the rule name as the key
|
||||
while ( ($k, $v) = each %t) {
|
||||
$k =~ s/^gitolite-options\.refex-expr\.//;
|
||||
$rules{$k} = $v;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,56 @@
|
|||
package Gitolite::Triggers::RepoUmask;
|
||||
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# setting a repo specific umask
|
||||
# ----------------------------------------------------------------------
|
||||
# this is for people who are too paranoid to trust e.g., gitweb's repo
|
||||
# exclusion logic, but not paranoid enough to put it on a different server
|
||||
|
||||
=for usage
|
||||
|
||||
* In the rc file, add 'RepoUmask::pre_git' and 'RepoUmask::post_create' to
|
||||
the corresponding trigger lists.
|
||||
|
||||
* For each repo that is to get a different umask than the default, add a
|
||||
line like this:
|
||||
|
||||
option umask = 0027
|
||||
|
||||
=cut
|
||||
|
||||
# sadly option/config values are not available at pre_create time for normal
|
||||
# repos. So we have to do a one-time fixup in a post_create trigger.
|
||||
sub post_create {
|
||||
my $repo = $_[1];
|
||||
|
||||
my $umask = option($repo, 'umask');
|
||||
_chdir($rc{GL_REPO_BASE}); # because using option() moves us to ADMIN_BASE!
|
||||
|
||||
return unless $umask;
|
||||
|
||||
# unlike the one in the rc file, this is a string
|
||||
$umask = oct($umask);
|
||||
my $mode = "0" . sprintf("%o", $umask ^ 0777);
|
||||
|
||||
system("chmod -R $mode $repo.git >&2");
|
||||
}
|
||||
|
||||
sub pre_git {
|
||||
my $repo = $_[1];
|
||||
|
||||
my $umask = option($repo, 'umask');
|
||||
_chdir($rc{GL_REPO_BASE}); # because using option() moves us to ADMIN_BASE!
|
||||
|
||||
return unless $umask;
|
||||
|
||||
# unlike the one in the rc file, this is a string
|
||||
umask oct($umask);
|
||||
}
|
||||
|
||||
1;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue