├── .gitignore ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.markdown ├── bin └── gitbot ├── config.yml.example ├── extra └── post-receive └── gitbot.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | config.yml 2 | .bundle 3 | coverage 4 | rdoc 5 | pkg 6 | *.gem 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | gitbot (1.0.0) 5 | cinch (~> 1.0.0) 6 | json 7 | sinatra (~> 1.1.0) 8 | 9 | GEM 10 | remote: http://rubygems.org/ 11 | specs: 12 | cinch (1.0.2) 13 | json (1.4.6) 14 | rack (1.2.1) 15 | sinatra (1.1.0) 16 | rack (~> 1.1) 17 | tilt (~> 1.1) 18 | tilt (1.1) 19 | 20 | PLATFORMS 21 | ruby 22 | 23 | DEPENDENCIES 24 | bundler (~> 1.0.0) 25 | cinch (~> 1.0.0) 26 | gitbot! 27 | json 28 | sinatra (~> 1.1.0) 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Emil Loer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | GitBot is a Ruby IRC bot that provides a builtin GitHub webhook server. 2 | 3 | Installation 4 | ============ 5 | 6 | You can install GitBot from RubyGems by typing: 7 | 8 | gem install gitbot 9 | 10 | Note that GitBot uses the Cinch IRC framework, which requires Ruby 1.9.1 or newer. 11 | 12 | Usage 13 | ===== 14 | 15 | * First you must make a `config.yml` file so that GitBot knows where it should connect to. You can make a copy of the `config.yml.example` file and use that as a basis. 16 | 17 | * After that just run `gitbot` in the directory where the config file is located. This will start the bot and its built-in webhook server. Alternatively, you can specify a different location for the config file with `gitbot [config_file]`. 18 | 19 | * You can now add a webhook to a GitHub project. Use the following url: `http://yourserver.com:5651/github`. You can specify a different port in the config file if you want. 20 | 21 | * Now do some commits and see the commit messages appearing on the IRC channel. 22 | 23 | License 24 | ======= 25 | 26 | Copyright (c) 2010-2011 Emil Loer 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 29 | 30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | -------------------------------------------------------------------------------- /bin/gitbot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "date" 4 | require "cinch" 5 | require "sinatra" 6 | require "yaml" 7 | require "json" 8 | 9 | config_file = ARGV.shift || "config.yml" 10 | if not File.exists? config_file 11 | puts "Can't find config file #{config_file}" 12 | puts "Either create it or specify another config file with: #{File.basename $0} [filename]" 13 | exit 14 | end 15 | 16 | $config = YAML.load_file config_file 17 | 18 | $bot = Cinch::Bot.new do 19 | configure do |c| 20 | c.nick = $config["irc"]["nick"] 21 | c.user = "gitbot" 22 | c.realname = "GitBot" 23 | c.server = $config["irc"]["server"] 24 | c.port = $config["irc"]["port"] 25 | c.channels = $config["irc"]["channels"] 26 | end 27 | end 28 | 29 | Thread.new do 30 | $bot.start 31 | end 32 | 33 | def say(repo,msg) 34 | $config["irc"]["channels"].each do |chan| 35 | unless $config["filters"].include? chan and not $config["filters"][chan].include? repo 36 | $bot.Channel(chan).send msg 37 | end 38 | end 39 | end 40 | 41 | configure do 42 | set :bind, $config["http"]["host"] 43 | set :port, $config["http"]["port"] 44 | set :logging, false 45 | set :lock, true 46 | end 47 | 48 | get "/" do 49 | "GitBot lives here. Direct your hooks to /github." 50 | end 51 | 52 | post "/github" do 53 | p params[:payload] 54 | push = JSON.parse(params[:payload]) 55 | 56 | repo = push["repository"]["name"] 57 | branch = push["ref"].gsub(/^refs\/heads\//,"") 58 | 59 | # sort commits by timestamp 60 | push["commits"].sort! do |a,b| 61 | ta = tb = nil 62 | begin 63 | ta = DateTime.parse(a["timestamp"]) 64 | rescue ArgumentError 65 | ta = Time.at(a["timestamp"].to_i) 66 | end 67 | 68 | begin 69 | tb = DateTime.parse(b["timestamp"]) 70 | rescue ArgumentError 71 | tb = Time.at(b["timestamp"].to_i) 72 | end 73 | 74 | ta <=> tb 75 | end 76 | 77 | # output first 3 commits 78 | push["commits"][0..2].each do |c| 79 | say repo, "\0030#{repo}:\0037 #{branch}\0033 #{c["author"]["name"]}\003 #{c["message"]}" 80 | end 81 | 82 | if push["commits"].length-2 > 0 83 | say repo, "\0030#{repo}:\0037 #{branch}\003 ... and #{push["commits"].length-2} more" 84 | end 85 | 86 | push.inspect 87 | end 88 | -------------------------------------------------------------------------------- /config.yml.example: -------------------------------------------------------------------------------- 1 | # IRC connection settings 2 | irc: 3 | server: "irc.freenode.org" 4 | port: 6667 5 | nick: GitBot 6 | 7 | # Here you can specify the channels that GitBot will join 8 | channels: 9 | - "#someproject" 10 | - "#otherproject" 11 | 12 | # HTTP server settings 13 | http: 14 | host: 0.0.0.0 15 | port: 5651 16 | 17 | # By default GitBot will announce all commits to all channels. You can 18 | # limit a certain channel to only a specified group of repositories by 19 | # adding a channel filter 20 | filters: 21 | "#someproject": [only_this_repo, and_that_repo] 22 | 23 | -------------------------------------------------------------------------------- /extra/post-receive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | HOST=example.com 4 | PORT=5651 5 | URL=http://host.com/path/to/repo 6 | NAME=repository_name 7 | 8 | GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) 9 | if [ -z "$GIT_DIR" ]; then 10 | echo >&2 "fatal: post-receive: GIT_DIR not set" 11 | exit 1 12 | fi 13 | 14 | if [ -n "$1" -a -n "$2" -a -n "$3" ]; then 15 | oldrev=$1 16 | newrev=$2 17 | refname=$3 18 | else 19 | read oldrev newrev refname 20 | fi 21 | oldrev=$(git rev-parse $oldrev) 22 | newrev=$(git rev-parse $newrev) 23 | 24 | function commits { 25 | log=`git log --pretty="format:%h%n%an%n%ae%n%at%n%s" $oldrev..$newrev | tr '\n' '|'` 26 | IFS="|" 27 | arr=( $log ) 28 | 29 | # TODO: get creations/deletions from --shortstat 30 | 31 | first=1 32 | for (( i = 0; i < ${#arr[@]}; i+=5 )); do 33 | # [[ ${arr[i+5]} =~ ^\ ([0-9]+)\ .*\ ([0-9]+)\ .*\ ([0-9]+)\ .*$ ]] 34 | # changed=${BASH_REMATCH[1]} 35 | 36 | [[ $first == 0 ]] && echo "," 37 | echo "{ 38 | \"id\": \"${arr[i]}\", 39 | \"message\": \"${arr[i+4]}\", 40 | \"timestamp\": \"${arr[i+3]}\", 41 | \"url\": \"$URL\", 42 | \"added\": [], 43 | \"removed\": [], 44 | \"modified\": [], 45 | \"author\": { 46 | \"name\": \"${arr[i+1]}\", 47 | \"email\": \"${arr[i+2]}\" 48 | } 49 | }" 50 | 51 | first=0 52 | done 53 | 54 | } 55 | 56 | function json { 57 | echo "{ 58 | \"before\": \"$oldrev\", 59 | \"after\": \"$newrev\", 60 | \"ref\": \"$refname\", 61 | \"repository\": { 62 | \"name\": \"$NAME\", 63 | \"url\": \"$URL\", 64 | \"pledgie\": \"\", 65 | \"description\": \"Unknown\", 66 | \"homepage\": \"$URL\", 67 | \"watchers\": 0, 68 | \"forks\": 0, 69 | \"private\": true, 70 | \"owner\": { 71 | \"name\": \"Unknown\", 72 | \"email\": \"Unknown\" 73 | } 74 | }, 75 | \"commits\": [ 76 | $(commits) 77 | ] 78 | }" 79 | } 80 | 81 | json | curl --form "payload=<-" http://$HOST:$PORT/github 82 | -------------------------------------------------------------------------------- /gitbot.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{gitbot} 5 | s.version = "1.0.0" 6 | s.platform = Gem::Platform::RUBY 7 | s.licenses = ["MIT"] 8 | s.authors = ["Emil Loer"] 9 | s.email = %q{emil@koffietijd.net} 10 | s.homepage = %q{http://github.com/thedjinn/gitbot} 11 | s.summary = %q{An IRC bot that listens to GitHub webhooks} 12 | s.description = %q{An IRC bot that listens to GitHub webhooks} 13 | 14 | #s.rubyforge_project = "gitbot" 15 | 16 | s.files = `git ls-files`.split("\n") 17 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 18 | s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } 19 | s.require_paths = ["lib"] 20 | 21 | s.extra_rdoc_files = [ 22 | "LICENSE.txt", 23 | "README.markdown" 24 | ] 25 | 26 | s.default_executable = %q{gitbot} 27 | 28 | s.add_runtime_dependency(%q, ["~> 1.0.0"]) 29 | s.add_runtime_dependency(%q, ["~> 1.1.0"]) 30 | s.add_runtime_dependency(%q, [">= 0"]) 31 | 32 | s.add_development_dependency(%q, ["~> 1.0.0"]) 33 | 34 | s.required_ruby_version = Gem::Requirement.new(">= 1.9.1") 35 | end 36 | --------------------------------------------------------------------------------