aboutsummaryrefslogtreecommitdiff
blob: 7a38f139d804efa5c93430a587e79593e1d6fee8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
#!/bin/bash

# easy install for gitolite

# you run this on the client side, and it takes care of all the server side
# work.  You don't have to do anything on the server side directly

# to do a manual install (since I have tested this only on Linux), open this
# script in a nice, syntax coloring, text editor and follow the instructions
# prefixed by the word "MANUAL" in the comments below :-)

# run without any arguments for "usage" info

# important setting: bail on any errors (else we have to check every single
# command!)
set -e

# ----------------------------------------------------------------------
# bootstrap and main
# ----------------------------------------------------------------------
if [[ $1 != boot/strap ]]
then
    # did someone tell you you can't call functions before they're defined in
    # bash?  Don't believe everything you hear ;-)
    . $0 boot/strap
    main "$@"
    cleanup
    exit 0
fi

# ----------------------------------------------------------------------
# no direct executable statements after this; only functions
# ----------------------------------------------------------------------

main() {
    basic_sanity "$@"

    setup_tempdir

    version_info "$@"

    [[ -n $admin_name ]] && setup_local_ssh

    copy_gl     # src, conf, etc

    run_install

    [[ $upgrade == 0 ]] && initial_conf_key

    # MANUAL: cd to $GL_ADMINDIR and run "src/gl-compile-conf"
    ssh -p $port $user@$host "cd $GL_ADMINDIR; \$PWD/src/gl-compile-conf $quiet"

    setup_pta

    clone_it
}

# ----------------------------------------------------------------------
# setup temp files
# ----------------------------------------------------------------------

setup_tempdir() {
    export tmpgli=tmp-gl-install
    trap cleanup 0
    mkdir -p $tmpgli
}

cleanup() {
    rm -rf $tmpgli
}

# ----------------------------------------------------------------------
# service functions
# ----------------------------------------------------------------------

die() { echo "$@"; echo; echo "run $0 without any arguments for help and tips"; cleanup; exit 1; }
prompt() {
    # receives two arguments.  A short piece of text to be displayed, without
    # pausing, in "quiet" mode, and a much longer one to be displayed, *with*
    # a pause, in normal (verbose) mode
    [[ $quiet == -q ]] && [[ -n $1 ]] && {
        eval "echo \"$1\""
        return
    }
    shift
    echo
    echo
    echo ------------------------------------------------------------------------
    eval "echo \"$1\""
    echo
    read -p '...press enter to continue or Ctrl-C to bail out'
}
usage() {
    cat <<EOFU
Usage: $0 [-q] user host [port] admin_name     # install
       $0 [-q] user host [port]                # upgrade

  - (optional) "-q" as first arg sets "quiet" mode: no verbose descriptions of
    what is going on, no pauses unless absolutely necessary
  - "user" is the username on the server where you will be installing gitolite
  - "host" is that server's hostname (or IP address)
  - "port" is the ssh server port on "host"; optional, defaults to 22
  - "admin_name" is *your* name as it should appear in the eventual gitolite
    config file.  For upgrades (ie., gitolite is already installed on the
    server), this argument is not needed, and will be *ignored* if provided.

Example usage: $0 git my.git.server sitaram

Notes:
  - "user" and "admin_name" must be simple names -- no special characters etc
    please (only alphanumerics, dot, hyphen, underscore)
  - traditionally, the "user" is "git", but it can be anything you want
  - "admin_name" should be your name, for clarity, or whoever will be the
    gitolite admin

Pre-requisites:
  - you must already have pubkey based access to user@host.  If you currently
    only have password access, use "ssh-copy-id" or something equivalent (or
    copy the key manually).  Somehow (doesn't matter how), get to the point
    where you can type "ssh user@host" and get a command line.

            **DO NOT RUN THIS PROGRAM UNTIL THAT WORKS**

EOFU
    exit 1;
}

# ----------------------------------------------------------------------
# basic sanity / argument checks
# ----------------------------------------------------------------------

basic_sanity() {
    # MANUAL: this *must* be run as "src/gl-easy-install", not by cd-ing to
    # src and then running "./gl-easy-install"

    bindir=${0%/*}
    # switch to parent of bindir; we assume the conf files are all there
    cd "$bindir"; cd ..

    # are we in quiet mode?
    quiet=
    [[ "$1" == "-q" ]] && {
        quiet=-q
        shift
    }

    # MANUAL: (info) we'll use "git" as the user, "server" as the host, and
    # "sitaram" as the admin_name in example commands shown below, if any

    [[ -z $2 ]] && usage
    user=$1
    host=$2
    port=22
    admin_name=$3
    # but if the 3rd arg is a number, that's a port number, and the 4th arg is
    # the admin_name
    if echo $3 | perl -lne 'exit 1 unless /^[0-9]+$/'
    then
        port=$3
        admin_name=$4
    fi

    echo $user | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' ||
        die "user '$user' invalid"
    [[ "$user" == "root" ]] && die I refuse to install to root
    echo $admin_name | perl -lne 'exit 1 if /[^a-zA-Z0-9._-]/' ||
        die "admin_name '$admin_name' invalid"

    # MANUAL: make sure you're in the gitolite directory, at the top level.
    # The following files should all be visible:

    ls  hooks/gitolite-admin/post-update     \
        hooks/common/update         \
        src/gitolite.pm             \
        src/gl-install              \
        src/gl-auth-command         \
        src/gl-compile-conf         \
        conf/example.conf           \
        conf/example.gitolite.rc    >/dev/null ||
        die "cant find at least some files in gitolite sources/config; aborting"

    # MANUAL: make sure you have password-less (pubkey) auth on the server.
    # That is, running "ssh git@server" should log in straight away, without
    # asking for a password

    ssh -p $port -o PasswordAuthentication=no $user@$host true ||
        die "pubkey access didn't work; please set it up using 'ssh-copy-id' or something"
}

# ----------------------------------------------------------------------
# version info
# ----------------------------------------------------------------------

version_info() {

    # MANUAL: if needed, make a note of the version you are upgrading from, and to

    # record which version is being sent across; we assume it's HEAD
    git describe --tags --long HEAD 2>/dev/null > conf/VERSION || echo '(unknown)' > conf/VERSION

    # what was the old version there?
    export upgrade_details="you are upgrading \
    $(ssh -p $port $user@$host cat gitolite-install/conf/VERSION 2>/dev/null || echo '(or installing first-time)' ) \
    to $(cat conf/VERSION)"

    prompt "$upgrade_details" "$v_upgrade_details"
}

# ----------------------------------------------------------------------
# new keypair, ssh-config para; only on "install" (not upgrade)
# ----------------------------------------------------------------------

setup_local_ssh() {

    # MANUAL: create a new key for you as a "gitolite user" (as opposed to you
    # as the "gitolite admin" who needs to login to the server and get a
    # command line).  For example, "ssh-keygen -t rsa ~/.ssh/sitaram"; this
    # would create two files in ~/.ssh (sitaram and sitaram.pub)

    prompt "setting up keypair..." "$v_setting_up_keypair"

    if [[ -f "$HOME/.ssh/$admin_name.pub" ]]
    then
        prompt "" "$v_reuse_pubkey"
    else
        ssh-keygen -t rsa -f "$HOME/.ssh/$admin_name" || die "ssh-keygen failed for some reason..."
    fi

    # MANUAL: copy the pubkey created to the server, say to /tmp.  This would
    # be "scp ~/.ssh/sitaram.pub git@server:/tmp" (the script does this at a
    # later stage, you do it now for convenience).  Note: only the pubkey
    # (sitaram.pub).  Do NOT copy the ~/.ssh/sitaram file -- that is a private
    # key!

    # MANUAL: if you're running ssh-agent (see if you have an environment
    # variable called SSH_AGENT_PID in your "env"), you should add this new
    # key.  The command is "ssh-add ~/.ssh/sitaram"

    if ssh-add -l &>/dev/null
    then
        prompt "    ...adding key to agent..." "$v_ssh_add"
        ssh-add "$HOME/.ssh/$admin_name"
    fi

    # MANUAL: you now need to add some lines to the end of your ~/.ssh/config
    # file.  If the file doesn't exist, create it.  Make sure the file is
    # "chmod 644".

    # The lines to be included look like this:

    #   host gitolite
    #       user git
    #       hostname server
    #       port 22
    #       identityfile ~/.ssh/sitaram

    echo "host gitolite
     user $user
     hostname $host
     port $port
     identityfile ~/.ssh/$admin_name" > $tmpgli/.gl-stanza

    if grep 'host  *gitolite' "$HOME/.ssh/config" &>/dev/null
    then
        prompt "found gitolite para in ~/.ssh/config; assuming it is correct..." "$v_found_para"
    else
        prompt "creating gitolite para in ~/.ssh/config..." "$v_creating_para"
        cat $tmpgli/.gl-stanza >> "$HOME/.ssh/config"
        # if the file didn't exist at all, it might have the wrong permissions
        chmod 644 "$HOME/.ssh/config"
    fi
}

# ----------------------------------------------------------------------
# server side
# ----------------------------------------------------------------------

copy_gl() {

    # MANUAL: copy the gitolite directories "src", "conf", and "doc" to the
    # server, to a directory called (for example) "gitolite-install".  You may
    # have to create the directory first.

    ssh -p $port $user@$host mkdir -p gitolite-install
    scp $quiet -P $port -p -r src conf doc hooks $user@$host:gitolite-install/

    # MANUAL: now log on to the server (ssh git@server) and get a command
    # line.  This step is for your convenience; the script does it all from
    # the client side but that may be too much typing for manual use ;-)

    # MANUAL: cd to the "gitolite-install" directory where the sources are.
    # Then copy conf/example.gitolite.rc as ~/.gitolite.rc and edit it if you
    # wish to change any paths.  Make a note of the GL_ADMINDIR and REPO_BASE
    # paths; you will need them later

    prompt "finding/creating gitolite rc..." "$v_edit_glrc"

    # lets try and get the file from there first
    if scp -P $port $user@$host:.gitolite.rc $tmpgli &>/dev/null
    then
        prompt "    ...trying to reuse existing rc" \
        "Oh hey... you already had a '.gitolite.rc' file on the server.
Let's see if we can use that instead of the default one..."
        < $tmpgli/.gitolite.rc     perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.old
        < conf/example.gitolite.rc perl -ne 'print "$1\n" if /^\s*(\$\w+) *=/' | sort > $tmpgli/glrc.new
        # msysgit doesn't have "comm".  diff is not ideal for our purposes
        # because we only care about differences in one direction, but we'll
        # have to make do...
        set +e
        diff -u  $tmpgli/glrc.old $tmpgli/glrc.new | grep '^+.*\$' > $tmpgli/glrc.comm13
        set -e
        if [[ ! -s $tmpgli/glrc.comm13 ]]
        then
            [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc
        else
            echo new variables found in rc file:
            cat $tmpgli/glrc.comm13
            echo
            # MANUAL: if you're upgrading, read the instructions below and
            # manually make sure your final ~/.gitolite.rc has both your existing
            # customisations as well as any new variables that the new version of
            # gitolite has introduced
            prompt "" "$v_upgrade_glrc"
            ${VISUAL:-${EDITOR:-vi}} conf/example.gitolite.rc $tmpgli/.gitolite.rc
        fi
    else
        cp conf/example.gitolite.rc $tmpgli/.gitolite.rc
        [[ $quiet == -q ]] || ${VISUAL:-${EDITOR:-vi}} $tmpgli/.gitolite.rc
    fi

    # copy the rc across
    scp $quiet -P $port $tmpgli/.gitolite.rc $user@$host:
}

run_install() {

    prompt "installing/upgrading..." "$v_ignore_stuff"

    # extract the GL_ADMINDIR, REPO_BASE and GIT_PATH locations
    GL_ADMINDIR=$(ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GL_ADMINDIR'")
    REPO_BASE=$(  ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$REPO_BASE'")
    GIT_PATH=$(   ssh -p $port $user@$host "perl -e 'do \".gitolite.rc\"; print \$GIT_PATH'")

    # determine if this is an upgrade; we decide based on whether a file
    # called $GL_ADMINDIR/conf/gitolite.conf exists on the remote side.  We
    # can't do this till we know the correct value for GL_ADMINDIR
    upgrade=0
    if ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf &> /dev/null
    then
        upgrade=1
        ssh -p $port $user@$host cat $GL_ADMINDIR/conf/gitolite.conf 2> /dev/null | grep '@SHELL' &&
            prompt "" "$v_at_shell_bwi"
        [[ -n $admin_name ]] && echo -e "\n    *** WARNING ***: looks like an upgrade... ignoring argument '$admin_name'"
    else
        [[ -z $admin_name ]] && die "    *** ERROR ***: doesn't look like an upgrade, so I need a name for the admin"
    fi

    # MANUAL: still in the "gitolite-install" directory?  Good.  Run
    # "src/gl-install"

    ssh -p $port $user@$host "cd gitolite-install; src/gl-install $quiet"

    # MANUAL: if you're upgrading, run "src/gl-compile-conf" and you're done!
    # -- ignore the rest of this file for the purposes of an upgrade

}

# ----------------------------------------------------------------------
# from here on it's install only
# ----------------------------------------------------------------------

# MANUAL: setup the initial config file.  Edit $GL_ADMINDIR/conf/gitolite.conf
# and add at least the following lines to it:

#   repo gitolite-admin
#       RW+                 = sitaram

initial_conf_key() {
    echo "#gitolite conf
# please see conf/example.conf for details on syntax and features

repo gitolite-admin
    RW+                 = $admin_name

repo testing
    RW+                 = @all

" > $tmpgli/gitolite.conf

    # send the config and the key to the remote
    scp $quiet -P $port $tmpgli/gitolite.conf $user@$host:$GL_ADMINDIR/conf/
    scp $quiet -P $port "$HOME/.ssh/$admin_name.pub" $user@$host:$GL_ADMINDIR/keydir
}

# ----------------------------------------------------------------------
# hey lets go the whole hog on this; setup push-to-admin!
# ----------------------------------------------------------------------

setup_pta() {

    # MANUAL: you have to now make the first commit in the admin repo.  This
    # is a little more complex, so read carefully and substitute the correct
    # paths.  What you have to do is:

    #   cd $REPO_BASE/gitolite-admin.git
    #   GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
    #   GIT_WORK_TREE=$GL_ADMINDIR git commit -am start

    # Substitute $GL_ADMINDIR and $REPO_BASE appropriately.  Note there is no
    # space around the "=" in the second and third lines.

    echo "cd $REPO_BASE/gitolite-admin.git
PATH=\$PATH:$GIT_PATH
GIT_WORK_TREE=$GL_ADMINDIR git add conf/gitolite.conf keydir
GIT_WORK_TREE=$GL_ADMINDIR git diff --cached --quiet 2>/dev/null || GIT_WORK_TREE=$GL_ADMINDIR git commit -am start
" | ssh -p $port $user@$host

    # MANUAL: now that the admin repo is created, you have to set the hooks
    # properly.  The install program does this.  So cd back to the
    # "gitolite-install" directory and run "src/gl-install"

    ssh -p $port $user@$host "cd gitolite-install; src/gl-install $quiet"

    # MANUAL: you're done!  Log out of the server, come back to your
    # workstation, and clone the admin repo using "git clone
    # gitolite:gitolite-admin", or pull once again if you already have a
    # clone
}

clone_it()
{
    cleanup
    cd "$HOME"
    if [[ -d gitolite-admin ]]
    then
        echo $HOME/gitolite-admin exists, skipping clone step...
    else
        prompt "cloning gitolite-admin repo..." "$v_cloning"
        git clone gitolite:gitolite-admin
    fi

    # MANUAL: be sure to read the message below; this applies to you too...

    echo
    echo
    echo ---------------------------------------------------------------
    eval "echo \"$v_done\""
}

# ----------------------------------------------------------------------
# prompt strings
# ----------------------------------------------------------------------

v_upgrade_details="
\$upgrade_details

Note: getting '(unknown)' for the 'from' version should only happen once.
Getting '(unknown)' for the 'to' version means you are probably installing
from a tar file dump, not a real clone.  This is not an error but it's nice to
have those version numbers in case you need support.  Try and install from a
clone
"

v_setting_up_keypair="
the next command will create a new keypair for your gitolite access

The pubkey will be \$HOME/.ssh/\$admin_name.pub.  You will have to choose a
passphrase or hit enter for none.  I recommend not having a passphrase for
now, *especially* if you do not have a passphrase for the key which you are
already using to get server access!

Add one using 'ssh-keygen -p' after all the setup is done and you've
successfully cloned and pushed the gitolite-admin repo.  After that, install
'keychain' or something similar, and add the following command to your bashrc
(since this is a non-default key)

    ssh-add "\\\$HOME/.ssh/\$admin_name"

This makes using passphrases very convenient.
"

v_reuse_pubkey="
Hmmm... pubkey \$HOME/.ssh/\$admin_name.pub exists; should I just (re-)use it?

IMPORTANT: once the install completes, *this* key can no longer be used to get
a command line on the server -- it will be used by gitolite, for git access
only.  If that is a problem, please ABORT now.

doc/6-ssh-troubleshooting.mkd will explain what is happening here, if you need
more info.
"

v_ssh_add="
you're running ssh-agent.  We'll try and do an ssh-add of the
private key we just created, otherwise this key won't get picked up.  If
you specified a passphrase in the previous step, you'll get asked for one
now -- type in the same one.
"

v_found_para="
your \\\$HOME/.ssh/config already has settings for gitolite.  I will assume
they're correct, but if they're not, please edit that file, delete that
paragraph (that line and the following few lines), Ctrl-C, and rerun.

In case you want to check right now (from another terminal) if they're
correct, here's what they are *supposed* to look like:

\$(cat \$tmpgli/.gl-stanza)

"

v_creating_para="
creating settings for your gitolite access in \$HOME/.ssh/config;
these are the lines that will be appended to your ~/.ssh/config:

\$(cat \$tmpgli/.gl-stanza)

"

v_edit_glrc="
the gitolite rc file needs to be edited by hand.  The defaults are sensible,
so if you wish, you can just exit the editor.

Otherwise, make any changes you wish and save it.  Read the comments to
understand what is what -- the rc file's documentation is inline.

Please remember this file will actually be copied to the server, and that all
the paths etc. represent paths on the server!
"

v_upgrade_glrc="
looks like you're upgrading, and there are some new rc variables that this
version is expecting that your old rc file doesn't have.

I'm going to run your \\\$EDITOR with two filenames.  The first is the example
file from this gitolite version.  It will have a block (code and comments) for
each of the variables shown above with a '+' sign.

The second is your current rc file, the destination.  Copy those lines into
this file, preferably *with* the surrounding comments (for clarity) and save
it.

This is necessary; please dont skip this!

[It's upto you to figure out how your \\\$EDITOR handles 2 filename arguments,
switch between them, copy lines, etc ;-)]
"

v_ignore_stuff="
ignore any 'please edit this file' or 'run this command' type lines in the
next set of command outputs coming up.  They're only relevant for a manual
install, not this one...
"

v_at_shell_bwi="
you are using the @SHELL feature in your gitolite config.  This feature has
now changed in a backward incompatible way; see doc/6-ssh-troubleshooting.mkd
for information on migrating this to the new syntax.

DO NOT hit enter unless you have understood that information and properly
migrated your setup, or you are sure you have shell access to the server
through some other means than the $admin_name key.

"

v_done="
done!

Reminder:
    *Your* URL for cloning any repo on this server will be
        gitolite:reponame.git

    *Other* users you set up will have to use
        \$user@\$host:reponame.git
    However, if your server uses a non-standard ssh port, they should use
        ssh://\$user@\$host:\$port/reponame.git

    If this is your first time installing gitolite, please also:
        tail -31 \$0
    for next steps.
"

v_cloning="
now we will clone the gitolite-admin repo to your workstation and see if it
all hangs together.  We'll do this in your \\\$HOME for now, and you can move
it elsewhere later if you wish to.
"

tail="
NOTE: All the below stuff is on your *workstation*.  You should not, normally,
have to do anything directly on your server to administer/use gitolite.

The admin repo is currently cloned at ~/gitolite-admin.  You can reclone it
anywhere else if you wish.  To administer gitolite, make changes to the config
file (conf/gitolite.conf) and/or the pubkeys (in subdirectory 'keydir') in any
clone, then git add, git commit, and git push.

ADDING REPOS: Do NOT add repos manually on the server.  Edit the config file
to give *some* user access to the repo.  When you push, an empty repo will be
created on the server.

ADDING USERS: copy their pubkey as keydir/<username>.pub, add it, commit and
push.

CONFIG FILE FORMAT: see comments in conf/example.conf in the gitolite source.

SSH MAGIC: Remember you (the admin) now have *two* keys to access the server
hosting your gitolite setup -- one to get you a command line, and one to get
you gitolite access; see doc/6-ssh-troubleshooting.mkd.  If you're not using
keychain or some such software, you may have to run an 'ssh-add' command to
add that key each time you log in.

URLS:  *Your* URL for cloning any repo on this server is different from the
url that the *other* users have to use.  The easy install command should tell
you what these URLs look like, at the end of each successful run.  Feel free
to re-run easy install again (using the same arguments) if you missed it.

UPGRADING GITOLITE: just pull a fresh clone from github, and run the same easy
install command as before, with the same arguments.
"