roxanne 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/Guardfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +131 -0
- data/Rakefile +9 -0
- data/bin/roxanne +16 -0
- data/config/config.yml +6 -0
- data/lib/roxanne/configuration/yaml.rb +46 -0
- data/lib/roxanne/configuration.rb +26 -0
- data/lib/roxanne/consumers.rb +19 -0
- data/lib/roxanne/gpio/publisher.rb +24 -0
- data/lib/roxanne/http/consumer.rb +33 -0
- data/lib/roxanne/http_support.rb +43 -0
- data/lib/roxanne/jenkins/consumer.rb +34 -0
- data/lib/roxanne/loop.rb +35 -0
- data/lib/roxanne/publishers.rb +2 -0
- data/lib/roxanne/server.rb +24 -0
- data/lib/roxanne/test/consumer.rb +17 -0
- data/lib/roxanne/test/publisher.rb +18 -0
- data/lib/roxanne/travis/consumer.rb +32 -0
- data/lib/roxanne/version.rb +3 -0
- data/lib/roxanne.rb +10 -0
- data/roxanne.gemspec +37 -0
- data/service/etc_init.d_roxanne +21 -0
- data/service/roxanne.sh +5 -0
- data/spec/roxanne/configuration/yaml_spec.rb +48 -0
- data/spec/roxanne/configuration_spec.rb +37 -0
- data/spec/roxanne/gpio/publisher_spec.rb +54 -0
- data/spec/roxanne/jenkins/consumer_spec.rb +31 -0
- data/spec/roxanne/loop_spec.rb +80 -0
- data/spec/roxanne/server_spec.rb +62 -0
- data/spec/roxanne/travis/consumer_spec.rb +46 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/yaml/defaults.yml +8 -0
- data/spec/yaml/full_blown.yml +12 -0
- metadata +280 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c5d851cce33eec10554348917d888de2850376f4
|
4
|
+
data.tar.gz: 8170b18990fa9ad11befe6a0da395acdf487cda8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ea7d837226ee30baa49c0efec2b78242803de3190d173abc2b2c1ffc4cf9db4321549dff008e6b9b56fb8f6ee5f10328f2424b73a49d08937ea4c4fa49e0ba86
|
7
|
+
data.tar.gz: cc624af0b25c3717e0a8c4a247d3c18110308e2700c1c2fa538f634311e0d6a39e851db05d19eb2dd2a270fd7c3ffc696c00b75656c352a9d956590390383f36
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.1
|
data/.travis.yml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.1
|
6
|
+
notifications:
|
7
|
+
hipchat:
|
8
|
+
rooms:
|
9
|
+
secure: B+SptkYk6J9+PPJnqzA8S4hce6veT/bHciATE5MEA6fBd4gZQ2YMACgdaPqqzv1TU2Lr725uKFRpMQkE/E5B6oLGBcck7tAzZccKKSLiXyKMxgpyEcw+WC2laGhqqnfzd83iYjQujMy6kwksTknmgLV34HIuMUcyREYovEIxPgA=
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jef Mathiot
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Roxanne
|
2
|
+
|
3
|
+
Roxanne allows you to aggregate the status of Continuous Integration jobs or other sources and to
|
4
|
+
publish the results on a BigVisibleThing™ (traffic light, lava lamp, whatever).
|
5
|
+
|
6
|
+
[![Build Status](https://travis-ci.org/servebox/roxanne.png)](https://travis-ci.org/servebox/roxanne)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'roxanne'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install roxanne
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Configuration
|
25
|
+
|
26
|
+
Configuration is stored in a YAML file. By default, Roxanne will search for a `config/config.yml`
|
27
|
+
file.
|
28
|
+
|
29
|
+
#### Activation
|
30
|
+
|
31
|
+
Roxanne is configured by default to only be active between 9am and 20pm during working days. You
|
32
|
+
may use the `activation` section to change the default behavior:
|
33
|
+
|
34
|
+
```yaml
|
35
|
+
roxanne:
|
36
|
+
activation:
|
37
|
+
# Active from monday to wednesday
|
38
|
+
days: !ruby/range 1..3
|
39
|
+
# 07:00 to 14:59
|
40
|
+
timerange: !ruby/range 7...15
|
41
|
+
```
|
42
|
+
|
43
|
+
#### Consumers
|
44
|
+
|
45
|
+
Use the `consumers` section to specify consumers configuration:
|
46
|
+
|
47
|
+
```yaml
|
48
|
+
roxanne:
|
49
|
+
consumers:
|
50
|
+
jenkins:
|
51
|
+
class: "Roxanne::Jenkins::Consumer"
|
52
|
+
host: "192.168.20.30"
|
53
|
+
port: 8080
|
54
|
+
use_ssl: false
|
55
|
+
path: "/api/json"
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Publisher
|
59
|
+
|
60
|
+
Use the `publisher` section to specify consumers configuration:
|
61
|
+
|
62
|
+
```yaml
|
63
|
+
roxanne:
|
64
|
+
publisher:
|
65
|
+
class: "Roxanne::GPIO::Publisher"
|
66
|
+
green_pin: 25
|
67
|
+
orange_pin: 17
|
68
|
+
red_pin: 27
|
69
|
+
```
|
70
|
+
|
71
|
+
### Start the daemon
|
72
|
+
|
73
|
+
Start the daemon using the `start` command:
|
74
|
+
|
75
|
+
```
|
76
|
+
bundle exec roxanne start
|
77
|
+
```
|
78
|
+
|
79
|
+
If you want to specify a configuration file pass its path as the first argument:
|
80
|
+
|
81
|
+
```
|
82
|
+
bundle exec roxanne start /etc/roxanne/config.yml
|
83
|
+
```
|
84
|
+
|
85
|
+
Other available commands are obviously `stop`, `status` and `restart`.
|
86
|
+
|
87
|
+
### Available Consumers
|
88
|
+
|
89
|
+
#### Jenkins
|
90
|
+
|
91
|
+
Use the `Roxanne::Jenkins::Consumer` class. Available options:
|
92
|
+
|
93
|
+
* **host**: the Jenkins host name or IP address
|
94
|
+
* **port**: the TCP port the Jenkins service can be reached on
|
95
|
+
* **path**: the relative path to the JSON API (`/jenkins/api/json`)
|
96
|
+
* **username**: the username to use for HTTP Basic Auth
|
97
|
+
* **password**: the password to use for HTTP Basic Auth
|
98
|
+
* **disable_certificate_verification** : set to true if you use a self-signed SSL certificate
|
99
|
+
|
100
|
+
#### Travis CI
|
101
|
+
|
102
|
+
Use the `Roxanne::Travis::Consumer` class. Available options:
|
103
|
+
|
104
|
+
* **organization_or_user**: the Github organization or user to pull build states from
|
105
|
+
|
106
|
+
### Available Publishers
|
107
|
+
|
108
|
+
#### GPIO
|
109
|
+
|
110
|
+
Use the `Roxanne::GPIO::Consumer` class. Available options:
|
111
|
+
|
112
|
+
* **green_pin**: the GPIO pin to turn on when status changes to green
|
113
|
+
* **orange_pin**: the GPIO pin to turn on when status changes to orange
|
114
|
+
* **red_pin**: the GPIO pin to turn on when status changes to red
|
115
|
+
|
116
|
+
## Statuses
|
117
|
+
|
118
|
+
Every 5 seconds, Roxanne will loop to check the status of the consumers:
|
119
|
+
|
120
|
+
* if any of the consumers returns `red` the publisher receive `red`
|
121
|
+
* if all of the consumers return `green` the publisher receive `green`
|
122
|
+
* if the previous state **was not** `green` and one of the consumer returns `orange` the publisher
|
123
|
+
receive `orange`
|
124
|
+
|
125
|
+
## Contributing
|
126
|
+
|
127
|
+
1. Fork it
|
128
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
129
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
130
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
131
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/roxanne
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'roxanne'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
basedir = File.expand_path(File.join( File.dirname(__FILE__) , '..'))
|
7
|
+
%w(log pids).each do |dir|
|
8
|
+
dir = File.join(basedir, dir)
|
9
|
+
FileUtils.mkdir_p dir unless File.directory?(dir)
|
10
|
+
end
|
11
|
+
Roxanne::Server.spawn!(
|
12
|
+
log_file: File.join(basedir, 'log/roxanne.log'),
|
13
|
+
pid_file: File.join(basedir, 'pids/roxanne.pid'),
|
14
|
+
sync_log: true,
|
15
|
+
working_dir: basedir
|
16
|
+
)
|
data/config/config.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
module Roxanne
|
5
|
+
|
6
|
+
module Configuration
|
7
|
+
|
8
|
+
class YAML < Base
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
super
|
12
|
+
yaml = ::YAML.load( File.open( path ) ).with_indifferent_access[:roxanne]
|
13
|
+
override_activation(yaml[:activation])
|
14
|
+
build_consumers(yaml[:consumers])
|
15
|
+
build_publisher(yaml[:publisher])
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def override_activation(settings)
|
20
|
+
if settings
|
21
|
+
@active_days = settings[:days] if settings.has_key?(:days)
|
22
|
+
@timerange = settings[:timerange] if settings.has_key?(:timerange)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_consumers(hash)
|
27
|
+
(hash||{}).each do |id, hash|
|
28
|
+
@consumers << assign_properties( hash.delete(:class).constantize.new, hash )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_publisher(hash)
|
33
|
+
@publisher = assign_properties( hash.delete(:class).constantize.new, hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
def assign_properties(object, hash)
|
37
|
+
hash.each do |prop, value|
|
38
|
+
object.send "#{prop}=", value
|
39
|
+
end
|
40
|
+
object
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Roxanne
|
2
|
+
module Configuration
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_reader :consumers, :publisher, :active_days, :timerange
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@consumers = []
|
9
|
+
@publisher = nil
|
10
|
+
@active_days = 1..5
|
11
|
+
@timerange=8..19
|
12
|
+
end
|
13
|
+
|
14
|
+
def activated
|
15
|
+
will_activate(DateTime.now)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def will_activate( dt )
|
20
|
+
@active_days.include?(dt.wday) && @timerange.cover?(dt.hour)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'roxanne/configuration/yaml'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Roxanne
|
2
|
+
module Consumers
|
3
|
+
module Priority
|
4
|
+
|
5
|
+
def prioritize(current_status, former_status)
|
6
|
+
[:red, :orange].each do |status|
|
7
|
+
return status if status == former_status
|
8
|
+
end
|
9
|
+
current_status
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'roxanne/jenkins/consumer'
|
17
|
+
require 'roxanne/test/consumer'
|
18
|
+
require 'roxanne/travis/consumer'
|
19
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'taopaipai'
|
2
|
+
|
3
|
+
module Roxanne
|
4
|
+
module GPIO
|
5
|
+
class Publisher
|
6
|
+
attr_accessor :green_pin, :orange_pin, :red_pin
|
7
|
+
|
8
|
+
def disable
|
9
|
+
[green_pin, orange_pin, red_pin].each do |num|
|
10
|
+
Taopaipai.gpio.pin(num, direction: :out).value 0
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def push(previous, status)
|
15
|
+
return disable unless status
|
16
|
+
[:green, :orange, :red].each do |color|
|
17
|
+
Taopaipai.gpio.pin(send("#{color}_pin"), direction: :out).
|
18
|
+
value(color == status ? 1 : 0)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'roxanne/http_support'
|
2
|
+
|
3
|
+
module Roxanne
|
4
|
+
module HTTP
|
5
|
+
class Consumer
|
6
|
+
include Consumers::Priority
|
7
|
+
include Roxanne::HTTPSupport
|
8
|
+
|
9
|
+
def pull
|
10
|
+
response = fetch_response
|
11
|
+
case response
|
12
|
+
when Net::HTTPSuccess
|
13
|
+
handle_response(response.body)
|
14
|
+
when Net::HTTPRedirection
|
15
|
+
puts "The request has been redirected to #{response['location']}"
|
16
|
+
:red
|
17
|
+
else
|
18
|
+
puts "The request has failed #{response.error}"
|
19
|
+
:red
|
20
|
+
end
|
21
|
+
rescue Exception => e
|
22
|
+
puts "Unable to fetch data : #{e.message}"
|
23
|
+
:red
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_response(body)
|
27
|
+
puts "Does nothing, HTTP consumer should be overriden."
|
28
|
+
:green
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Roxanne
|
4
|
+
module HTTPSupport
|
5
|
+
attr_accessor :host
|
6
|
+
attr_accessor :use_ssl
|
7
|
+
attr_accessor :disable_certificate_verification
|
8
|
+
attr_accessor :path
|
9
|
+
attr_accessor :port
|
10
|
+
attr_accessor :username
|
11
|
+
attr_accessor :password
|
12
|
+
|
13
|
+
private
|
14
|
+
def fetch_response
|
15
|
+
connection = ::Net::HTTP.new(@host, @port)
|
16
|
+
connection.use_ssl=@use_ssl
|
17
|
+
if @disable_certificate_verification
|
18
|
+
# TODO retrieve the certificate from the remote system, see http://redcorundum.blogspot.com/2008/03/ssl-certificates-and-nethttps.html
|
19
|
+
# Avoid issues with autosigned certificates
|
20
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
21
|
+
end
|
22
|
+
request = Net::HTTP::Get.new(complete_path, {'Accept'=>accept})
|
23
|
+
|
24
|
+
# TODO Authentication
|
25
|
+
unless (username.nil? && password.nil?)
|
26
|
+
request.basic_auth username, password
|
27
|
+
else
|
28
|
+
end
|
29
|
+
connection.request(request)
|
30
|
+
end
|
31
|
+
|
32
|
+
def complete_path
|
33
|
+
@path
|
34
|
+
end
|
35
|
+
|
36
|
+
# Override if needed to force a particular format : application/json, text/xml, etc
|
37
|
+
protected
|
38
|
+
def accept
|
39
|
+
'*'
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'roxanne/http/consumer'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Roxanne
|
5
|
+
module Jenkins
|
6
|
+
class Consumer < Roxanne::HTTP::Consumer
|
7
|
+
|
8
|
+
def handle_response(body)
|
9
|
+
json = JSON.parse(body)
|
10
|
+
status = :green
|
11
|
+
json['jobs'].each do |job|
|
12
|
+
if COLORS.keys.include?(job['color'])
|
13
|
+
status = prioritize(to_status(job['color']), status)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
status
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
COLORS = {
|
21
|
+
'blue'=>:green,
|
22
|
+
'yellow'=>:green,
|
23
|
+
'red'=>:red,
|
24
|
+
'blue_anime'=>:orange,
|
25
|
+
'yellow_anime'=>:orange,
|
26
|
+
'red_anime'=>:orange
|
27
|
+
}
|
28
|
+
|
29
|
+
def to_status(hudson_color)
|
30
|
+
COLORS[hudson_color]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/roxanne/loop.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Roxanne
|
2
|
+
class Loop
|
3
|
+
def initialize(config)
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def cycle
|
8
|
+
if @config.activated
|
9
|
+
status = :green
|
10
|
+
@config.consumers.each do |consumer|
|
11
|
+
actual = consumer.pull
|
12
|
+
if actual == :red
|
13
|
+
status = :red
|
14
|
+
break
|
15
|
+
elsif actual == :orange && @previous != :green
|
16
|
+
status = :orange
|
17
|
+
end
|
18
|
+
end
|
19
|
+
publish( @previous, status )
|
20
|
+
@previous = status
|
21
|
+
else
|
22
|
+
@config.publisher.disable
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset
|
27
|
+
@config.publisher.push(nil, nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def publish(previous, current)
|
32
|
+
@config.publisher.push(previous, current)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'daemon_spawn'
|
2
|
+
|
3
|
+
module Roxanne
|
4
|
+
class Server < DaemonSpawn::Base
|
5
|
+
|
6
|
+
def start(args)
|
7
|
+
puts "Roxanne starting in #{self.working_dir}"
|
8
|
+
@controller = Loop.new(config(args))
|
9
|
+
loop do
|
10
|
+
@controller.cycle
|
11
|
+
sleep 5
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
@controller.reset
|
17
|
+
end
|
18
|
+
|
19
|
+
def config(args)
|
20
|
+
@config ||= Configuration::YAML.new(args.first || 'config/config.yml')
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Roxanne
|
2
|
+
module Test
|
3
|
+
class Publisher
|
4
|
+
def disable
|
5
|
+
puts "Publisher is now disabled"
|
6
|
+
end
|
7
|
+
|
8
|
+
def push(previous, status)
|
9
|
+
puts "Publisher switching from status #{default(previous)} to #{default(status)}"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def default(status)
|
14
|
+
status.nil? ? 'none' : status
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'travis'
|
2
|
+
|
3
|
+
module Roxanne
|
4
|
+
module Travis
|
5
|
+
class Consumer
|
6
|
+
include Consumers::Priority
|
7
|
+
|
8
|
+
attr_accessor :organization_or_user
|
9
|
+
|
10
|
+
def pull
|
11
|
+
repos = ::Travis::Repository.find_all(owner_name: organization_or_user)
|
12
|
+
status = :green
|
13
|
+
repos.select{|repo| repo.active? }.each do |repo|
|
14
|
+
status = prioritize(to_status(repo.last_build_state), status)
|
15
|
+
end
|
16
|
+
status
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
STATES = {
|
21
|
+
'failed' => :red,
|
22
|
+
'started' => :orange,
|
23
|
+
'passed' => :green
|
24
|
+
}
|
25
|
+
|
26
|
+
def to_status(travis_build_state)
|
27
|
+
STATES[travis_build_state]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/roxanne.rb
ADDED
data/roxanne.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'roxanne/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "roxanne"
|
8
|
+
spec.version = Roxanne::VERSION
|
9
|
+
spec.authors = ["Jef Mathiot", "Patrice Izzo", "Fabrice Nourisson",
|
10
|
+
"Benjamin Severac", "Eric Hartmann"]
|
11
|
+
spec.email = ["foss@servebox.com"]
|
12
|
+
spec.description = %q{Roxanne: publish your CI status to your device of choice}
|
13
|
+
spec.summary = %q{Aggregate the status of Continuous Integration jobs or other sources and
|
14
|
+
publish them}
|
15
|
+
spec.homepage = "http://github.com/servebox/roxanne"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_dependency 'json'
|
24
|
+
spec.add_dependency 'daemon-spawn'
|
25
|
+
spec.add_dependency 'activesupport', '>= 3.0.0'
|
26
|
+
spec.add_dependency 'taopaipai', '~> 0.1.1'
|
27
|
+
spec.add_dependency 'travis', '~> 1.6.8'
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
30
|
+
spec.add_development_dependency "rake"
|
31
|
+
spec.add_development_dependency "minitest", "~> 5.0.7"
|
32
|
+
spec.add_development_dependency "minitest-implicit-subject", "~> 1.4.0"
|
33
|
+
spec.add_development_dependency "rb-readline", "~> 0.5.0"
|
34
|
+
spec.add_development_dependency "guard-minitest", "~> 2.1.3"
|
35
|
+
spec.add_development_dependency "timecop"
|
36
|
+
spec.add_development_dependency "mocha"
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
case "$1" in
|
4
|
+
start)
|
5
|
+
su roxanne -c "bash /var/lib/roxanne/roxanne/service/roxanne.sh start" ; exit
|
6
|
+
;;
|
7
|
+
stop)
|
8
|
+
su roxanne -c "bash /var/lib/roxanne/roxanne/service/roxanne.sh stop" ; exit
|
9
|
+
;;
|
10
|
+
status)
|
11
|
+
;;
|
12
|
+
restart|force-reload)
|
13
|
+
su roxanne -c "bash /var/lib/roxanne/roxanne/service/roxanne.sh restart" ; exit
|
14
|
+
;;
|
15
|
+
*)
|
16
|
+
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
|
17
|
+
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
18
|
+
exit 3
|
19
|
+
;;
|
20
|
+
esac
|
21
|
+
|