extreme_feedback_device 0.0.1

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.
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