acmcommits 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.
@@ -0,0 +1,94 @@
1
+ require 'aruba/cucumber'
2
+ require 'methadone/cucumber'
3
+ require 'open3'
4
+ require 'test/unit/assertions'
5
+ include Test::Unit::Assertions
6
+ require 'faker'
7
+ require 'lolcommits/configuration'
8
+ include Lolcommits
9
+
10
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
11
+ LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
12
+
13
+ Before do
14
+ # Using "announce" causes massive warnings on 1.9.2
15
+ @puts = true
16
+ @aruba_timeout_seconds = 20
17
+
18
+ @original_rubylib = ENV['RUBYLIB']
19
+ ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
20
+
21
+ @original_fakecapture = ENV['LOLCOMMITS_FAKECAPTURE']
22
+ ENV['LOLCOMMITS_FAKECAPTURE'] = "1"
23
+
24
+ # @original_loldir = ENV['LOLCOMMITS_DIR']
25
+ # ENV['LOLCOMMITS_DIR'] = File.expand_path( File.join(current_dir, ".lolcommits") )
26
+
27
+ @original_home = ENV['HOME']
28
+ ENV['HOME'] = File.expand_path( current_dir )
29
+
30
+ ENV['LAUNCHY_DRY_RUN'] = 'true'
31
+ end
32
+
33
+ After do
34
+ ENV['RUBYLIB'] = @original_rubylib
35
+ ENV['LOLCOMMITS_FAKECAPTURE'] = @original_fakecapture
36
+ # ENV['LOLCOMMITS_DIR'] = @original_loldir
37
+ ENV['HOME'] = @original_home
38
+ ENV['LAUNCHY_DRY_RUN'] = nil
39
+ end
40
+
41
+ Before('@fake-interactive-rebase') do
42
+ # in order to fake an interactive rebase,
43
+ # we replace the editor with a script that simply squashes a few random commits
44
+ @original_git_editor = ENV['GIT_EDITOR']
45
+ # ENV['GIT_EDITOR'] = "sed -i -e 'n;s/pick/squash/g'" #every other commit
46
+ ENV['GIT_EDITOR'] = "sed -i -e '3,5 s/pick/squash/g'" #lines 3-5
47
+ end
48
+
49
+ After('@fake-interactive-rebase') do
50
+ ENV['GIT_EDITOR'] = @original_git_editor
51
+ end
52
+
53
+ Before('@slow_process') do
54
+ @aruba_io_wait_seconds = 5
55
+ @aruba_timeout_seconds = 60
56
+ end
57
+
58
+ # adjust the path so tests dont see a global imagemagick install
59
+ Before('@fake-no-imagemagick') do
60
+
61
+ # make a new subdir that still contains git and mplayer
62
+ tmpbindir = File.expand_path(File.join @dirs, "bin")
63
+ FileUtils.mkdir_p tmpbindir
64
+ ["git","mplayer"].each do |cmd|
65
+ whichcmd = Lolcommits::Configuration.command_which(cmd)
66
+ unless whichcmd.nil?
67
+ FileUtils.ln_s whichcmd, File.join(tmpbindir, File.basename(whichcmd))
68
+ end
69
+ end
70
+
71
+ # use a modified version of Configuration::command_which to detect where IM is installed
72
+ # and remove that from the path
73
+ cmd = 'mogrify'
74
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
75
+ newpaths = ENV['PATH'].split(File::PATH_SEPARATOR).reject do |path|
76
+ found_cmd = false
77
+ exts.each { |ext|
78
+ exe = "#{path}/#{cmd}#{ext}"
79
+ found_cmd = true if File.executable? exe
80
+ }
81
+ found_cmd
82
+ end
83
+
84
+ # add the temporary directory with git in it back into the path
85
+ newpaths << tmpbindir
86
+
87
+ @original_path = ENV['PATH']
88
+ ENV['PATH'] = newpaths.join(File::PATH_SEPARATOR)
89
+ puts ENV['PATH']
90
+ end
91
+
92
+ After('@fake-no-imagemagick') do
93
+ ENV['PATH'] = @original_path
94
+ end
@@ -0,0 +1,7 @@
1
+ class Class
2
+ def subclasses
3
+ result = []
4
+ ObjectSpace.each_object(Class) { |klass| result << klass if klass < self }
5
+ result
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Lolcommits
2
+ class CaptureFake < Capturer
3
+ def capture
4
+ test_image = File.join Configuration::LOLCOMMITS_ROOT, "test", "images", "test_image.jpg"
5
+ FileUtils.cp test_image, snapshot_location
6
+ end
7
+
8
+ end
9
+ end
10
+
@@ -0,0 +1,28 @@
1
+ module Lolcommits
2
+ class CaptureLinux < Capturer
3
+ def capture
4
+ debug "LinuxCapturer: making tmp directory"
5
+ tmpdir = Dir.mktmpdir
6
+
7
+ # There's no way to give a capture delay in mplayer, but a number of frame
8
+ # I've found that 6 is a good value for me.
9
+ frames = if capture_delay != 0 then capture_delay else 6 end
10
+
11
+ debug "LinuxCapturer: calling out to mplayer to capture image"
12
+ # mplayer's output is ugly and useless, let's throw it away
13
+ _, r, _ = Open3.popen3("mplayer -vo jpeg:outdir=#{tmpdir} -frames #{frames} tv://")
14
+ # looks like we still need to read the output for something to happen
15
+ r.read
16
+
17
+ # the below SHOULD tell FileUtils actions to post their output if we are in debug mode
18
+ include FileUtils::Verbose if logger.level == 0
19
+
20
+ debug "LinuxCapturer: calling out to mplayer to capture image"
21
+ FileUtils.mv(tmpdir + "/%08d.jpg" % frames, snapshot_location)
22
+ debug "LinuxCapturer: cleaning up"
23
+ FileUtils.rm_rf( tmpdir )
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,19 @@
1
+ module Lolcommits
2
+ class CaptureMac < Capturer
3
+ def capture_device_string
4
+ @capture_device.nil? ? nil : "-d \"#{@capture_device}\""
5
+ end
6
+
7
+ def capture
8
+ call_str = "#{imagesnap_bin} -q \"#{snapshot_location}\" -w #{capture_delay} #{capture_device_string}"
9
+ debug "Capturer: making system call for #{call_str}"
10
+ system(call_str)
11
+ end
12
+
13
+ private
14
+
15
+ def imagesnap_bin
16
+ File.join(Configuration::LOLCOMMITS_ROOT, "vendor", "ext", "imagesnap", "imagesnap")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Lolcommits
2
+ class CaptureWindows < Capturer
3
+ def capture
4
+ commandcam_exe = File.join Configuration::LOLCOMMITS_ROOT, "vendor", "ext", "CommandCam", "CommandCam.exe"
5
+ # DirectShow takes a while to show... at least for me anyway
6
+ delaycmd = " /delay 3000"
7
+ if capture_delay > 0
8
+ # CommandCam delay is in milliseconds
9
+ delaycmd = " /delay #{capture_delay * 1000}"
10
+ end
11
+ _, r, _ = Open3.popen3("#{commandcam_exe} /filename #{snapshot_location}#{delaycmd}")
12
+ # looks like we still need to read the output for something to happen
13
+ r.read
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,13 @@
1
+ module Lolcommits
2
+ class Capturer
3
+ include Methadone::CLILogging
4
+ attr_accessor :capture_device, :capture_delay, :snapshot_location, :font
5
+
6
+ def initialize(attributes = Hash.new)
7
+ attributes.each do |attr, val|
8
+ self.send("#{attr}=", val)
9
+ end
10
+ debug "Capturer: initializing new instance " + self.to_s
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,160 @@
1
+ module Lolcommits
2
+ class Configuration
3
+ LOLBASEDIR = ENV['LOLCOMMITS_DIR'] || File.join(ENV['HOME'], '.lolcommits')
4
+ LOLCOMMITS_ROOT = File.join(File.dirname(__FILE__), '../..')
5
+ attr_writer :loldir
6
+
7
+ def initialize(attributes={})
8
+ attributes.each do |attr, val|
9
+ self.send("#{attr}=", val)
10
+ end
11
+ end
12
+
13
+ def self.platform
14
+ if is_fakecapture?
15
+ 'Fake'
16
+ elsif is_mac?
17
+ 'Mac'
18
+ elsif is_linux?
19
+ 'Linux'
20
+ elsif is_windows?
21
+ 'Windows'
22
+ else
23
+ raise "Unknown / Unsupported Platform."
24
+ end
25
+ end
26
+
27
+ def user_configuration
28
+ if File.exists?(user_configuration_file)
29
+ YAML.load(File.open(user_configuration_file))
30
+ else
31
+ nil
32
+ end
33
+ end
34
+
35
+ def user_configuration_file
36
+ "#{self.loldir}/config.yml"
37
+ end
38
+
39
+ def loldir
40
+ return @loldir if @loldir
41
+
42
+ basename ||= File.basename(Git.open('.').dir.to_s).sub(/^\./, 'dot')
43
+ basename.sub!(/ /, '-')
44
+
45
+ @loldir = Configuration.loldir_for(basename)
46
+ end
47
+
48
+ def self.loldir_for(basename)
49
+ loldir = File.join(LOLBASEDIR, basename)
50
+
51
+ if not File.directory? loldir
52
+ FileUtils.mkdir_p loldir
53
+ end
54
+ loldir
55
+ end
56
+
57
+ def most_recent
58
+ Dir.glob(File.join self.loldir, "*").max_by {|f| File.mtime(f)}
59
+ end
60
+
61
+ def raw_image
62
+ File.join self.loldir, "tmp_snapshot.jpg"
63
+ end
64
+
65
+ def main_image(commit_sha)
66
+ File.join self.loldir, "#{commit_sha}.jpg"
67
+ end
68
+
69
+ def puts_plugins
70
+ names = Lolcommits::PLUGINS.collect {|p| p.new(nil).name }
71
+ puts "Available plugins: #{names.join(', ')}"
72
+ end
73
+
74
+ def do_configure!(plugin, forced_options=nil)
75
+ if plugin.nil? || plugin.strip == ''
76
+ puts_plugins
77
+ print "Name of plugin to configure: "
78
+ plugin = STDIN.gets.strip
79
+ end
80
+
81
+ plugins = Lolcommits::PLUGINS.inject(Hash.new) do |acc, val|
82
+ p = val.new(nil)
83
+ acc.merge(p.name => p)
84
+ end
85
+
86
+ plugin_object = plugins[plugin]
87
+
88
+ if plugin_object.nil?
89
+ puts "Unable to find plugin: #{plugin}"
90
+ return
91
+ end
92
+
93
+ if forced_options.nil?
94
+ options = plugin_object.options.inject(Hash.new) do |acc, option|
95
+ print "#{option}: "
96
+ val = STDIN.gets.strip
97
+ val = true if val == 'true'
98
+ val = false if val == 'false'
99
+
100
+ acc.merge(option => val)
101
+ end
102
+ else
103
+ options = forced_options
104
+ end
105
+
106
+ config = self.user_configuration || Hash.new
107
+ config[plugin] = options
108
+ File.open(self.user_configuration_file, 'w') do |f|
109
+ f.write(config.to_yaml)
110
+ end
111
+
112
+ puts "#{config.to_yaml}\n"
113
+ puts "Successfully Configured"
114
+ end
115
+
116
+ def self.is_mac?
117
+ RUBY_PLATFORM.to_s.downcase.include?("darwin")
118
+ end
119
+
120
+ def self.is_linux?
121
+ RUBY_PLATFORM.to_s.downcase.include?("linux")
122
+ end
123
+
124
+ def self.is_windows?
125
+ !! RUBY_PLATFORM.match(/(win|w)32/)
126
+ end
127
+
128
+ def self.is_fakecapture?
129
+ (ENV['LOLCOMMITS_FAKECAPTURE'] == '1' || false)
130
+ end
131
+
132
+ def self.valid_imagemagick_installed?
133
+ return false unless self.command_which('identify')
134
+ return false unless self.command_which('mogrify')
135
+ # you'd expect the below to work on its own, but it only handles old versions
136
+ # and will throw an exception if IM is not installed in PATH
137
+ MiniMagick::valid_version_installed?
138
+ end
139
+
140
+ def self.git_config_color_always?
141
+ `git config color.ui`.chomp =~ /always/
142
+ end
143
+
144
+ # Cross-platform way of finding an executable in the $PATH.
145
+ # idea taken from http://bit.ly/qDaTbY
146
+ #
147
+ # which('ruby') #=> /usr/bin/ruby
148
+ def self.command_which(cmd)
149
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
150
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
151
+ exts.each { |ext|
152
+ exe = "#{path}/#{cmd}#{ext}"
153
+ return exe if File.executable? exe
154
+ }
155
+ end
156
+ return nil
157
+ end
158
+
159
+ end
160
+ end
@@ -0,0 +1,27 @@
1
+ module Lolcommits
2
+ class GitInfo
3
+ include Methadone::CLILogging
4
+ attr_accessor :sha, :message, :repo_internal_path, :repo
5
+
6
+ def initialize
7
+ debug "GitInfo: attempting to read local repository"
8
+ g = Git.open('.')
9
+ debug "GitInfo: reading commits logs"
10
+ commit = g.log.first
11
+ debug "GitInfo: most recent commit is '#{commit}'"
12
+
13
+ self.message = commit.message.split("\n").first
14
+ self.sha = commit.sha[0..10]
15
+ self.repo_internal_path = g.repo.path
16
+ regex = /.*[:\/](\w*).git/
17
+ match = g.remote.url.match regex if g.remote.url
18
+ self.repo = match[1] if match
19
+
20
+ debug "GitInfo: parsed the following values from commit:"
21
+ debug "GitInfo: \t#{self.message}"
22
+ debug "GitInfo: \t#{self.sha}"
23
+ debug "GitInfo: \t#{self.repo_internal_path}"
24
+ debug "GitInfo: \t#{self.repo}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module Lolcommits
2
+ class Plugin
3
+ include Methadone::CLILogging
4
+ attr_accessor :default, :name, :runner, :options
5
+
6
+ def configuration
7
+ config = runner.config.user_configuration
8
+ return Hash.new if config.nil?
9
+ config[self.name] || Hash.new
10
+ end
11
+
12
+ def initialize(runner)
13
+ self.runner = runner
14
+ self.options = ['enabled']
15
+
16
+ plugdebug "Initializing"
17
+ end
18
+
19
+ def is_enabled?
20
+ enabled_config = configuration['enabled']
21
+ return self.default if enabled_config.nil? || enabled_config == ''
22
+ return enabled_config
23
+ end
24
+
25
+
26
+ def execute
27
+ if is_enabled?
28
+ plugdebug "I am enabled, about to run"
29
+ run
30
+ else
31
+ plugdebug "Disabled, doing nothing for execution"
32
+ end
33
+ end
34
+
35
+ # uniform debug logging output for plugins
36
+ def plugdebug(msg)
37
+ debug("Plugin: #{self.class.to_s}: " + msg)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,73 @@
1
+ require "rest_client"
2
+ require "pp"
3
+ require "json"
4
+ require "logger"
5
+
6
+ module Lolcommits
7
+
8
+ class Lolsrv < Plugin
9
+
10
+ SERVER = 'server'
11
+
12
+ def initialize(runner)
13
+ super
14
+ self.name = 'lolsrv'
15
+ self.default = false
16
+ self.options << SERVER
17
+
18
+ end
19
+
20
+ def run
21
+
22
+ log_file = File.new(self.runner.config.loldir + "/lolsrv.log", "a+")
23
+ @logger = Logger.new(log_file)
24
+
25
+ if configuration[SERVER].nil?
26
+ puts "Missing server configuration. Use lolcommits --config -p lolsrv"
27
+ return
28
+ end
29
+
30
+ fork do
31
+ sync()
32
+ end
33
+ end
34
+
35
+ def sync
36
+ existing = get_existing_lols
37
+ unless existing.nil?
38
+ Dir.glob(self.runner.config.loldir + "/*.jpg") do |item|
39
+ next if item == '.' or item == '..'
40
+ # do work on real items
41
+ sha = File.basename(item, '.*')
42
+ unless existing.include?(sha) || sha == 'tmp_snapshot'
43
+ upload(item, sha)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def get_existing_lols
50
+ begin
51
+ lols = JSON.parse(
52
+ RestClient.get(configuration[SERVER] + '/commits'))
53
+ lols.map { |lol| lol["sha"] }
54
+ rescue => error
55
+ @logger.info "Existing commits could not be retrieved with Error " + error.message
56
+ @logger.info error.backtrace
57
+ return nil
58
+ end
59
+ end
60
+
61
+ def upload(file, sha)
62
+ begin
63
+ RestClient.post(
64
+ configuration[SERVER] + '/upload',
65
+ :lol => File.new(file),
66
+ :sha => sha)
67
+ rescue => error
68
+ @logger.info "Upload of LOL "+ sha + " failed with Error " + error.message
69
+ return
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,79 @@
1
+ module Lolcommits
2
+ class Loltext < Plugin
3
+
4
+ def initialize(runner)
5
+ super
6
+
7
+ @font_location = runner ? runner.font : nil
8
+
9
+ self.name = 'loltext'
10
+ self.default = true
11
+ end
12
+
13
+ def run
14
+ mm_run
15
+ end
16
+
17
+ # use minimagick wrapper
18
+ def mm_run
19
+ font_location = @font_location || File.join(Configuration::LOLCOMMITS_ROOT, "vendor", "fonts", "Impact.ttf")
20
+
21
+ plugdebug "Annotating image via MiniMagick"
22
+ image = MiniMagick::Image.open(self.runner.main_image)
23
+ image.combine_options do |c|
24
+ c.gravity 'SouthWest'
25
+ c.fill 'white'
26
+ c.stroke 'black'
27
+ c.strokewidth '2'
28
+ c.pointsize '48'
29
+ c.interline_spacing '-9'
30
+ c.font font_location
31
+ c.annotate '0', clean_msg(self.runner.message)
32
+ end
33
+
34
+ image.combine_options do |c|
35
+ c.gravity 'NorthEast'
36
+ c.fill 'white'
37
+ c.stroke 'black'
38
+ c.strokewidth '2'
39
+ c.pointsize '32'
40
+ c.font font_location
41
+ c.annotate '0', self.runner.sha
42
+ end
43
+
44
+ plugdebug "Writing changed file to #{self.runner.main_image}"
45
+ image.write self.runner.main_image
46
+ end
47
+
48
+ private
49
+
50
+ # do whatever is required to commit message to get it clean and ready for imagemagick
51
+ def clean_msg(text)
52
+ wrapped_text = word_wrap text
53
+ escape_quotes wrapped_text
54
+ text = scrub_profanity text
55
+ end
56
+
57
+
58
+ #search the commit for profanity and if it exists remove the commit
59
+ def scrub_profanity(text)
60
+ profanity = ['fuck', 'bitch', 'cunt', 'hate', 'pissed', 'balls', 'screw', 'ACM',
61
+ 'ass']
62
+ text.each do |word|
63
+ text = ' ' if profanity.include? word
64
+ end
65
+ end
66
+ # conversion for quotation marks to avoid shell interpretation
67
+ # does not seem to be a safe way to escape cross-platform?
68
+ def escape_quotes(text)
69
+ text.gsub(/"/, "''")
70
+ end
71
+
72
+ # convenience method for word wrapping
73
+ # based on https://github.com/cmdrkeene/memegen/blob/master/lib/meme_generator.rb
74
+ def word_wrap(text, col = 27)
75
+ wrapped = text.gsub(/(.{1,#{col + 4}})(\s+|\Z)/, "\\1\n")
76
+ wrapped.chomp!
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,25 @@
1
+ require "statsd"
2
+
3
+ #
4
+ # This is just to keep some usage statistics on lolcommits.
5
+ #
6
+
7
+ module Lolcommits
8
+ class StatsD < Plugin
9
+ def initialize(runner)
10
+ super
11
+
12
+ self.name = 'statsd'
13
+ self.default = true
14
+ end
15
+
16
+ def run
17
+ $statsd = Statsd.new('23.20.178.143')
18
+ if Configuration.is_fakecapture?
19
+ $statsd.increment 'app.lolcommits.fakecaptures'
20
+ else
21
+ $statsd.increment 'app.lolcommits.captures'
22
+ end
23
+ end
24
+ end
25
+ end