├── README.md ├── git └── post-receive.rb └── mercurial └── mercurial-changegroup.rb /README.md: -------------------------------------------------------------------------------- 1 | AppVeyor webhooks 2 | ================= 3 | 4 | > WARNING: This is pre-release software - "generic" Git and Mercurial repositories are not yet implemented in AppVeyor. 5 | 6 | When you push new commits to your remote repository you want AppVeyor to instantly start a new build for these changes. Polling repositoris is bad. Fortunately, both Git and Mercurial support hooks - extensibility mechanism that allows calling custom scripts on various server-side events such as "code received". Hook script collects the information about commit(s) and send JSON request to AppVeyor to start a new build. 7 | 8 | This repository contains Git and Mercurial server-side hooks for triggering AppVeyor builds. For maximum interoperability between Linux and Windows hooks are written in Ruby. There is only one external dependency - `json`. 9 | 10 | ## Installing Mercurial hook 11 | 12 | Mercurial hook is called on `changegroup` event - this is run after a group of changesets has been brought into the repository from elsewhere. See [Mercurial hooks](http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html) for more details. 13 | 14 | Hook can be installed system-wide for all repositories and per-repository. See [Mercurial configuration files](http://www.selenic.com/mercurial/hgrc.5.html) for more details. 15 | 16 | To intall system-wide hook on Windows open (or create) `%USERPROFILE%\Mercurial.ini` and add the following: 17 | 18 | ```ini 19 | [hooks] 20 | changegroup.appveyor = ruby \mercurial-changegroup.rb 21 | ``` 22 | 23 | ## Installing Git hook 24 | 25 | Git hook is called on `post-receive` server-side event. See [Git hooks](http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) for more details. Server-side hooks are called on "bare" repositories only. Git hooks are located in `\hooks` directory. 26 | 27 | To install Git hook copy `post-receive.rb` to `\hooks` directory as `post-receive` (remove `.rb` extension). On Linux set execute permissions on hook script file. 28 | 29 | Git hook is configured through git configuration. To set webhook URL: 30 | 31 | git config appveyor.webhook 32 | 33 | 34 | 35 | ## Fixing SSL in Ruby 36 | - Download http://curl.haxx.se/ca/cacert.pem to `` 37 | - `SET SSL_CERT_FILE=\cacert.pem` 38 | -------------------------------------------------------------------------------- /git/post-receive.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'net/https' 4 | require 'json' 5 | require 'uri' 6 | require 'securerandom' 7 | require 'pathname' 8 | require 'date' 9 | 10 | def get_git_config(setting_name) 11 | val =`git config #{setting_name}` 12 | if $?.success? 13 | val 14 | else 15 | nil 16 | end 17 | end 18 | 19 | def get_git_blob_contents(ref, fileName) 20 | contents = `git show #{ref}:#{fileName}` 21 | if $?.success? 22 | contents 23 | else 24 | nil 25 | end 26 | end 27 | 28 | def parse_commits(lines, separator) 29 | commits = [] 30 | commit = { 31 | :author => {} 32 | } 33 | num = 1 34 | message = [] 35 | lines.each { |line| 36 | case 37 | when num == 1 38 | commit[:id] = line 39 | when num == 2 40 | commit[:author][:name] = line 41 | when num == 3 42 | commit[:author][:email] = line 43 | when num == 4 44 | commit[:timestamp] = DateTime.parse(line) 45 | when num > 4 && line != separator 46 | message << line 47 | when line == separator 48 | commit[:message] = message.join('\n') 49 | commits << commit 50 | 51 | num = 0 52 | message = [] 53 | commit = { 54 | :author => {} 55 | } 56 | end 57 | num += 1 58 | } 59 | commits 60 | end 61 | 62 | start_commit_id = nil 63 | end_commit_id = nil 64 | ref = nil 65 | 66 | # read input data from STDIN in the format "base commit ref" 67 | STDIN.each_line do |line| 68 | start_commit_id, end_commit_id, ref = line.strip.split 69 | end 70 | 71 | # current directory is repository root 72 | repo_path = Dir.pwd 73 | repo_name = Pathname.new(repo_path).basename 74 | 75 | #puts "Input data: #{start_commit_id} #{end_commit_id} #{ref}" 76 | #puts "Repository path: #{repo_path}" 77 | 78 | # get git config 79 | webhook_url = get_git_config("appveyor.webhook") 80 | #puts "Webhook URL: #{webhook_url}" 81 | 82 | comments_end = SecureRandom.hex 83 | log_format = "--date=rfc --format=%H%n%an%n%ae%n%ad%n%B#{comments_end}" 84 | commits = [] 85 | 86 | if start_commit_id == "0000000000000000000000000000000000000000" 87 | # tag 88 | result = `git log #{end_commit_id} -1 #{log_format}`.split("\n") 89 | commits += parse_commits(result, comments_end) 90 | else 91 | # get commits exclude start commit 92 | result = `git log #{start_commit_id}..#{end_commit_id} #{log_format}`.split("\n") 93 | commits += parse_commits(result, comments_end) 94 | 95 | # get start commit only 96 | result = `git log #{start_commit_id} -1 #{log_format}`.split("\n") 97 | commits += parse_commits(result, comments_end) 98 | end 99 | 100 | #commits.each { |commit| puts "commit: #{commit[:id]}" } 101 | 102 | # get blob contents 103 | appveyor_yml = get_git_blob_contents(end_commit_id, "appveyor.yml") 104 | 105 | payload = { 106 | :ref => ref, 107 | :before => start_commit_id, 108 | :after => end_commit_id, 109 | :commits => commits, 110 | :head_commit => commits.first, 111 | :repository => { 112 | :name => repo_name 113 | }, 114 | :config => appveyor_yml 115 | } 116 | 117 | # send webhook 118 | uri = URI(webhook_url) 119 | req = Net::HTTP::Post.new(uri.request_uri, initheader = {'Content-Type' =>'application/json'}) 120 | # ruby 2.0: req = Net::HTTP::Post.new uri 121 | #req.basic_auth 'username', 'password' 122 | req.body = payload.to_json 123 | 124 | response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == "https") do |http| 125 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 126 | http.request req 127 | end 128 | 129 | if response.code != "200" and response.code != "204" 130 | raise "Error sending webhook: #{response.code}" 131 | end 132 | -------------------------------------------------------------------------------- /mercurial/mercurial-changegroup.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'net/https' 4 | require 'json' 5 | require 'uri' 6 | require 'securerandom' 7 | require 'pathname' 8 | require 'date' 9 | 10 | def get_hg_file_contents(ref, fileName) 11 | contents = `hg cat #{fileName} -r #{ref}` 12 | if $?.success? 13 | contents 14 | else 15 | nil 16 | end 17 | end 18 | 19 | def parse_commits(lines, separator) 20 | commits = [] 21 | commit = { 22 | :author => {} 23 | } 24 | num = 1 25 | message = [] 26 | lines.each { |line| 27 | case 28 | when num == 1 29 | commit[:branch] = line 30 | when num == 2 31 | commit[:id] = line 32 | when num == 3 33 | commit[:author][:name] = line 34 | when num == 4 35 | commit[:author][:email] = line 36 | when num == 5 37 | commit[:timestamp] = DateTime.parse(line) 38 | when num == 6 39 | commit[:tag] = line 40 | when num > 6 && line != separator 41 | message << line 42 | when line == separator 43 | commit[:message] = message.join('\n') 44 | commits << commit 45 | 46 | num = 0 47 | message = [] 48 | commit = { 49 | :author => {} 50 | } 51 | end 52 | num += 1 53 | } 54 | commits 55 | end 56 | 57 | commit_id = ENV["HG_NODE"] 58 | repo_url = ENV["HG_URL"] 59 | 60 | # current directory is repository root 61 | repo_path = Dir.pwd 62 | repo_name = Pathname.new(repo_path).basename 63 | 64 | #puts "Input data: #{start_commit_id} #{end_commit_id} #{ref}" 65 | #puts "Repository path: #{repo_path}" 66 | 67 | # get git config 68 | webhook_url = ARGV[0] 69 | #puts "Webhook URL: #{webhook_url}" 70 | 71 | comments_end = SecureRandom.hex 72 | 73 | result = `hg log -r #{commit_id} --template "{branch}\n{node}\n{author|user}\n{author|email}\n{date|date}\n{tags}\n{desc}\n#{comments_end}\n"`.split("\n") 74 | commits = parse_commits(result, comments_end) 75 | 76 | #commits.each { |commit| puts "commit: #{commit[:id]}" } 77 | 78 | # get blob contents 79 | appveyor_yml = get_hg_file_contents(commit_id, "appveyor.yml") 80 | 81 | payload = { 82 | :commit => commits.first, 83 | :repository => { 84 | :name => repo_name, 85 | :url => repo_url 86 | }, 87 | :config => appveyor_yml 88 | } 89 | 90 | # send webhook 91 | uri = URI(webhook_url) 92 | req = Net::HTTP::Post.new(uri.path, initheader = {'Content-Type' =>'application/json'}) 93 | # ruby 2.0: req = Net::HTTP::Post.new uri 94 | #req.basic_auth 'username', 'password' 95 | req.body = payload.to_json 96 | 97 | response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == "https") do |http| 98 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 99 | http.request req 100 | end 101 | 102 | if response.code != "200" and response.code != "204" 103 | raise "Error sending webhook: #{response.code}" 104 | end 105 | --------------------------------------------------------------------------------