Skip to content

Commit

Permalink
0.2.0 overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
progrium committed May 30, 2014
1 parent 9d4e686 commit 7ffbc84
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 165 deletions.
38 changes: 0 additions & 38 deletions .s3cfg

This file was deleted.

30 changes: 23 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
---
branches:
only:
- master
script: make -e remote TARGET=test
before_install:
- "sudo apt-get install -y s3cmd"
- "tests/setup_travis"
- sudo apt-get install -y rsync
- echo -n $id_rsa_{00..30} >> ~/.ssh/id_rsa_base64
- base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host ci.progrium.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
script:
- rsync -avz --exclude ".git" -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" . gitreceive@ci.progrium.com:.
- ssh gitreceive@ci.progrium.com make test

#notifications:
# irc: "chat.freenode.net#dokku"
env:
global:
- REMOTE=test@test.progrium.com
- secure: "Q+lKt2CG7/1nur1TkuXyd2LiNjctaCoMdyFnkrq4H3PnJROWL20Vc9qBbtVr\n5yM+kiXmRBaCbhqjNe3T7WdRReLihR1fNC5DFI8Upt3jE4+C4/IYc7lPQBcn\ntvFAklBnDFiE6LparlhNdJaEJfdocP8c1kILTwRIZjof0ozYYU0="
- secure: "XdtWn4kmlvOBWlkCAifuR8MIYK1i3GQhzba28lFIIdTPrcpeiD6G33h2bljI\nTP9p2h5Hcz/wN7Nf4vcr8gZ0ponTMjpp6bzCcNkPuhjAmjVP0BuwQdhNPoJI\ntvhVKG356wBYp6LvqDFwRm7vK1dDwz+c4n5+wPBP5L7FkI5ZK7E="
- secure: "TQJOJIg1zMcrOqyl2UD0NG4HALhs27bXAVwte33Uyapao7Ayi2naGAcsLRIW\n2E5BK9ILvcH0MR4/jBTzuFkkMNCckluhQX6yi66/Ju/OHbFIXRSfbV9f7e14\njw3xLYNCRXR5tYnDTPR/nsfly9eWITmixl7qzuRkeU5eWJzm/D0="
- secure: "EDktPY4hwYAPo5gFmZps7sCB64rc6dwHbuNBLMDayYuHDFiSk9ZJfyF4R0fC\ndkVeR/d6IKeB+23mfhKlribGXJJ8eIjxbTsxZJi0KvhmXo/8uBJNHjvvwm8d\ncXSX1+a+vc6jtgoXxcghV1kKGJVHFwNuvA7kcFdMdiW8uTuga4c="
- secure: "BCpcznLXeI7+rNxmDw4oHI/+xhgi/7PxB4IMLPa74+4LQMRf8VlRugQT7n/z\nNoz2+bIswOACzdnJRxJfuVdw7EpRYuh3s0fxYWesVOo2E8Ixz+1ssJLGdmB7\n6AGQRl5GiByUqTGN5SrEmRbBm8JwOZO/vW3dt1uXLMRZGRXg2CQ="
- secure: "SzieTW8P9lvaPyTExc0fyTj0QgwRPQUGWPJ14bN2+SVu1gIV5CpHGFq6gH66\nOpMfxvDzh4fTkZZXowa4sQKlcSQ9r5ReyWWFh+5n08+H09nUrjccUYiFWWQ5\nIPexIvfSm0pvLXkJu/zf/TWl8K3p0+6KuE1JPke2GSaVKY1Rlzg="
- secure: "FlEWim548ISDVQY4nysBHrb9VFA/uxfOa+aEVzTTBPkEybI6973wyUbmR2R1\nPODP/OJR8xDgZzUFZ7aYPrWiSlT+Tl6nsvxJUhVQh2ISpIVDw0kFmOdbbLFx\nZXP9aYwrKop19iqmEYxYO8Or9cmjJONQE4nSNa0P86H1KzrqoI4="
- secure: "eu1pAE37JP1UBUoQ3P0OIZKBIvTbekcd3VMWjCZ/Ozs+JrgE9aDeuCTHiJOr\n7n7MrKIpoR90+3qnlPszfnzv9Y+t11TT2/61RT5H0XaUfW4wIk5R9z82qPEg\nKCAqj2bWipmE/8tZ8VUDXfxTa2c0IaepUDY51m9pl4NeXeeW1fQ="
- secure: "eyF4THM+NC71VXG7iWwzp1LvDLRz7Fv2LTGVF/MSWd7DTdzOPA7Jwev6Z5XA\nq592og7ivhz0k+pbcbXx1o8c+tRJfLZ/KLDlKWK0jDOgpGFkMMJf2sXsERXW\nem0exnGnAmEchBmEko15Voa93VKdbVqycukyEvI3I8OpUYLEh+k="
- secure: "eqfIiywFG/3ccXtKOSV0s9gyz/0wwAbQyhehhhrUF/MMTj9Dhe9IZMdQdrj9\nPP50Y9BvsW7CogPJ4HpffOOttwcX91aQwfAQRDmRddLTWAyb9tOa7lQx57oD\nLGFD3gdd3dBCWEdjf+bXt6Lp1A7zzqeEVKyrHLHfVubsb7OfJqc="
- secure: "Bc8eukp8K5wnYFMqBClAem8G9K2MSDL6jTx4+L+3Bkpa3i0u53J1WwuoTQfg\njFoajIMYDu3YVnVQ+1Rl67OwcGsCbKGIO9JRIcgZqhehsfc7W82LGejpCkYo\ncAtJELc3ONEjDCUQXsvDHUFa0J/teOmTUO+T0qmz1p09v51jzyo="
- secure: "FoWY0mozoh6lJzEc3e5bAv5Fl7Z1ixowci9dTrVTo5Irh9znz84EevocNNpp\n2aopxtjKwBnJMTiaJa1zA8/FEnTDaSJJT0FffurR3P84nV68VSDyx3tIx1Tz\nPqMybN6JpISuRhtfPgZwpH0N3JjLRmoaX4U6UyXOZyJG758xxic="
- secure: "JNjOcyDb+AuGZDuc4epDDD08v1eQFngl1JZcZVZ2QY+WAtAUrmy2J+kVv79A\nEpS2oL8NjCX5Cnn0kR7T9+RW8kcLT5wJrGGdr7N/x9zPJ2Djbqs8mfEXhm0v\njjR84hps60RrFY5D+suIg5aug6IBWcHMoScPrc/dEr1UGw/opPI="
- secure: "A3kdmk9N0nzN54okFPhe7Z7bf6xQ7pxBP658ED/stnPFEU07PrL2lDsBe4VL\nDbT940utiw0mda6DysMQuUL8vS7dd2m3HUIT+p6xrpSMTOQGnt4xy19r465N\nJsBdyBN3LGXJF8CodiI6sS24jt+P2fsHIyWyVotHLm8F9HbRztQ="
- secure: "WXLMXpxGwhegZWJGZfSTt0dIyRBh/Epp8o3pURNWMK5vayCoK3tBLbLI8xrD\n49GhtU2MeakDfXDeXOyqYZytvlFHIQWZH39aPg9sUs0sbwRbiwprfI67ypS8\ns+7B3iF1cvq01brJ5OogAGlSGMNSoqLGjmsC0f7efKw/88dUOJQ="
- secure: "OPzfmpw3FLGJVdJro83yx/GKR/5wAR4cE9fJ0ekmbe3oMAKW27RdOOrPG3mW\nzUWMdTwr3QamYVvT8fa2px78EyKTya4+o74uCVD8NKSOkiKJ8QlHrvPUsbeg\nyewZvaTmRaBpa1iVKsFiKsdUSJ+4O9fPa7E2RB0SqcshWFST3zU="
12 changes: 5 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
VERSION = 0.1.0

REMOTE_DIR ?= /tmp/gitreceive
VERSION = 0.2.0

install:
cp gitreceive /usr/local/bin/gitreceive
Expand All @@ -11,9 +9,9 @@ check-docker:

test: check-docker
cp gitreceive tests
docker build -t progrium/gitreceive-tests tests
docker build -t gitreceive-test tests
rm tests/gitreceive
docker run progrium/gitreceive-tests
docker run --rm gitreceive-test

remote:
tar -c . | ssh -o "StrictHostKeyChecking=no" ${REMOTE} "rm -rf ${REMOTE_DIR} && mkdir -p ${REMOTE_DIR} && cd ${REMOTE_DIR} && tar -xf - && make ${TARGET}"
clean:
docker rmi gitreceive-test
28 changes: 10 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ On your server, download https://raw.github.com/progrium/gitreceive/master/gitre

This automatically makes a user and home directory if it doesn't exist.

$ sudo gitreceive init
$ sudo gitreceive sshd:init
Created receiver script in /home/git for user 'git'.

You use a different user by setting `GITUSER=somethingelse` in the
Expand Down Expand Up @@ -58,14 +58,14 @@ The repo contents are streamed into `STDIN` as an uncompressed archive (tar file

#### Create a user by uploading a public key from your laptop

We just pipe our local SSH key into the `gitreceive upload-key` command via SSH:
We just pipe our local SSH key into the `gitreceive sshd:upload-key` command via SSH:

$ cat ~/.ssh/id_rsa.pub | ssh you@yourserver.com "sudo gitreceive upload-key <username>"
$ cat ~/.ssh/id_rsa.pub | ssh you@yourserver.com "sudo gitreceive sshd:upload-key <username>"

The `username` argument is just an arbitrary name associated with the key, mostly
for use in your system for auth, etc.

`gitreceive upload-key` will authorize this key for use on the `$GITUSER`
`gitreceive sshd:upload-key` will authorize this key for use on the `$GITUSER`
account on the server, and use the SSH "forced commands" syntax in the remote
`.ssh/authorized_keys` file, causing the internal `gitreceive run` command to
be called when this key is used with the remote git account. This allows us to
Expand All @@ -74,9 +74,9 @@ repo, which triggers the custom receiver script.

#### Add a remote to a local repository

$ git remote add demo git@yourserver.com:example.git
$ git remote add demo git@yourserver.com:example

The repository `example.git` will be created on the fly when you push.
The repository `example` will be created on the fly when you push.

#### Push!!

Expand All @@ -86,9 +86,9 @@ The repository `example.git` will be created on the fly when you push.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 332 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: ----> Receiving progrium/gitreceive.git ...
remote: ----> Posting to http://requestb.in/rlh4znrl ...
remote: ok
----> Receiving progrium/gitreceive.git ...
----> Posting to http://requestb.in/rlh4znrl ...
ok
To git@gittest:progrium/gitreceive.git
59aa541..6eafb55 master -> master

Expand Down Expand Up @@ -116,18 +116,10 @@ I used to work at Twilio. Imagine pushing a repo with a TwiML file to a
gitreceive repo with a phone number for a name. And then it runs that
TwiML on Twilio and shows you the result, all from the `git push`.

Another idea: When it's so easy to handle pushed code, how about
creating a screen in the office that will display whatever code is
pushed to it.

## Contribute

This whole system is contained in a single bash script less than 100
lines long. Let's keep it simple, but I'm definitely open to contribution!

## Big Thanks

DotCloud
DotCloud, DigitalOcean

## License

Expand Down
186 changes: 122 additions & 64 deletions gitreceive
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
#!/bin/bash
set -euo pipefail

GITUSER=${GITUSER:-git}
GITHOME="/home/$GITUSER"
SELF=`which $0`
readonly GITUSER="${GITUSER:-git}"
readonly GITHOME="/home/$GITUSER"

case "$1" in
# Public commands
absolute_path() {
pushd "$(dirname $1)" > /dev/null
local abspath="$(pwd -P)"
popd > /dev/null
echo "$abspath/$(basename $1)"
}

init) # gitreceive init
useradd -d $GITHOME $GITUSER || true
mkdir -p $GITHOME/.ssh
touch $GITHOME/.ssh/authorized_keys
cat > $GITHOME/receiver <<EOF
setup_git_user() {
declare home_dir="$1" git_user="$2"
useradd -d "$home_dir" "$git_user" || true
mkdir -p "$home_dir/.ssh"
touch "$home_dir/.ssh/authorized_keys"
chown -R "$git_user" "$home_dir"
}

setup_receiver_script() {
declare home_dir="$1" git_user="$2"
local receiver_path="$home_dir/receiver"
cat > "$receiver_path" <<EOF
#!/bin/bash
#URL=http://requestb.in/rlh4znrl
#echo "----> Posting to \$URL ..."
Expand All @@ -24,63 +35,110 @@ case "$1" in
# -F contents=@- \\
# --silent \$URL
EOF
chmod +x $GITHOME/receiver
chown -R $GITUSER $GITHOME
echo "Created receiver script in $GITHOME for user '$GITUSER'."
;;
chmod +x "$receiver_path"
chown "$git_user" "$receiver_path"
}

generate_fingerprint() {
awk '{print $2}' | base64 -d | md5sum | awk '{print $1}' | sed -e 's/../:&/2g'
}

install_authorized_key() {
declare key="$1" name="$2" home_dir="$3" git_user="$4" self="$5"
local fingerprint="$(echo "$key" | generate_fingerprint)"
local forced_command="GITUSER=$git_user $self run $name $fingerprint"
local key_options="command=\"$forced_command\",no-agent-forwarding,no-pty,no-user-rc,no-X11-forwarding,no-port-forwarding"
echo "$key_options $key" >> "$home_dir/.ssh/authorized_keys"
}

upload-key) # sudo gitreceive upload-key <username>
KEY=$(cat)
FINGERPRINT=$(ssh-keygen -lf /dev/stdin <<< $(echo $KEY) | awk '{print $2}')
AUTHORIZED_KEYS=$GITHOME/.ssh/authorized_keys
# When this key is used, use the ssh 'forced command' feature to have 'gitreceive run' to run instead.
KEY_PREFIX="command=\"GITUSER=$GITUSER $SELF run $2 $FINGERPRINT\",no-agent-forwarding,no-pty,no-user-rc,no-X11-forwarding,no-port-forwarding"
echo "$KEY_PREFIX $KEY" >> $AUTHORIZED_KEYS
echo $FINGERPRINT
;;
strip_root_slash() {
local str="$(cat)"
if [ "${str:0:1}" == "/" ]; then
echo "$str" | cut -c 2-
else
echo "$str"
fi
}

# Internal commands
run)
export RECEIVE_USER=$2
export RECEIVE_FINGERPRINT=$3
# ssh provides the original requested command in $SSH_ORIGINAL_COMMAND
export RECEIVE_REPO="$(echo $SSH_ORIGINAL_COMMAND | awk '{print $2}' | perl -pe 's/(?<!\\)'\''//g' | sed 's/\\'\''/'\''/g')"
REPO_PATH="$GITHOME/$RECEIVE_REPO"
if [ ! -d $REPO_PATH ]; then
mkdir -p $REPO_PATH
cd $REPO_PATH
git init --bare > /dev/null
fi
cd $GITHOME
PRERECEIVE_HOOK="$REPO_PATH/hooks/pre-receive"
cat > $PRERECEIVE_HOOK <<EOF
parse_repo_from_ssh_command() {
awk '{print $2}' | perl -pe 's/(?<!\\)'\''//g' | sed 's/\\'\''/'\''/g' | strip_root_slash
}

ensure_bare_repo() {
declare repo_path="$1"
if [ ! -d "$repo_path" ]; then
mkdir -p "$repo_path"
cd "$repo_path"
git init --bare > /dev/null
cd - > /dev/null
fi
}

ensure_prereceive_hook() {
declare repo_path="$1" home_dir="$2" self="$3"
local hook_path="$repo_path/hooks/pre-receive"
cd "$home_dir"
cat > "$hook_path" <<EOF
#!/bin/bash
cat | $SELF hook
cat | $self hook
EOF
chmod +x $PRERECEIVE_HOOK
git-shell -c "$SSH_ORIGINAL_COMMAND"
;;
chmod +x "$hook_path"
cd - > /dev/null
}

trigger_receiver() {
declare repo="$1" user="$2" fingerprint="$3" home_dir="$4"
while read oldrev newrev refname; do
# Only run this script for the master branch. You can remove this
# if block if you wish to run it for others as well.
[[ "$refname" == "refs/heads/master" ]] && \
git archive "$newrev" | "$home_dir/receiver" "$repo" "$newrev" "$user" "$fingerprint"
done
}

hook)
while read oldrev newrev refname
do
# Only run this script for the master branch. You can remove this
# if block if you wish to run it for others as well.
if [[ $refname = "refs/heads/master" ]] ; then

git archive $newrev | $GITHOME/receiver "$RECEIVE_REPO" "$newrev" "$RECEIVE_USER" "$RECEIVE_FINGERPRINT"

rc=$?
if [[ $rc != 0 ]] ; then
echo " ERROR: failed on rev $newrev - push denied"
exit $rc
fi
fi
done
#exit 1 # for debugging
;;
strip_remote_prefix() {
sed -u "s/^/"$'\e[1G'"/"
}

*)
echo "Usage: gitreceive <command> [options]"
;;
esac
main() {
readonly SELF="$(absolute_path $0)"
case "$1" in
# Public commands

sshd:init) # gitreceive init
setup_git_user "$GITHOME" "$GITUSER"
setup_receiver_script "$GITHOME" "$GITUSER"
echo "Created receiver script in $GITHOME for user '$GITUSER'."
;;

sshd:upload-key) # sudo gitreceive upload-key <username>
declare name="$2"
local key="$(cat)"
install_authorized_key "$key" "$name" "$GITHOME" "$GITUSER" "$SELF"
echo "$key" | generate_fingerprint
;;

# Internal commands

run)
declare user="$2" fingerprint="$3"
export RECEIVE_USER="$user"
export RECEIVE_FINGERPRINT="$fingerprint"
export RECEIVE_REPO="$(echo "$SSH_ORIGINAL_COMMAND" | parse_repo_from_ssh_command)"
local repo_path="$GITHOME/$RECEIVE_REPO"
ensure_bare_repo "$repo_path"
ensure_prereceive_hook "$repo_path" "$GITHOME" "$SELF"
cd "$GITHOME"
git-shell -c "$(echo "$SSH_ORIGINAL_COMMAND" | awk '{print $1}') '$RECEIVE_REPO'"
;;

hook)
trigger_receiver "$RECEIVE_REPO" "$RECEIVE_USER" "$RECEIVE_FINGERPRINT" "$GITHOME" | strip_remote_prefix
;;

*)
echo "Usage: gitreceive <command> [options]"
;;
esac
}
main $@
Loading

0 comments on commit 7ffbc84

Please sign in to comment.