extreme_feedback_device 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fc66bca37d672e1868415f67627fd756c0a099a7
4
+ data.tar.gz: b027ef7580a3bcb5d052779bdf181487fef01899
5
+ SHA512:
6
+ metadata.gz: 944e3f32b1118fd3a18250cae2b3d0b308f4b575c804277e81f751e2aea5937b09a2ecc19e08d9076bcf99f92ccae75c92794f1c51a442b47c1b24254e4e24ee
7
+ data.tar.gz: b327e0f4da0a3f12e32cc79de0e8d20b411d2ce46d1337055962607cf26ef2b915f88853b033e8e4189d17361e48e879ca57e08bc7b4b5c5d12779150ff51aed
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ efd
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '2.0.0'
4
+
5
+ # Specify your gem's dependencies in extreme_feedback_device.gemspec
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 TODO: Write your name
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,114 @@
1
+ # ExtremeFeedbackDevice
2
+
3
+ TODO: Write a gem description
4
+
5
+ Fetches Jobs from a Jenkins CI via the REST JSON API and sets LEDs of an Extreme Feedback Device to the status of the corresponding Job.
6
+
7
+ Tested with Ruby v2.0.0p247 running on Raspberry Pi with Raspbian "wheezy" / Debian GNU/Linux 7 "wheezy".
8
+
9
+ ## Prepare
10
+
11
+ To get read-write permissions for the SPI-Device with udev you can add the following rule:
12
+
13
+ # /etc/udev/rules.d/99-spidev.rules
14
+ SUBSYSTEM=="spidev", GROUP="spidev", MODE="0660"
15
+
16
+ For this you first need to create a group spidev _(as root)_:
17
+
18
+ $ groupadd spidev
19
+
20
+ Add your user to the new group _(as root)_:
21
+
22
+ $ adduser foo spidev
23
+
24
+ After this, do a restart or run the following commands _(as root)_:
25
+
26
+ $ udevadm control --reload-rules
27
+ $ udevadm trigger --subsystem-match=spidev
28
+
29
+ ## Installation
30
+
31
+ Add this line to your application's Gemfile:
32
+
33
+ gem 'extreme_feedback_device'
34
+
35
+ And then execute:
36
+
37
+ $ bundle
38
+
39
+ Or install it yourself as:
40
+
41
+ $ gem install extreme_feedback_device
42
+
43
+ ## Settings
44
+
45
+ TODO: Write configuration instruction here
46
+
47
+ The default location for the settings file is: `$HOME/.extreme_feedback_device.yml`
48
+
49
+ * `jenkins.*`: ...
50
+ * `infiniti_loop.sleep`: the time _(in seconds)_ to sleep between each request.
51
+ * `pi.num_leds`: the number of LEDs on your attached Extreme Feedback Device.
52
+ * `pi.map_leds`: the mapping of LEDs _(with LED's index)_ to Jenkins Jobs _(with Job's name)_.
53
+ * `spi.devive`: the SPI Device to write on.
54
+
55
+ ### Example
56
+
57
+ default:
58
+ jenkins:
59
+ user: api
60
+ token: 0123456789ABCDEF
61
+ url: http://jenkins.example.com/
62
+ infiniti_loop:
63
+ sleep: 5
64
+ pi:
65
+ num_leds: 2
66
+ map_leds:
67
+ i_1: Jenkins - Master
68
+ i_0: Jenkins - Production
69
+ spi:
70
+ device: /dev/spidev0.0
71
+
72
+ ## Usage
73
+
74
+ Output of `extreme_feedback_device --help`:
75
+
76
+ extreme_feedback_device [OPTION] ...
77
+
78
+ -h, --help:
79
+ show help.
80
+
81
+ -m, --mode [run]:
82
+ set mode 'run' for a single run or 'loop' for infiniti loop.
83
+
84
+ -n, --namespace [default]:
85
+ settings namespace.
86
+
87
+ -p, --pid [~/.extreme_feedback_device.pid]:
88
+ pid file.
89
+
90
+ -s, --settings [~/.extreme_feedback_device.yml]:
91
+ settings file.
92
+
93
+ -v, --version:
94
+ show version.
95
+
96
+ ## Daemon
97
+
98
+ Copy the example init.d script and change it for your needs _(as root)_:
99
+
100
+ $ cp `gem contents extreme_feedback_device | grep vendor/init.d/extreme_feedback_device` /etc/init.d/extreme_feedback_device
101
+ $ chmod u+x /etc/init.d/extreme_feedback_device
102
+ $ vim /etc/init.d/extreme_feedback_device
103
+
104
+ If you want to start the daemon automaticaly on boot _(as root)_:
105
+
106
+ $ update-rc.d extreme_feedback_device defaults
107
+
108
+ ## Contributing
109
+
110
+ 1. Fork it
111
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
112
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
113
+ 4. Push to the branch (`git push origin my-new-feature`)
114
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
8
+
9
+
10
+ task :environment do
11
+ require 'extreme_feedback_device'
12
+ end
13
+
14
+ namespace :pi do
15
+ namespace :leds do
16
+ desc "turn all LEDs off / black"
17
+ task off: [:environment] do
18
+ ExtremeFeedbackDevice.pi.leds_black!
19
+ end
20
+
21
+ desc "turn all LEDs on / white"
22
+ task on: [:environment] do
23
+ ExtremeFeedbackDevice.pi.leds_white!
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ENV['EFD_ENV'] == 'development'
4
+ lib = File.expand_path('../../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ require 'pry'
8
+ end
9
+
10
+ begin
11
+ require 'extreme_feedback_device'
12
+ rescue LoadError
13
+ require 'rubygems'
14
+ require 'extreme_feedback_device'
15
+ end
16
+
17
+ ExtremeFeedbackDevice::CLI.instance.main
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'daemons'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'daemons'
8
+ end
9
+
10
+ Daemons.run(File.expand_path('../extreme_feedback_device', __FILE__))
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'extreme_feedback_device/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "extreme_feedback_device"
8
+ spec.version = ExtremeFeedbackDevice::VERSION
9
+ spec.authors = ["Michael Nowak"]
10
+ spec.email = ["nowak@taktsoft.com"]
11
+ spec.summary = %q{Sets LEDs of an Extreme Feedback Device to a Status of a Job in Jenkins}
12
+ spec.summary = "Fetches Jobs from a Jenkins CI via the REST JSON API and sets LEDs of an Extreme Feedback Device to the status of the corresponding Job."
13
+ spec.description = "#{spec.summary} Build and designed for running on a Raspberry Pi with Debian 7."
14
+ spec.homepage = "https://github.com/taktsoft/#{spec.name}"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "color", "~> 1.4"
23
+ spec.add_dependency "daemons", "~> 1.1"
24
+ spec.add_dependency "settingslogic", "~> 2.0"
25
+ spec.add_dependency "pidfile", "~> 0.3"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "guard-rspec", "~> 2.5"
29
+ spec.add_development_dependency "pry", "~> 0.9"
30
+ spec.add_development_dependency "rake", "~> 10.1"
31
+ spec.add_development_dependency "rspec", "~> 2.14"
32
+ spec.add_development_dependency "rb-fsevent", "~> 0.9"
33
+ spec.add_development_dependency "rb-inotify", "~> 0.9"
34
+ end
@@ -0,0 +1,145 @@
1
+ require 'getoptlong'
2
+ require 'pidfile'
3
+
4
+ module ExtremeFeedbackDevice
5
+ class CLI
6
+ DEFAULT_MODE = 'run'
7
+ DEFAULT_NAMESPACE = 'default'
8
+ DEFAULT_PID = '~/.extreme_feedback_device.pid'
9
+ DEFAULT_SETTINGS = '~/.extreme_feedback_device.yml'
10
+
11
+ class << self
12
+ def instance
13
+ @instance ||= new
14
+ end
15
+ end
16
+
17
+ attr_reader :opts, :pid_file
18
+
19
+ def initialize
20
+ @opts = ::GetoptLong.new(
21
+ [ '--help', '-h', ::GetoptLong::NO_ARGUMENT ],
22
+ [ '--mode', '-m', ::GetoptLong::REQUIRED_ARGUMENT ],
23
+ [ '--namespace', '-n', ::GetoptLong::REQUIRED_ARGUMENT ],
24
+ [ '--pid', '-p', ::GetoptLong::REQUIRED_ARGUMENT ],
25
+ [ '--settings', '-s', ::GetoptLong::REQUIRED_ARGUMENT ],
26
+ [ '--version', '-v', ::GetoptLong::NO_ARGUMENT ]
27
+ )
28
+ end
29
+
30
+ def mode
31
+ @mode || DEFAULT_MODE
32
+ end
33
+
34
+ def namespace
35
+ @namespace || DEFAULT_NAMESPACE
36
+ end
37
+
38
+ def pid
39
+ @pid || DEFAULT_PID # File.expand_path(@pid || DEFAULT_PID)
40
+ end
41
+
42
+ def settings
43
+ @settings || DEFAULT_SETTINGS # File.expand_path(@settings || DEFAULT_SETTINGS)
44
+ end
45
+
46
+ def main
47
+ parse_opts!
48
+
49
+ if argv.length > 0
50
+ puts_help
51
+ exit -1
52
+ else
53
+ @pid_file = PidFile.new(piddir: File.expand_path(pid_dir), pidfile: pid_name)
54
+
55
+ ExtremeFeedbackDevice::Settings.source(File.expand_path(settings))
56
+ ExtremeFeedbackDevice::Settings.namespace(namespace)
57
+ ExtremeFeedbackDevice::Settings.reload!
58
+
59
+ if mode =~ /^run$/i
60
+ ExtremeFeedbackDevice.run
61
+ exit 0
62
+ elsif mode =~ /^loop$/i
63
+ ExtremeFeedbackDevice.infiniti_loop
64
+ exit 0
65
+ else
66
+ puts_help
67
+ exit -1
68
+ end
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def argv
75
+ ARGV
76
+ end
77
+
78
+ def pid_dir
79
+ File.dirname(pid)
80
+ end
81
+
82
+ def pid_name
83
+ File.basename(pid)
84
+ end
85
+
86
+ def parse_opts!
87
+ begin
88
+ opts.each do |opt, arg|
89
+ case opt
90
+ when '--help'
91
+ puts_help
92
+ exit 0
93
+ when '--mode'
94
+ @mode = arg
95
+ when '--namespace'
96
+ @namespace = arg
97
+ when '--pid'
98
+ @pid = arg
99
+ when '--settings'
100
+ @settings = arg
101
+ when '--version'
102
+ puts_version
103
+ exit 0
104
+ end
105
+ end
106
+ rescue ::GetoptLong::Error => error
107
+ puts # linebreak
108
+ puts_help
109
+ exit -1
110
+ end
111
+ end
112
+
113
+ def puts_help
114
+ puts <<-EOS
115
+ extreme_feedback_device [OPTION] ...
116
+
117
+ -h, --help:
118
+ \tshow help.
119
+
120
+ -m, --mode [#{DEFAULT_MODE}]:
121
+ \tset mode 'run' for a single run or 'loop' for infiniti loop.
122
+
123
+ -n, --namespace [#{DEFAULT_NAMESPACE}]:
124
+ \tsettings namespace.
125
+
126
+ -p, --pid [#{DEFAULT_PID}]:
127
+ \tpid file.
128
+
129
+ -s, --settings [#{DEFAULT_SETTINGS}]:
130
+ \tsettings file.
131
+
132
+ -v, --version:
133
+ \tshow version.
134
+
135
+ EOS
136
+ end
137
+
138
+ def puts_version
139
+ puts <<-EOS
140
+ extreme_feedback_device v#{ExtremeFeedbackDevice::VERSION}
141
+
142
+ EOS
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+ require 'net/http'
3
+
4
+ require "extreme_feedback_device/job"
5
+
6
+ module ExtremeFeedbackDevice
7
+ class Jenkins < ::Struct.new(:user, :token, :base_url)
8
+ def full_url
9
+ ::File.join(base_url, 'api', 'json?tree=jobs[name,color,healthReport[score]]')
10
+ end
11
+
12
+ def full_uri
13
+ URI(full_url)
14
+ end
15
+
16
+ def get_json
17
+ request = ::Net::HTTP::Get.new(full_uri)
18
+ request.basic_auth(user, token)
19
+
20
+ response = ::Net::HTTP.start(full_uri.hostname, full_uri.port) do |http|
21
+ http.request(request)
22
+ end
23
+
24
+ if response.is_a?(::Net::HTTPSuccess)
25
+ response.body
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ def jobs
32
+ jobs_from_json(get_json)
33
+ end
34
+
35
+ private
36
+
37
+ def jobs_from_json(json)
38
+ json_objects = []
39
+ begin
40
+ json_objects = ::JSON.parse(json)
41
+ rescue ::JSON::JSONError
42
+ end
43
+
44
+ json_objects["jobs"].map { |json_object| ExtremeFeedbackDevice::Job.from_json_object(json_object) }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,52 @@
1
+ module ExtremeFeedbackDevice
2
+ class Job < ::Struct.new(:name, :color, :health)
3
+ # http://buildsystem.bonn.taktsoft.com/api/schema
4
+ COLOR_RED = /\Ared/
5
+ COLOR_YELLOW = /\Ayellow/
6
+ COLOR_BLUE = /\Ablue/
7
+ COLOR_GREY = /\Agrey/
8
+ COLOR_DISABLED = /\Adisabled/
9
+ COLOR_ABORTED = /\Aaborted/
10
+ COLOR_NOTBUILT = /\Anotbuilt/
11
+
12
+ class << self
13
+ def from_json_object(attributes)
14
+ health_report = attributes['healthReport']
15
+ attributes['score'] = health_report.first.nil? ? nil : health_report.first['score']
16
+ new(attributes['name'], attributes['color'], attributes['score'])
17
+ end
18
+ end
19
+
20
+ def to_hash
21
+ {name: name, color: color, health: health}
22
+ end
23
+
24
+ def fail?
25
+ color && color =~ COLOR_RED
26
+ end
27
+
28
+ def unstable?
29
+ color && color =~ COLOR_YELLOW
30
+ end
31
+
32
+ def success?
33
+ color && color =~ COLOR_BLUE
34
+ end
35
+
36
+ def inactive?
37
+ color && color =~ COLOR_GREY
38
+ end
39
+
40
+ def disabled?
41
+ color && color =~ COLOR_DISABLED
42
+ end
43
+
44
+ def aborted?
45
+ color && color =~ COLOR_ABORTED
46
+ end
47
+
48
+ def not_built?
49
+ color && color =~ COLOR_NOTBUILT
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,45 @@
1
+ require 'color'
2
+
3
+ require "extreme_feedback_device/spi"
4
+
5
+ module ExtremeFeedbackDevice
6
+ class Pi
7
+ attr_reader :leds
8
+
9
+ def initialize(num_leds, spi_device=nil)
10
+ @leds = []
11
+ num_leds.times { @leds << ::Color::RGB.new }
12
+ @spi_device = spi_device
13
+ end
14
+
15
+ def leds_white
16
+ leds.map! { |led| ::Color::RGB::White }
17
+ end
18
+
19
+ def leds_white!
20
+ leds_white && write!
21
+ end
22
+
23
+ def leds_black
24
+ leds.map! { |led| ::Color::RGB::Black }
25
+ end
26
+
27
+ def leds_black!
28
+ leds_black && write!
29
+ end
30
+
31
+ def write!
32
+ spi.write_colors(colors)
33
+ end
34
+
35
+ private
36
+
37
+ def spi
38
+ @spi ||= ExtremeFeedbackDevice::SPI.new(@spi_device)
39
+ end
40
+
41
+ def colors
42
+ leds.map { |led| [led.red, led.green, led.blue] }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,8 @@
1
+ require 'settingslogic'
2
+
3
+ module ExtremeFeedbackDevice
4
+ class Settings < ::Settingslogic
5
+ source(::File.join(::Dir.home, '.extreme_feedback_device.yml'))
6
+ namespace('default')
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ module ExtremeFeedbackDevice
2
+ class SPI
3
+ DEFAULT_DEVICE = '/dev/spidev0.0'
4
+
5
+ attr_reader :device
6
+
7
+ def initialize(device=nil)
8
+ @device = device || DEFAULT_DEVICE
9
+ end
10
+
11
+ def write(data)
12
+ ::File.open(device, 'wb') do |spi|
13
+ spi.write(data)
14
+ spi.flush
15
+ end
16
+ end
17
+
18
+ def write_colors(colors)
19
+ write(colors_to_data(colors))
20
+ end
21
+
22
+ private
23
+
24
+ def colors_to_data(colors)
25
+ colors.flatten.pack('CCC' * colors.length)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module ExtremeFeedbackDevice
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,77 @@
1
+ require 'logger'
2
+ require 'rubygems'
3
+
4
+ require "extreme_feedback_device/version"
5
+
6
+ module ExtremeFeedbackDevice
7
+ class << self
8
+ def logger
9
+ if settings['log']
10
+ @logger ||= ::Logger.new(settings.log.file, settings.log['keep'], settings.log['size'])
11
+ else
12
+ @logger ||= ::Logger.new(STDOUT)
13
+ end
14
+ end
15
+
16
+ def settings
17
+ @settings ||= ExtremeFeedbackDevice::Settings
18
+ end
19
+
20
+ def jenkins
21
+ @jenkins ||= ExtremeFeedbackDevice::Jenkins.new(settings.jenkins.user, settings.jenkins.token, settings.jenkins.url)
22
+ end
23
+
24
+ def pi
25
+ @pi ||= ExtremeFeedbackDevice::Pi.new(settings.pi.num_leds, settings.spi['device'])
26
+ end
27
+
28
+ def run
29
+ jobs = jenkins.jobs
30
+ num_leds = settings.pi.num_leds
31
+ map_leds = settings.pi.map_leds
32
+
33
+ num_leds.times do |led_idx|
34
+ job_name = map_leds["i_#{led_idx}"]
35
+ if job_name
36
+ job = jobs.find { |job| job.name == job_name }
37
+ if job
38
+ if job.fail?
39
+ pi.leds[led_idx] = ::Color::RGB::Red
40
+ elsif job.unstable?
41
+ pi.leds[led_idx] = ::Color::RGB::Yellow
42
+ elsif job.success?
43
+ pi.leds[led_idx] = ::Color::RGB::Green
44
+ elsif job.inactive?
45
+ pi.leds[led_idx] = ::Color::RGB::Grey
46
+ elsif job.disabled?
47
+ pi.leds[led_idx] = ::Color::RGB::Grey
48
+ else # any other state
49
+ pi.leds[led_idx] = ::Color::RGB::Black
50
+ end
51
+ else # no job with this name in jenkins
52
+ pi.leds[led_idx] = ::Color::RGB::Black
53
+ end
54
+ else # no job associated with this led_idx
55
+ pi.leds[led_idx] = ::Color::RGB::Black
56
+ end
57
+ end
58
+ pi.write!
59
+ end
60
+
61
+ def infiniti_loop
62
+ settings['infiniti_loop'] ||= {}
63
+ interval = settings.infiniti_loop['sleep'] || 30
64
+ while true
65
+ run
66
+ sleep(interval)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ require "extreme_feedback_device/cli"
73
+ require "extreme_feedback_device/jenkins"
74
+ require "extreme_feedback_device/job"
75
+ require "extreme_feedback_device/pi"
76
+ require "extreme_feedback_device/settings"
77
+ require "extreme_feedback_device/spi"
@@ -0,0 +1,17 @@
1
+ default: &default
2
+ jenkins:
3
+ user: api
4
+ token: 0123456789ABCDEF
5
+ url: http://jenkins.example.com/
6
+ infiniti_loop:
7
+ sleep: 5
8
+ pi:
9
+ num_leds: 2
10
+ map_leds:
11
+ i_1: Jenkins - Master
12
+ i_0: Jenkins - Production
13
+ spi:
14
+ device: /dev/spidev0.0
15
+
16
+ test:
17
+ <<: *default
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'extreme_feedback_device'
3
+
4
+ describe ExtremeFeedbackDevice::CLI do
5
+ context "#instance" do
6
+ it "returns an instance of CLI" do
7
+ ExtremeFeedbackDevice::CLI.instance.should be_a ExtremeFeedbackDevice::CLI
8
+ end
9
+ end
10
+
11
+ context ".main" do
12
+ before(:each) do
13
+ ExtremeFeedbackDevice.stub(:run)
14
+ ExtremeFeedbackDevice.stub(:infiniti_loop)
15
+ PidFile.stub(:new)
16
+
17
+ subject.stub(:argv).and_return([])
18
+ subject.stub(:parse_opts!)
19
+ subject.stub(:puts_help)
20
+ subject.stub(:puts_version)
21
+ subject.stub(:exit)
22
+ end
23
+
24
+ it "calls parse_opts!" do
25
+ subject.should_receive(:parse_opts!)
26
+ subject.main
27
+ end
28
+
29
+ it "exits with null by default" do
30
+ subject.should_receive(:exit).with(0)
31
+ subject.main
32
+ end
33
+
34
+ it "calls ExtremeFeedbackDevice.run by default" do
35
+ ExtremeFeedbackDevice.should_receive(:run)
36
+ ExtremeFeedbackDevice.should_not_receive(:infiniti_loop)
37
+ subject.main
38
+ end
39
+ end
40
+
41
+ context ".mode" do
42
+ it "returns default value if not set" do
43
+ subject.mode.should eql ExtremeFeedbackDevice::CLI::DEFAULT_MODE
44
+ end
45
+ end
46
+
47
+ context ".namespace" do
48
+ it "returns default value if not set" do
49
+ subject.namespace.should eql ExtremeFeedbackDevice::CLI::DEFAULT_NAMESPACE
50
+ end
51
+ end
52
+
53
+ context ".pid" do
54
+ it "returns default value if not set" do
55
+ subject.pid.should eql ExtremeFeedbackDevice::CLI::DEFAULT_PID
56
+ end
57
+ end
58
+
59
+ context ".settings" do
60
+ it "returns default value if not set" do
61
+ subject.settings.should eql ExtremeFeedbackDevice::CLI::DEFAULT_SETTINGS
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'extreme_feedback_device/jenkins'
3
+
4
+ describe ExtremeFeedbackDevice::Jenkins do
5
+ context ".user" do
6
+ it "responds to user" do
7
+ subject.should be_respond_to "user"
8
+ end
9
+
10
+ it "responds to user=" do
11
+ subject.should be_respond_to "user="
12
+ end
13
+ end
14
+
15
+ context ".token" do
16
+ it "responds to token" do
17
+ subject.should be_respond_to "token"
18
+ end
19
+
20
+ it "responds to token=" do
21
+ subject.should be_respond_to "token="
22
+ end
23
+ end
24
+
25
+ context ".base_url" do
26
+ it "responds to base_url" do
27
+ subject.should be_respond_to "base_url"
28
+ end
29
+
30
+ it "responds to base_url=" do
31
+ subject.should be_respond_to "base_url="
32
+ end
33
+ end
34
+
35
+ context ".full_url" do
36
+ it "joins parts correctly" do
37
+ subject.base_url = "http://localhost/"
38
+ subject.full_url.should eql "http://localhost/api/json?tree=jobs[name,color,healthReport[score]]"
39
+ end
40
+ end
41
+
42
+ context ".full_uri" do
43
+ it "returns an URI" do
44
+ subject.base_url = "http://localhost/"
45
+ subject.full_uri.should be_a URI
46
+ end
47
+ end
48
+
49
+ context ".jobs" do
50
+ context "with mocked JSON" do
51
+ let(:response) { '''{"jobs":[
52
+ {"name":"Project X - Master","color":"blue","healthReport":[{"score":80}]},
53
+ {"name":"Project X - Production","color":"blue","healthReport":[{"score":60}]},
54
+ {"name":"Project Y - Master","color":"blue","healthReport":[{"score":100}]},
55
+ {"name":"Project Y - Production","color":"blue","healthReport":[{"score":40}]}
56
+ ]}''' }
57
+
58
+ before(:each) do
59
+ subject.stub(:get_json).and_return(response)
60
+ end
61
+
62
+ it "returns exactly four jobs" do
63
+ subject.jobs.should have(4).items
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'extreme_feedback_device/job'
3
+
4
+ describe ExtremeFeedbackDevice::Job do
5
+ context ".name" do
6
+ it "responds to name" do
7
+ subject.should be_respond_to "name"
8
+ end
9
+
10
+ it "responds to name=" do
11
+ subject.should be_respond_to "name="
12
+ end
13
+ end
14
+
15
+ context ".color" do
16
+ it "responds to color" do
17
+ subject.should be_respond_to "color"
18
+ end
19
+
20
+ it "responds to color=" do
21
+ subject.should be_respond_to "color="
22
+ end
23
+ end
24
+
25
+ context ".fail?" do
26
+ it "returns correct answer" do
27
+ subject.color = 'red'
28
+ subject.should be_fail
29
+ subject.color = 'red_anime'
30
+ subject.should be_fail
31
+ end
32
+ end
33
+
34
+ context ".unstable?" do
35
+ it "returns correct answer" do
36
+ subject.color = 'yellow'
37
+ subject.should be_unstable
38
+ subject.color = 'yellow_anime'
39
+ subject.should be_unstable
40
+ end
41
+ end
42
+ context ".success?" do
43
+ it "returns correct answer" do
44
+ subject.color = 'blue'
45
+ subject.should be_success
46
+ subject.color = 'blue_anime'
47
+ subject.should be_success
48
+ end
49
+ end
50
+
51
+ context ".disabled?" do
52
+ it "returns correct answer" do
53
+ subject.color = 'disabled'
54
+ subject.should be_disabled
55
+ subject.color = 'disabled_anime'
56
+ subject.should be_disabled
57
+ end
58
+ end
59
+
60
+ context ".health" do
61
+ it "responds to health" do
62
+ subject.should be_respond_to "health"
63
+ end
64
+
65
+ it "responds to health=" do
66
+ subject.should be_respond_to "health="
67
+ end
68
+ end
69
+
70
+ context "#from_json_object" do
71
+ let(:klass) { ExtremeFeedbackDevice::Job }
72
+ let(:json_object) { {"name"=>"Project X - Master", "color"=>"blue", "healthReport"=>[{"score"=>80}]} }
73
+
74
+ it "sets attributes correctly" do
75
+ subject = klass.from_json_object(json_object)
76
+ subject.should be_a klass
77
+ subject.name.should eql "Project X - Master"
78
+ subject.color.should eql "blue"
79
+ subject.health.should eql 80
80
+ end
81
+
82
+ it "doesn't raise error if healthReport is empty" do
83
+ json_object["healthReport"].clear
84
+ subject = klass.from_json_object(json_object)
85
+ subject.should be_a klass
86
+ subject.name.should eql "Project X - Master"
87
+ subject.color.should eql "blue"
88
+ subject.health.should eql nil
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'extreme_feedback_device'
3
+
4
+ describe ExtremeFeedbackDevice do
5
+ context "#logger" do
6
+ it "returns a Logger" do
7
+ ExtremeFeedbackDevice.logger.should be_a ::Logger
8
+ end
9
+ end
10
+
11
+ context "#settings" do
12
+ it "returns a ExtremeFeedbackDevice::Settings" do
13
+ ExtremeFeedbackDevice.settings.should be_a ::Class
14
+ end
15
+ end
16
+
17
+ context "#jenkins" do
18
+ it "returns a ExtremeFeedbackDevice::Jenkins" do
19
+ ExtremeFeedbackDevice.jenkins.should be_a ExtremeFeedbackDevice::Jenkins
20
+ end
21
+ end
22
+
23
+ context "#pi" do
24
+ it "returns a ExtremeFeedbackDevice::Pi" do
25
+ ExtremeFeedbackDevice.pi.should be_a ExtremeFeedbackDevice::Pi
26
+ end
27
+ end
28
+
29
+ context "#run" do
30
+ end
31
+
32
+ context "#loop" do
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ require 'pry'
2
+
3
+ ENV["EFD_YML"] ||= File.expand_path('../extreme_feedback_device.yml', __FILE__)
4
+ ENV["EFD_NS"] ||= 'test'
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # Require this file using `require "spec_helper"` to ensure that it is only
9
+ # loaded once.
10
+ #
11
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
12
+ RSpec.configure do |config|
13
+ config.treat_symbols_as_metadata_keys_with_true_values = true
14
+ config.run_all_when_everything_filtered = true
15
+ config.filter_run :focus
16
+
17
+ # Run specs in random order to surface order dependencies. If you find an
18
+ # order dependency and want to debug it, you can fix the order by providing
19
+ # the seed, which is printed after each run.
20
+ # --seed 1234
21
+ config.order = 'random'
22
+ end
@@ -0,0 +1,109 @@
1
+ #!/bin/sh
2
+ ### BEGIN INIT INFO
3
+ # Provides: extreme_feedback_device
4
+ # Required-Start: $local_fs $remote_fs $network $syslog
5
+ # Required-Stop: $local_fs $remote_fs $network $syslog
6
+ # Default-Start: 2 3 4 5
7
+ # Default-Stop: 0 1 6
8
+ # Short-Description: Extreme Feedback Device -- Control
9
+ ### END INIT INFO
10
+
11
+ DESC="Extreme Feedback Device -- Control"
12
+ NAME=extreme_feedback_device
13
+ SCRIPTNAME=/etc/init.d/$NAME
14
+ DAEMON=extreme_feedback_device_control
15
+ USER=foo
16
+ VERBOSE=yes
17
+
18
+ # Define LSB log_* functions.
19
+ # Depend on lsb-base (>= 3.0-6) to ensure that this file is present
20
+ . /lib/lsb/init-functions
21
+
22
+ #
23
+ # Function that starts the daemon/service
24
+ #
25
+ do_start ()
26
+ {
27
+ su - ${USER} -c "${DAEMON} start -- --mode loop" | grep "running \[pid" >/dev/null
28
+
29
+ [ "$?" -gt 0 ] && return 0
30
+ return 1
31
+ }
32
+
33
+ #
34
+ # Function that stops the daemon/service
35
+ #
36
+ do_stop()
37
+ {
38
+ su - ${USER} -c "${DAEMON} stop" | grep "successfully stopped" >/dev/null
39
+
40
+ [ "$?" -gt 0 ] && return 1
41
+ return 0
42
+ }
43
+
44
+ #
45
+ # Function that asks status of the daemon/service
46
+ #
47
+ do_status()
48
+ {
49
+ su - ${USER} -c "${DAEMON} status" | grep "running \[pid" >/dev/null
50
+
51
+ [ "$?" -gt 0 ] && return 3
52
+ return 0
53
+ }
54
+
55
+ case "$1" in
56
+ start)
57
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
58
+ do_status
59
+ case "$?" in
60
+ 3)
61
+ do_start
62
+ ENDVAL="$?"
63
+ ;;
64
+ 0)
65
+ ENDVAL="0"
66
+ ;;
67
+ *)
68
+ ENDVAL="1"
69
+ ;;
70
+ esac
71
+ [ "$VERBOSE" != no ] && log_end_msg $ENDVAL
72
+ ;;
73
+ stop)
74
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
75
+ do_status
76
+ case "$?" in
77
+ 3)
78
+ ENDVAL="0"
79
+ ;;
80
+ 0)
81
+ do_stop
82
+ ENDVAL="$?"
83
+ ;;
84
+ *)
85
+ ENDVAL="1"
86
+ ;;
87
+ esac
88
+ [ "$VERBOSE" != no ] && log_end_msg $ENDVAL
89
+ ;;
90
+ status)
91
+ [ "$VERBOSE" != no ] && log_daemon_msg "Status $DESC" "$NAME"
92
+ do_status
93
+ [ "$VERBOSE" != no ] && log_end_msg $?
94
+ ;;
95
+ restart)
96
+ [ "$VERBOSE" != no ] && log_daemon_msg "Restarting $DESC" "$NAME"
97
+ do_stop
98
+ case "$?" in
99
+ 0)
100
+ do_start
101
+ ;;
102
+ esac
103
+ [ "$VERBOSE" != no ] && log_end_msg $?
104
+ ;;
105
+ *)
106
+ echo "Usage: $SCRIPTNAME {start|stop|restart|status}" >&2
107
+ exit 3
108
+ ;;
109
+ esac
Binary file
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: extreme_feedback_device
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Nowak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: color
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: daemons
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: settingslogic
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pidfile
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '2.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '2.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '10.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '10.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: '2.14'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: '2.14'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rb-fsevent
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ~>
144
+ - !ruby/object:Gem::Version
145
+ version: '0.9'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ version: '0.9'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rb-inotify
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ~>
158
+ - !ruby/object:Gem::Version
159
+ version: '0.9'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ~>
165
+ - !ruby/object:Gem::Version
166
+ version: '0.9'
167
+ description: Fetches Jobs from a Jenkins CI via the REST JSON API and sets LEDs of
168
+ an Extreme Feedback Device to the status of the corresponding Job. Build and designed
169
+ for running on a Raspberry Pi with Debian 7.
170
+ email:
171
+ - nowak@taktsoft.com
172
+ executables:
173
+ - extreme_feedback_device
174
+ - extreme_feedback_device_control
175
+ extensions: []
176
+ extra_rdoc_files: []
177
+ files:
178
+ - .gitignore
179
+ - .rspec
180
+ - .ruby-gemset
181
+ - .ruby-version
182
+ - Gemfile
183
+ - Guardfile
184
+ - LICENSE.txt
185
+ - README.md
186
+ - Rakefile
187
+ - bin/extreme_feedback_device
188
+ - bin/extreme_feedback_device_control
189
+ - extreme_feedback_device.gemspec
190
+ - lib/extreme_feedback_device.rb
191
+ - lib/extreme_feedback_device/cli.rb
192
+ - lib/extreme_feedback_device/jenkins.rb
193
+ - lib/extreme_feedback_device/job.rb
194
+ - lib/extreme_feedback_device/pi.rb
195
+ - lib/extreme_feedback_device/settings.rb
196
+ - lib/extreme_feedback_device/spi.rb
197
+ - lib/extreme_feedback_device/version.rb
198
+ - spec/extreme_feedback_device.yml
199
+ - spec/lib/extreme_feedback_device/cli_spec.rb
200
+ - spec/lib/extreme_feedback_device/jenkins_spec.rb
201
+ - spec/lib/extreme_feedback_device/job_spec.rb
202
+ - spec/lib/extreme_feedback_device_spec.rb
203
+ - spec/spec_helper.rb
204
+ - vendor/init.d/extreme_feedback_device
205
+ - vendor/misc/application_cards.odt
206
+ homepage: https://github.com/taktsoft/extreme_feedback_device
207
+ licenses:
208
+ - MIT
209
+ metadata: {}
210
+ post_install_message:
211
+ rdoc_options: []
212
+ require_paths:
213
+ - lib
214
+ required_ruby_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - '>='
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ required_rubygems_version: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - '>='
222
+ - !ruby/object:Gem::Version
223
+ version: '0'
224
+ requirements: []
225
+ rubyforge_project:
226
+ rubygems_version: 2.0.3
227
+ signing_key:
228
+ specification_version: 4
229
+ summary: Fetches Jobs from a Jenkins CI via the REST JSON API and sets LEDs of an
230
+ Extreme Feedback Device to the status of the corresponding Job.
231
+ test_files:
232
+ - spec/extreme_feedback_device.yml
233
+ - spec/lib/extreme_feedback_device/cli_spec.rb
234
+ - spec/lib/extreme_feedback_device/jenkins_spec.rb
235
+ - spec/lib/extreme_feedback_device/job_spec.rb
236
+ - spec/lib/extreme_feedback_device_spec.rb
237
+ - spec/spec_helper.rb