fett 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4410cd737f964ba2a93ae57d82565514fac1a3fc
4
+ data.tar.gz: 86440b24821f49f0b49d792d9a2780a78e3a2f55
5
+ SHA512:
6
+ metadata.gz: e0a5efb07bf106313698a9d5261b621ec621701d1d784e0fcc8db1950c649e7bfbc19389575b75ba223e7b2764782a7440a7351edee437d73c126113328e07ac
7
+ data.tar.gz: 9b20e4e8335eaff5cce8fec1940c06e08022f33121ca7bba688dc15614d12db8271c1e43b3e89b7d6ec7118c5e10f2e3aacf12ed75ed115de5978e943c754fa4
@@ -0,0 +1,83 @@
1
+ Fett
2
+ ======
3
+
4
+ Fett talks with [GitHub](https://github.com), [Slack](https://slack.com) and [Jenkins](https://jenkins.com) to give you all the information about your builds right in your chat room.
5
+
6
+ Installing
7
+ ----------
8
+
9
+ * Fett requires [Jenkins](https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins), I'm using the 1.574 version but it works on 1.427 too. The [Notification Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Notification+Plugin) is required in order to tell Fett about how last build went.
10
+ * I've only tested Fett on [Ruby](https://www.ruby-lang.org/es/) 2.1.0. It may work on older versions, but I haven't tested it. Anyways, if you give Fett a shot and get it working in any other version, give me a shout.
11
+
12
+ Install the dependencies:
13
+
14
+ ```bash
15
+ $ bundle install --binstubs
16
+ ```
17
+
18
+ Run the server:
19
+
20
+ ```bash
21
+ $ bundle exec rackup -o 0.0.0.0
22
+ ```
23
+
24
+ Done :)
25
+
26
+ Configuring
27
+ -----------
28
+
29
+ Fett makes heavy use of environment variables (all of them required):
30
+
31
+ * `FETT_URL`: The URL where Fett will listen. Eg: https://fett.example.com
32
+
33
+ * `FETT_GITHUB_LOGIN`: Any GitHub with access to your repos. Fett will use it to talk to the GitHub API. Eg: pedrogimenez
34
+ * `FETT_GITHUB_PASSWORD`: The password for the previous GitHub login. Eg: yolo
35
+
36
+
37
+ * `FETT_JENKINS_URL`: The URL where Jenkins is hidden. Eg: https://jenkins.example.com
38
+ * `FETT_JENKINS_USERNAME`: HTTP Basic Auth user. Eg: pedrogimenez
39
+ * `FETT_JENKINS_PASSWORD`: HTTP Basic Auth password. Eg: yolo
40
+
41
+
42
+ * `FETT_SLACK_URL`: Your Slack webhook URL. Eg: https://example.slack.com/services/hooks/incoming-webhook?token=xxxx
43
+
44
+
45
+ * `FETT_REDIS_HOST`: The host where Redis is installed. Eg: 127.0.0.1
46
+ * `FETT_REDIS_PORT`: The port where Redis will be listening. Eg: 6379
47
+ * `FETT_REDIS_PASSWORD`: Redis Auth Password. Eg: yolo
48
+ * `FETT_REDIS_DATABASE`: Redis database. Eg: 1
49
+
50
+ Hacking
51
+ -------
52
+
53
+ Install the dependencies:
54
+
55
+ ```bash
56
+ $ bundle install --binstubs
57
+ ```
58
+
59
+ You need to setup the environment variables related to Redis before running the tests:
60
+
61
+ * `FETT_REDIS_HOST`
62
+ * `FETT_REDIS_PORT`
63
+ * `FETT_REDIS_PASSWORD`
64
+ * `FETT_REDIS_DATABASE`
65
+
66
+ Run the tests:
67
+
68
+ ```bash
69
+ $ script/cibuild
70
+ ```
71
+
72
+ Run the server:
73
+
74
+ ```bash
75
+ $ bundle exec rackup -o 0.0.0.0
76
+ ```
77
+
78
+ Done :)
79
+
80
+ Contributing
81
+ ------------
82
+
83
+ Just fork the repo and send a PR <3
@@ -0,0 +1,2 @@
1
+ current_dir = File.dirname(__FILE__)
2
+ Dir["#{current_dir}/**/*.rb"].each { |file| require_relative file }
@@ -0,0 +1,74 @@
1
+ require "json"
2
+ require "sinatra/base"
3
+ require "sinatra/json"
4
+ require "octokit"
5
+ require "jenkins_api_client"
6
+
7
+ module Fett
8
+ class App < Sinatra::Base
9
+ helpers Sinatra::JSON
10
+
11
+ before do
12
+ Fett::RedisConnection.initialize
13
+ end
14
+
15
+ get "/ping" do
16
+ "PONG"
17
+ end
18
+
19
+ post "/repositories" do
20
+ transaction success: 204 do
21
+ Fett::Listener.listen(params[:repository])
22
+ headers["location"] = "#{ENV.fetch("FETT_URL")}/builds"
23
+ end
24
+ end
25
+
26
+ post "/builds" do
27
+ transaction success: 204 do
28
+ halt 400 if %w{ping pull_request}.include?(request.env["HTTP_X_GITHUB_EVENT"])
29
+
30
+ response = JSON.parse(request.env["rack.input"].read, symbolize_names: true)
31
+
32
+ Fett::Builder.build(
33
+ response[:repository][:full_name],
34
+ response[:pusher][:name],
35
+ response[:head_commit][:message],
36
+ response[:head_commit][:url],
37
+ response[:ref],
38
+ { GIT_SHA1: response[:head_commit][:id], GIT_BRANCH: response[:ref] }
39
+ )
40
+ end
41
+ end
42
+
43
+ post "/builds/status" do
44
+ response = JSON.parse(request.env["rack.input"].read, symbolize_names: true)
45
+
46
+ status = "pending"
47
+
48
+ if response[:build][:status] == "SUCCESS"
49
+ status = "success"
50
+ elsif response[:build][:status] == "FAILURE"
51
+ status = "failure"
52
+ end
53
+
54
+ return unless %w{STARTED FINISHED}.include?(response[:build][:phase])
55
+
56
+ Fett::BuildStatusNotifier.notify(response[:name], response[:build][:parameters][:GIT_SHA1], status)
57
+ end
58
+
59
+ def transaction(options, &block)
60
+ begin
61
+ status options[:success]
62
+
63
+ response = yield
64
+
65
+ return if options[:success] == 204
66
+
67
+ json response
68
+ rescue RuntimeError => error
69
+ status 400
70
+ json error: error.message
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,13 @@
1
+ module Fett
2
+ class BuildStatusColorHandler
3
+ def self.color(status)
4
+ if status == "success"
5
+ color = "#36a64f"
6
+ elsif status == "failure"
7
+ color = "#d00000"
8
+ elsif status == "pending"
9
+ color = "#ffcc66"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Fett
2
+ class BuildStatusMessageComposer
3
+ def self.compose(repository, status)
4
+ message = "Build of #{repository.fullname}@<#{repository.last_commit_url}|#{repository.last_branch}> by #{repository.last_pusher}."
5
+ message += " Well done :sparkles:." if status == "success"
6
+ message
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Fett
2
+ class BuildStatusNotifier
3
+ def self.notify(jenkins_name, commit_sha1, status)
4
+ fullname = jenkins_name.sub(/\./, "/")
5
+ repository = Repositories.repository_of_fullname(fullname)
6
+
7
+ GitHub.new_build_status(repository, commit_sha1, status)
8
+ Slack.build_status_message(repository, status)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module Fett
2
+ class Builder
3
+ def self.build(fullname, last_pusher, last_message, last_commit_url, last_branch, params)
4
+ repository = Repositories.repository_of_fullname(fullname)
5
+
6
+ repository.last_pusher = last_pusher
7
+ repository.last_message = last_message
8
+ repository.last_commit_url = last_commit_url
9
+ repository.last_branch = last_branch.split("/").last
10
+
11
+ Jenkins.new_build(repository, params)
12
+
13
+ Repositories.put(repository)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ module Fett
2
+ class GitHub
3
+ LOGIN = ENV.fetch("FETT_GITHUB_LOGIN")
4
+ PASSWORD = ENV.fetch("FETT_GITHUB_PASSWORD")
5
+ FETT_URL = ENV.fetch("FETT_URL")
6
+
7
+ def self.new_hook(repository)
8
+ client.create_hook(repository.fullname, "web", *params)
9
+ end
10
+
11
+ def self.new_build_status(repository, commit_sha1, status)
12
+ message = GitHubStatusMessageComposer.compose(status)
13
+ client.create_status(repository.fullname, commit_sha1, status, description: message)
14
+ end
15
+
16
+ private
17
+
18
+ def self.client
19
+ Octokit::Client.new(login: LOGIN, password: PASSWORD)
20
+ end
21
+
22
+ def self.params
23
+ [{
24
+ url: "#{FETT_URL}/builds",
25
+ content_type: "json"
26
+ },
27
+ {
28
+ events: ["push"],
29
+ active: true
30
+ }]
31
+ end
32
+
33
+ private_class_method :client, :params
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module Fett
2
+ class GitHubStatusMessageComposer
3
+ def self.compose(status)
4
+ if status == "success"
5
+ "I love this code. I love it more than sharks love blood"
6
+ elsif status == "failure"
7
+ "My great concern is not whether you have failed, but whether you are content with your failure"
8
+ else
9
+ "Don’t count every hour in the day, make every hour in the day count"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Fett
2
+ class Jenkins
3
+ URL = ENV.fetch("FETT_JENKINS_URL")
4
+ USERNAME = ENV.fetch("FETT_JENKINS_USERNAME")
5
+ PASSWORD = ENV.fetch("FETT_JENKINS_PASSWORD")
6
+
7
+ def self.new_job(repository, template)
8
+ client.job.create(repository.jenkins_name, template.content)
9
+ end
10
+
11
+ def self.new_build(repository, params)
12
+ client.job.build(repository.jenkins_name, params)
13
+ end
14
+
15
+ private
16
+
17
+ def self.client
18
+ JenkinsApi::Client.new(server_url: URL, username: USERNAME, password: PASSWORD)
19
+ end
20
+
21
+ private_class_method :client
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module Fett
2
+ class Listener
3
+ FETT_URL = ENV.fetch("FETT_URL")
4
+
5
+ def self.listen(fullname)
6
+ repository = RepositoryFactory.build(fullname)
7
+
8
+ template = Template.new("default", {
9
+ :name => "default",
10
+ :repo => "git@github.com:#{fullname}.git",
11
+ :callback_url => "#{FETT_URL}/builds/status"
12
+ })
13
+
14
+ Jenkins.new_job(repository, template)
15
+ GitHub.new_hook(repository)
16
+ Slack.listening_message(repository)
17
+ Repositories.put(repository)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require "redis"
2
+
3
+ module Fett
4
+ class RedisConnection
5
+ HOST = ENV.fetch("FETT_REDIS_HOST")
6
+ PORT = ENV.fetch("FETT_REDIS_PORT")
7
+ PASSWORD = ENV.fetch("FETT_REDIS_PASSWORD")
8
+ DATABASE = ENV.fetch("FETT_REDIS_DATABASE")
9
+
10
+ def self.initialize
11
+ params = { host: HOST, port: PORT, db: DATABASE }
12
+ params.merge!(password: PASSWORD) unless PASSWORD.empty?
13
+ @@client ||= ::Redis.new(params)
14
+ end
15
+
16
+ def self.client
17
+ @@client
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module Fett
2
+ class Repositories
3
+ def self.put(repository)
4
+ RedisConnection.client.set("repositories:#{repository.fullname}", Marshal.dump(repository))
5
+ end
6
+
7
+ def self.repository_of_fullname(fullname)
8
+ Marshal.load(RedisConnection.client.get("repositories:#{fullname}"))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module Fett
2
+ class Repository
3
+ attr_reader :last_pusher, :last_message, :last_commit_url, :last_branch
4
+ attr_writer :last_pusher, :last_message, :last_commit_url, :last_branch
5
+
6
+ def initialize(org, repository)
7
+ @org = org
8
+ @repository = repository
9
+ end
10
+
11
+ def jenkins_name
12
+ "#{@org}.#{@repository}"
13
+ end
14
+
15
+ def fullname
16
+ "#{@org}/#{@repository}"
17
+ end
18
+
19
+ def ==(another_repository)
20
+ fullname == another_repository.fullname
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ module Fett
2
+ class RepositoryFactory
3
+ def self.build(fullname)
4
+ org, repository = fullname.split("/")
5
+ Fett::Repository.new(org, repository)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ require "json"
2
+ require "nestful"
3
+
4
+ module Fett
5
+ class Slack
6
+ SLACK_URL = ENV.fetch("FETT_SLACK_URL")
7
+
8
+ def self.listening_message(repository)
9
+ send_listening_message("kk, I'm listening to https://github.com/#{repository.fullname}")
10
+ end
11
+
12
+ def self.build_status_message(repository, status)
13
+ send_build_status_message(BuildStatusMessageComposer.compose(repository, status), status)
14
+ end
15
+
16
+ private
17
+
18
+ def self.send_listening_message(message)
19
+ send_message(text: message)
20
+ end
21
+
22
+ def self.send_build_status_message(message, status)
23
+ color = BuildStatusColorHandler.color(status)
24
+
25
+ attachment = {
26
+ color: color,
27
+ fields: [ value: message, short: true ]
28
+ }
29
+
30
+ send_message(attachments: [attachment])
31
+ end
32
+
33
+ def self.send_message(params)
34
+ params.merge!(username: "Fett", icon_url: "http://imagizer.imageshack.us/v2/150x100q90/538/K7OlPJ.png")
35
+ Nestful.post(SLACK_URL, payload: JSON.dump(params))
36
+ end
37
+
38
+ private_class_method :send_listening_message, :send_build_status_message, :send_message
39
+ end
40
+ end
@@ -0,0 +1,28 @@
1
+ require "tilt"
2
+
3
+ module Fett
4
+ class Template
5
+ def initialize(template_name, params = {})
6
+ @name = template_name
7
+ @params = params
8
+ end
9
+
10
+ def content
11
+ parse_template(@name, @params)
12
+ end
13
+
14
+ private
15
+
16
+ def parse_template(template_name, params)
17
+ template_name ||= "rb"
18
+
19
+ template = Tilt.new(path_for_template(template_name))
20
+
21
+ template.render(Object.new, params)
22
+ end
23
+
24
+ def path_for_template(template_name)
25
+ File.expand_path("../../../templates/#{template_name}.xml.erb", __FILE__)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ require "fett/build_status_color_handler"
2
+
3
+ describe Fett::BuildStatusColorHandler do
4
+ it "returns a color based on the status" do
5
+ expect(Fett::BuildStatusColorHandler.color("success")).to eq("#36a64f")
6
+ expect(Fett::BuildStatusColorHandler.color("failure")).to eq("#36a64f")
7
+ expect(Fett::BuildStatusColorHandler.color("pending")).to eq("#36a64f")
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ require "fett/build_status_message_composer"
2
+
3
+ describe Fett::BuildStatusMessageComposer do
4
+ let(:repository) do
5
+ double(:repository,
6
+ last_pusher: "pedrogimenez",
7
+ last_message: "Testing fett.",
8
+ last_commit_url: "https://github.com/pedrogimenez/fett/commit/7240a7b561e56702889565e46d6ba93784fe23c5",
9
+ last_branch: "master",
10
+ fullname: "pedrogimenez/fett")
11
+ end
12
+
13
+ it "composes a message when the status is `pending`" do
14
+ message = Fett::BuildStatusMessageComposer.compose(repository, "pending")
15
+
16
+ expect(message).to eq("Build of pedrogimenez/fett@<#{repository.last_commit_url}|master> by pedrogimenez.")
17
+ end
18
+
19
+ it "composes a message when the status is `success`" do
20
+ message = Fett::BuildStatusMessageComposer.compose(repository, "success")
21
+
22
+ expect(message).to eq("Build of pedrogimenez/fett@<#{repository.last_commit_url}|master> by pedrogimenez. Well done :sparkles:.")
23
+ end
24
+
25
+ it "composes a message when the status is `failure`" do
26
+ message = Fett::BuildStatusMessageComposer.compose(repository, "failure")
27
+
28
+ expect(message).to eq("Build of pedrogimenez/fett@<#{repository.last_commit_url}|master> by pedrogimenez.")
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ require "fett/build_status_notifier"
2
+
3
+ module Fett
4
+ class Repositories; end
5
+ class GitHub; end
6
+ class Slack; end
7
+ end
8
+
9
+ describe Fett::BuildStatusNotifier do
10
+ it "notifies GitHub and Slack about the last build status" do
11
+ repository = double(:repository)
12
+ expect(Fett::Repositories).to receive(:repository_of_fullname).and_return(repository)
13
+ expect(Fett::GitHub).to receive(:new_build_status)
14
+ .with(repository, "7240a7b561e56702889565e46d6ba93784fe23c5", "success")
15
+ expect(Fett::Slack).to receive(:build_status_message)
16
+ .with(repository, "success")
17
+
18
+ Fett::BuildStatusNotifier.notify("pedrogimenez.fett", "7240a7b561e56702889565e46d6ba93784fe23c5", "success")
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require "fett/builder"
2
+
3
+ module Fett
4
+ class Repositories; end
5
+ class Jenkins; end
6
+ end
7
+
8
+ describe Fett::Builder do
9
+ it "builds a commit and returns the result" do
10
+ repository = double(:repository, :last_pusher= => nil, :last_message= => nil, :last_commit_url= => nil, :last_branch= => nil)
11
+
12
+ expect(Fett::Repositories).to receive(:repository_of_fullname).with("pedrogimenez/fett").and_return(repository)
13
+ expect(Fett::Jenkins).to receive(:new_build).with(repository, params)
14
+ expect(Fett::Repositories).to receive(:put).with(repository)
15
+
16
+ Fett::Builder.build("pedrogimenez/fett", "pedrogimenez", "Testing fett.",
17
+ "https://github.com/pedrogimenez/fett/commit/7240a7b561e56702889565e46d6ba93784fe23c5", "refs/heads/master", params)
18
+ end
19
+
20
+ def params
21
+ { GIT_SHA1: "7240a7b561e56702889565e46d6ba93784fe23c5", GIT_BRANCH: "master" }
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ ENV["FETT_GITHUB_LOGIN"] = "pedrogimenez"
2
+ ENV["FETT_GITHUB_PASSWORD"] = "pedrogimenez"
3
+ ENV["FETT_URL"] = "https://fett.pedrogimenez.com"
4
+
5
+ require "fett/github"
6
+
7
+ module Fett
8
+ class GitHubStatusMessageComposer; end
9
+ end
10
+
11
+ module Octokit
12
+ class Client; end
13
+ end
14
+
15
+ describe Fett::GitHub do
16
+ it "creates a new hook in GitHub" do
17
+ repository = double(:repository, fullname: "pedrogimenez/fett")
18
+ client = double(:client)
19
+
20
+ expect(Octokit::Client).to receive(:new).and_return(client)
21
+ expect(client).to receive(:create_hook).with("pedrogimenez/fett", "web", *params)
22
+
23
+ Fett::GitHub.new_hook(repository)
24
+ end
25
+
26
+ it "sets the last build status" do
27
+ repository = double(:repository, fullname: "pedrogimenez/fett")
28
+ client = double(:client)
29
+
30
+ expect(Fett::GitHubStatusMessageComposer).to receive(:compose).with("success").and_return("yay!")
31
+ expect(Octokit::Client).to receive(:new).and_return(client)
32
+ expect(client).to receive(:create_status)
33
+ .with("pedrogimenez/fett", "7240a7b561e56702889565e46d6ba93784fe23c5", "success", { description: "yay!" })
34
+
35
+ Fett::GitHub.new_build_status(repository, "7240a7b561e56702889565e46d6ba93784fe23c5", "success")
36
+ end
37
+
38
+ def params
39
+ [{
40
+ url: "https://fett.pedrogimenez.com/builds",
41
+ content_type: "json"
42
+ },
43
+ {
44
+ events: ["push"],
45
+ active: true
46
+ }]
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ require "fett/github_status_message_composer"
2
+
3
+ describe Fett::GitHubStatusMessageComposer do
4
+ it "composes a messages based on the given status" do
5
+ expect(Fett::GitHubStatusMessageComposer.compose("success")).
6
+ to eq("I love this code. I love it more than sharks love blood")
7
+ expect(Fett::GitHubStatusMessageComposer.compose("failure")).
8
+ to eq("My great concern is not whether you have failed, but whether you are content with your failure")
9
+ expect(Fett::GitHubStatusMessageComposer.compose("pending")).
10
+ to eq("Don’t count every hour in the day, make every hour in the day count")
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ ENV["FETT_JENKINS_URL"] = "https://jenkins.pedrogimenez.com"
2
+ ENV["FETT_JENKINS_USERNAME"] = "pedrogimenez"
3
+ ENV["FETT_JENKINS_PASSWORD"] = "pedrogimenez"
4
+
5
+ require "fett/jenkins"
6
+
7
+ module JenkinsApi
8
+ class Client; end
9
+ end
10
+
11
+ describe Fett::Jenkins do
12
+ it "creates a new job in Jenkins" do
13
+ client = double(:client)
14
+ repository = double(:repository, jenkins_name: "pedrogimenez.fett")
15
+ template = double(:template, content: "template")
16
+
17
+ expect(JenkinsApi::Client).to receive(:new).and_return(client)
18
+ expect(client).to receive(:job).and_return(client)
19
+ expect(client).to receive(:create).with("pedrogimenez.fett", "template")
20
+
21
+ Fett::Jenkins.new_job(repository, template)
22
+ end
23
+
24
+ it "runs a new build in Jenkins" do
25
+ client = double(:client)
26
+ repository = double(:repository, jenkins_name: "pedrogimenez.fett")
27
+
28
+ expect(JenkinsApi::Client).to receive(:new).and_return(client)
29
+ expect(client).to receive(:job).and_return(client)
30
+ expect(client).to receive(:build).with("pedrogimenez.fett", params)
31
+
32
+ Fett::Jenkins.new_build(repository, params)
33
+ end
34
+
35
+ def params
36
+ { GIT_SHA1: "7240a7b561e56702889565e46d6ba93784fe23c5", GIT_BRANCH: "master" }
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ require "fett/listener"
2
+
3
+ module Fett
4
+ class RepositoryFactory; end
5
+ class Jenkins; end
6
+ class GitHub; end
7
+ class Slack; end
8
+ class Repositories; end
9
+ class Template; end
10
+ end
11
+
12
+ describe Fett::Listener do
13
+ it "listens new repositories" do
14
+ repository = double(:repository)
15
+ template = double(:template)
16
+ expect(Fett::RepositoryFactory).to receive(:build).with("pedrogimenez/fett").and_return(repository)
17
+ expect(Fett::Template).to receive(:new).and_return(template)
18
+ expect(Fett::Jenkins).to receive(:new_job).with(repository, template)
19
+ expect(Fett::GitHub).to receive(:new_hook).with(repository)
20
+ expect(Fett::Slack).to receive(:listening_message).with(repository)
21
+ expect(Fett::Repositories).to receive(:put).with(repository)
22
+
23
+ Fett::Listener.listen("pedrogimenez/fett")
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ require "fett/repository"
2
+ require "fett/repository_factory"
3
+ require "fett/repositories"
4
+ require "fett/redis_connection"
5
+
6
+ describe Fett::Repositories do
7
+ before do
8
+ Fett::RedisConnection.initialize
9
+ Fett::RedisConnection.client.flushdb
10
+ end
11
+
12
+ it "persists a repository" do
13
+ client = double(:client)
14
+ repository = Fett::RepositoryFactory.build("pedrogimenez/fett")
15
+
16
+ expect(Fett::RedisConnection).to receive(:client).and_return(client)
17
+ expect(client).to receive(:set)
18
+
19
+ Fett::Repositories.put(repository)
20
+ end
21
+
22
+ it "updates a repository" do
23
+ new_repository = Fett::RepositoryFactory.build("pedrogimenez/fett")
24
+ Fett::Repositories.put(new_repository)
25
+
26
+ new_repository.last_pusher = "pedrogimenez"
27
+ Fett::Repositories.put(new_repository)
28
+
29
+ repository = Fett::Repositories.repository_of_fullname("pedrogimenez/fett")
30
+ expect(repository.last_pusher).to eq("pedrogimenez")
31
+ end
32
+
33
+ it "returns a repository of fullname" do
34
+ repository = Fett::RepositoryFactory.build("pedrogimenez/fett")
35
+ Fett::Repositories.put(repository)
36
+
37
+ expect(Fett::Repositories.repository_of_fullname("pedrogimenez/fett")).to eq(repository)
38
+ end
39
+ end
@@ -0,0 +1,53 @@
1
+ ENV["FETT_SLACK_URL"] = "https://slack.com"
2
+
3
+ require "json"
4
+ require "fett/slack"
5
+
6
+ module Nestful
7
+ class Request; end
8
+ end
9
+
10
+ module Fett
11
+ class BuildStatusMessageComposer; end
12
+ class BuildStatusColorHandler; end
13
+ end
14
+
15
+ describe Fett::Slack do
16
+ it "sends a message to Slack when a repository is added to the system" do
17
+ repository = double(:repository, fullname: "pedrogimenez/fett")
18
+ expect(Nestful).to receive(:post).with("https://slack.com", listening_message_params)
19
+
20
+ Fett::Slack.listening_message(repository)
21
+ end
22
+
23
+ it "sends a message to Slack about the last build status" do
24
+ repository = double(:repository)
25
+
26
+ expect(Fett::BuildStatusMessageComposer).to receive(:compose).with(repository, "success").and_return("yay!")
27
+ expect(Fett::BuildStatusColorHandler).to receive(:color).with("success").and_return("#fff000")
28
+ expect(Nestful).to receive(:post).with("https://slack.com", build_message_params)
29
+
30
+ Fett::Slack.build_status_message(repository, "success")
31
+ end
32
+
33
+ def listening_message_params
34
+ {
35
+ payload: JSON.dump(
36
+ text: "kk, I'm listening to https://github.com/pedrogimenez/fett",
37
+ username: "Fett",
38
+ icon_url: "http://imagizer.imageshack.us/v2/150x100q90/538/K7OlPJ.png")
39
+ }
40
+ end
41
+
42
+ def build_message_params
43
+ {
44
+ payload: JSON.dump(
45
+ attachments: [
46
+ color: "#fff000",
47
+ fields: [ value: "yay!", short: true ]
48
+ ],
49
+ username: "Fett",
50
+ icon_url: "http://imagizer.imageshack.us/v2/150x100q90/538/K7OlPJ.png")
51
+ }
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <project>
3
+ <actions/>
4
+ <name><%= name %></name>
5
+ <logRotator>
6
+ <daysToKeep>-1</daysToKeep>
7
+ <numToKeep>-1</numToKeep>
8
+ <artifactDaysToKeep>-1</artifactDaysToKeep>
9
+ <artifactNumToKeep>-1</artifactNumToKeep>
10
+ </logRotator>
11
+ <keepDependencies>false</keepDependencies>
12
+ <properties>
13
+ <hudson.model.ParametersDefinitionProperty>
14
+ <parameterDefinitions>
15
+ <hudson.model.StringParameterDefinition>
16
+ <name>GIT_SHA1</name>
17
+ <description></description>
18
+ <defaultValue></defaultValue>
19
+ </hudson.model.StringParameterDefinition>
20
+ <hudson.model.StringParameterDefinition>
21
+ <name>GIT_BRANCH</name>
22
+ <description></description>
23
+ <defaultValue></defaultValue>
24
+ </hudson.model.StringParameterDefinition>
25
+ </parameterDefinitions>
26
+ </hudson.model.ParametersDefinitionProperty>
27
+ <com.tikal.hudson.plugins.notification.HudsonNotificationProperty>
28
+ <endpoints>
29
+ <com.tikal.hudson.plugins.notification.Endpoint>
30
+ <protocol>HTTP</protocol>
31
+ <url><%= callback_url %></url>
32
+ </com.tikal.hudson.plugins.notification.Endpoint>
33
+ </endpoints>
34
+ </com.tikal.hudson.plugins.notification.HudsonNotificationProperty>
35
+ </properties>
36
+ <scm class="hudson.scm.NullSCM"/>
37
+ <assignedNode>master</assignedNode>
38
+ <canRoam>false</canRoam>
39
+ <disabled>false</disabled>
40
+ <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
41
+ <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
42
+ <triggers class="vector"/>
43
+ <concurrentBuild>false</concurrentBuild>
44
+ <builders>
45
+ <hudson.tasks.Shell>
46
+ <command>
47
+ if [ ! -d &quot;./.git&quot; ]; then
48
+ git init
49
+ git remote add origin <%= repo %>
50
+ fi
51
+ git fetch -q origin
52
+ git reset -q --hard $GIT_SHA1
53
+ bundle install --path vendor/gems --binstubs
54
+ script/cibuild
55
+ </command>
56
+ </hudson.tasks.Shell>
57
+ </builders>
58
+ <publishers/>
59
+ <buildWrappers/>
60
+ </project>
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fett
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Pedro Gimenez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sinatra-contrib
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nestful
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: octokit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jenkins_api_client
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: tilt
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.4.1
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.4.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec-mocks
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 3.0.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 3.0.2
139
+ description: Fett brings all the information about your builds right to your chat
140
+ room
141
+ email:
142
+ - me@pedro.bz
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files:
146
+ - README.md
147
+ files:
148
+ - README.md
149
+ - lib/fett.rb
150
+ - lib/fett/app.rb
151
+ - lib/fett/build_status_color_handler.rb
152
+ - lib/fett/build_status_message_composer.rb
153
+ - lib/fett/build_status_notifier.rb
154
+ - lib/fett/builder.rb
155
+ - lib/fett/github.rb
156
+ - lib/fett/github_status_message_composer.rb
157
+ - lib/fett/jenkins.rb
158
+ - lib/fett/listener.rb
159
+ - lib/fett/redis_connection.rb
160
+ - lib/fett/repositories.rb
161
+ - lib/fett/repository.rb
162
+ - lib/fett/repository_factory.rb
163
+ - lib/fett/slack.rb
164
+ - lib/fett/template.rb
165
+ - spec/fett/build_status_color_handler.rb
166
+ - spec/fett/build_status_message_composer_spec.rb
167
+ - spec/fett/build_status_notifier_spec.rb
168
+ - spec/fett/builder_spec.rb
169
+ - spec/fett/github_spec.rb
170
+ - spec/fett/github_status_message_composer_spec.rb
171
+ - spec/fett/jenkins_spec.rb
172
+ - spec/fett/listener_spec.rb
173
+ - spec/fett/repositories_spec.rb
174
+ - spec/fett/slack_spec.rb
175
+ - templates/default.xml.erb
176
+ homepage: https://github.com/pedrogimenez/fett
177
+ licenses:
178
+ - MIT
179
+ metadata: {}
180
+ post_install_message:
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: 2.1.0
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.2.0
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Fett brings all the information about your builds right to your chat room
200
+ test_files: []