snapgit 0.6.4

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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.editorconfig +17 -0
  4. data/.gitattributes +1 -0
  5. data/.gitignore +14 -0
  6. data/.rubocop.yml +10 -0
  7. data/.rubocop_todo.yml +57 -0
  8. data/.travis.yml +42 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE +165 -0
  11. data/README.md +28 -0
  12. data/Rakefile +79 -0
  13. data/bin/lolcommits +335 -0
  14. data/bin/snapgit +14 -0
  15. data/config/cucumber.yml +2 -0
  16. data/features/bugs.feature +63 -0
  17. data/features/lolcommits.feature +286 -0
  18. data/features/plugins.feature +20 -0
  19. data/features/step_definitions/lolcommits_steps.rb +142 -0
  20. data/features/support/env.rb +61 -0
  21. data/features/support/path_helpers.rb +39 -0
  22. data/lib/core_ext/class.rb +8 -0
  23. data/lib/core_ext/mini_magick/utilities.rb +15 -0
  24. data/lib/lolcommits.rb +34 -0
  25. data/lib/lolcommits/capturer.rb +24 -0
  26. data/lib/lolcommits/capturer/capture_cygwin.rb +22 -0
  27. data/lib/lolcommits/capturer/capture_fake.rb +9 -0
  28. data/lib/lolcommits/capturer/capture_linux.rb +45 -0
  29. data/lib/lolcommits/capturer/capture_linux_animated.rb +72 -0
  30. data/lib/lolcommits/capturer/capture_mac.rb +23 -0
  31. data/lib/lolcommits/capturer/capture_mac_animated.rb +73 -0
  32. data/lib/lolcommits/capturer/capture_windows.rb +22 -0
  33. data/lib/lolcommits/cli/fatals.rb +77 -0
  34. data/lib/lolcommits/cli/launcher.rb +29 -0
  35. data/lib/lolcommits/cli/process_runner.rb +48 -0
  36. data/lib/lolcommits/cli/timelapse_gif.rb +45 -0
  37. data/lib/lolcommits/configuration.rb +138 -0
  38. data/lib/lolcommits/git_info.rb +82 -0
  39. data/lib/lolcommits/init.rb +121 -0
  40. data/lib/lolcommits/installation.rb +128 -0
  41. data/lib/lolcommits/platform.rb +127 -0
  42. data/lib/lolcommits/plugin.rb +130 -0
  43. data/lib/lolcommits/plugins/dot_com.rb +50 -0
  44. data/lib/lolcommits/plugins/lol_protonet.rb +68 -0
  45. data/lib/lolcommits/plugins/lol_slack.rb +68 -0
  46. data/lib/lolcommits/plugins/lol_tumblr.rb +127 -0
  47. data/lib/lolcommits/plugins/lol_twitter.rb +155 -0
  48. data/lib/lolcommits/plugins/lol_yammer.rb +85 -0
  49. data/lib/lolcommits/plugins/lolsrv.rb +59 -0
  50. data/lib/lolcommits/plugins/loltext.rb +147 -0
  51. data/lib/lolcommits/plugins/snapgit.rb +167 -0
  52. data/lib/lolcommits/plugins/tranzlate.rb +115 -0
  53. data/lib/lolcommits/plugins/uploldz.rb +65 -0
  54. data/lib/lolcommits/runner.rb +134 -0
  55. data/lib/lolcommits/version.rb +5 -0
  56. data/snapgit.gemspec +63 -0
  57. data/test/images/test_image.jpg +0 -0
  58. data/test/lolcommits_test.rb +35 -0
  59. data/test/plugins_test.rb +52 -0
  60. data/vendor/ext/CommandCam/COPYING +674 -0
  61. data/vendor/ext/CommandCam/CommandCam.exe +0 -0
  62. data/vendor/ext/CommandCam/LICENSE +16 -0
  63. data/vendor/ext/imagesnap/ReadMeOrDont.rtf +117 -0
  64. data/vendor/ext/imagesnap/imagesnap +0 -0
  65. data/vendor/ext/videosnap/videosnap +0 -0
  66. data/vendor/fonts/Lato.ttf +0 -0
  67. data/vendor/logos/Snapgit.png +0 -0
  68. metadata +509 -0
@@ -0,0 +1,77 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'lolcommits/platform'
3
+ require 'methadone'
4
+
5
+ module Lolcommits
6
+ module CLI
7
+ # Helper methods for failing on error conditions in the lolcommits CLI.
8
+ module Fatals
9
+ include Lolcommits
10
+ include Methadone::CLILogging
11
+
12
+ # Check for platform related conditions that could prevent lolcommits from
13
+ # working properly.
14
+ #
15
+ # Die with an informative error message if any occur.
16
+ def self.die_on_fatal_platform_conditions!
17
+ # make sure the capture binaries are in a good state
18
+ if Platform.platform_mac?
19
+ %w(imagesnap videosnap).each do |executable|
20
+ next if File.executable? File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'ext', executable, executable)
21
+ fatal "Couldn't properly execute #{executable} for some reason, "\
22
+ 'please file a bug?!'
23
+ exit 1
24
+ end
25
+ elsif Platform.platform_linux?
26
+ unless Platform.command_which('mplayer')
27
+ fatal "Couldn't find mplayer in your PATH!"
28
+ exit 1
29
+ end
30
+ end
31
+
32
+ # make sure we can find the default font
33
+ unless File.readable? Lolcommits::Loltext::DEFAULT_FONT_PATH
34
+ fatal "Couldn't properly read Impact font from gem package, "\
35
+ 'please file a bug?!'
36
+ exit 1
37
+ end
38
+
39
+ # make sure imagemagick is around and good to go
40
+ unless Platform.valid_imagemagick_installed?
41
+ fatal 'FATAL: ImageMagick does not appear to be properly installed!'\
42
+ '(or version is too old)'
43
+ exit 1
44
+ end
45
+
46
+ # check for a error condition with git config affecting ruby-git
47
+ if Platform.git_config_color_always?
48
+ fatal 'Due to a bug in the ruby-git library, git config for color.ui'\
49
+ " cannot be set to 'always'."
50
+ fatal "Try setting it to 'auto' instead!"
51
+ exit 1
52
+ end
53
+ end
54
+
55
+ # Die with an informative error message if ffmpeg is not installed.
56
+ # This is only used for certain functions (such as animation), so only run
57
+ # this when you know the user wants to perform one of them.
58
+ def self.die_if_no_valid_ffmpeg_installed!
59
+ unless Platform.valid_ffmpeg_installed?
60
+ fatal 'FATAL: ffmpeg does not appear to be properly installed!'
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ # If we are not in a git repo, we can't do git related things!
66
+ # Die with an informative error message in that case.
67
+ def self.die_if_not_git_repo!
68
+ debug 'Checking for valid git repo'
69
+ Git.open('.') # FIXME: should be extracted to GitInfo class
70
+ rescue ArgumentError
71
+ # ruby-git throws an argument error if path isnt for a valid git repo.
72
+ fatal "Erm? Can't do that since we're not in a valid git repository!"
73
+ exit 1
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'launchy'
3
+
4
+ module Lolcommits
5
+ module CLI
6
+ # Helper class for wrapping the opening of files on the desktop in a
7
+ # cross-platform way.
8
+ #
9
+ # Right now this is mostly just a wrapper for Launchy, in case we want
10
+ # to factor out it's dependency later or swap it out.
11
+ class Launcher
12
+ def self.open_image(path)
13
+ open_with_launchy(path)
14
+ end
15
+
16
+ def self.open_folder(path)
17
+ open_with_launchy(path)
18
+ end
19
+
20
+ # Opens with Launchy, which knows how to open pretty much anything
21
+ # local files, urls, etc.
22
+ #
23
+ # Private so we replace it later easier if we want.
24
+ def self.open_with_launchy(thing)
25
+ Launchy.open(thing)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Lolcommits
4
+ module CLI
5
+ # Helper class for forking lolcommits process to the background (or not).
6
+ class ProcessRunner
7
+ # Initializes a new process runner.
8
+ #
9
+ # @param config [Lolcommits::Configuration]
10
+ def initialize(config)
11
+ @configuration = config
12
+ end
13
+
14
+ # Forks the lolcommits process if requested.
15
+ #
16
+ # Writes the PID of the lolcommits process to the filesystem when
17
+ # backgrounded, for monitoring purposes.
18
+ #
19
+ # @param please [Boolean] whether or not to fork lolcommits process
20
+ # @yield the main event loop for lolcommits
21
+ def fork_me?(please, &block)
22
+ if please
23
+ $stdout.sync = true
24
+ write_pid fork {
25
+ yield block
26
+ delete_pid
27
+ }
28
+ else
29
+ yield block
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def write_pid(pid)
36
+ File.open(pid_file, 'w') { |f| f.write(pid) }
37
+ end
38
+
39
+ def delete_pid
40
+ File.delete(pid_file) if File.exist?(pid_file)
41
+ end
42
+
43
+ def pid_file
44
+ File.join(@configuration.loldir, 'lolcommits.pid')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,45 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'lolcommits/cli/fatals'
3
+
4
+ require 'mini_magick'
5
+
6
+ module Lolcommits
7
+ module CLI
8
+ # Creates an animated timeline GIF of lolcommits history.
9
+ class TimelapseGif
10
+ # param config [Lolcommits::Configuration]
11
+ def initialize(config)
12
+ @configuration = config
13
+ end
14
+
15
+ # Runs the history timeline animator task thingy
16
+ # param args [String] the arg passed to the gif command on CLI (optional)
17
+ def run(args = nil)
18
+ Fatals.die_if_not_git_repo!
19
+
20
+ case args
21
+ when 'today'
22
+ lolimages = @configuration.jpg_images_today
23
+ filename = "#{Date.today}.gif"
24
+ else
25
+ lolimages = @configuration.jpg_images
26
+ filename = 'archive.gif'
27
+ end
28
+
29
+ if lolimages.empty?
30
+ warn 'No lolcommits have been captured for this time yet.'
31
+ exit 1
32
+ end
33
+
34
+ puts '*** Generating animated gif.'
35
+
36
+ gif = MiniMagick::Image.new File.join @configuration.archivedir, filename
37
+
38
+ # This is for ruby 1.8.7, *lolimages just doesn't work with ruby 187
39
+ gif.run_command('convert', *['-delay', '50', '-loop', '0', lolimages, gif.path.to_s].flatten)
40
+
41
+ puts "*** #{gif.path} generated."
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,138 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Lolcommits
4
+ class Configuration
5
+ LOLCOMMITS_BASE = ENV['LOLCOMMITS_DIR'] || File.join(ENV['HOME'], '.lolcommits')
6
+ LOLCOMMITS_ROOT = File.join(File.dirname(__FILE__), '../..')
7
+ attr_writer :loldir
8
+
9
+ def initialize(attributes = {})
10
+ attributes.each do |attr, val|
11
+ send("#{attr}=", val)
12
+ end
13
+ end
14
+
15
+ def read_configuration
16
+ return unless File.exist?(configuration_file)
17
+ YAML.load(File.open(configuration_file))
18
+ end
19
+
20
+ def configuration_file
21
+ "#{loldir}/config.yml"
22
+ end
23
+
24
+ def loldir
25
+ return @loldir if @loldir
26
+ basename ||= File.basename(Git.open('.').dir.to_s).sub(/^\./, 'dot')
27
+ basename.sub!(/ /, '-')
28
+ @loldir = Configuration.loldir_for(basename)
29
+ end
30
+
31
+ def archivedir
32
+ dir = File.join(loldir, 'archive')
33
+ FileUtils.mkdir_p dir unless File.directory? dir
34
+ dir
35
+ end
36
+
37
+ def most_recent
38
+ Dir.glob(File.join(loldir, '*.{jpg,gif}')).max_by { |f| File.mtime(f) }
39
+ end
40
+
41
+ def jpg_images
42
+ Dir.glob(File.join(loldir, '*.jpg')).sort_by { |f| File.mtime(f) }
43
+ end
44
+
45
+ def jpg_images_today
46
+ jpg_images.select { |f| Date.parse(File.mtime(f).to_s) == Date.today }
47
+ end
48
+
49
+ def raw_image(image_file_type = 'jpg')
50
+ File.join loldir, "tmp_snapshot.#{image_file_type}"
51
+ end
52
+
53
+ def main_image(commit_sha, image_file_type = 'jpg')
54
+ File.join loldir, "#{commit_sha}.#{image_file_type}"
55
+ end
56
+
57
+ def video_loc
58
+ File.join(loldir, 'tmp_video.mov')
59
+ end
60
+
61
+ def frames_loc
62
+ File.join(loldir, 'tmp_frames')
63
+ end
64
+
65
+ def puts_plugins
66
+ puts "Available plugins: #{Lolcommits::Runner.plugins.map(&:name).join(', ')}"
67
+ end
68
+
69
+ def ask_for_plugin_name
70
+ puts_plugins
71
+ print 'Name of plugin to configure: '
72
+ STDIN.gets.strip
73
+ end
74
+
75
+ def find_plugin(plugin_name)
76
+ Lolcommits::Runner.plugins.each do |plugin|
77
+ return plugin.new(nil) if plugin.name == plugin_name
78
+ end
79
+
80
+ puts "Unable to find plugin: '#{plugin_name}'"
81
+ puts_plugins
82
+ end
83
+
84
+ # Modified by Felix to return useful values
85
+ def do_configure!(plugin_name)
86
+ plugin_name = ask_for_plugin_name if plugin_name.to_s.strip.empty?
87
+
88
+ plugin = find_plugin(plugin_name)
89
+ return unless plugin
90
+ config = read_configuration || {}
91
+ plugin_config = plugin.configure_options!
92
+ # having a plugin_config, means configuring went OK
93
+ if plugin_config
94
+ # save plugin and print config
95
+ config[plugin_name] = plugin_config
96
+ save(config)
97
+ puts self
98
+ puts "\nSuccessfully configured plugin: #{plugin_name} at path '#{configuration_file}'"
99
+ return configuration_file
100
+ else
101
+ puts "\nAborted plugin configuration for: #{plugin_name}"
102
+ return false
103
+ end
104
+ end
105
+
106
+ def save(config)
107
+ config_file_contents = config.to_yaml
108
+ File.open(configuration_file, 'w') do |f|
109
+ f.write(config_file_contents)
110
+ end
111
+ end
112
+
113
+ def to_s
114
+ read_configuration.to_yaml.to_s
115
+ end
116
+
117
+ # class methods
118
+
119
+ def self.loldir_for(basename)
120
+ loldir = File.join(LOLCOMMITS_BASE, basename)
121
+
122
+ if File.directory? loldir
123
+ begin
124
+ # ensure 755 permissions for loldir
125
+ File.chmod(0755, loldir)
126
+ rescue Errno::EPERM
127
+ # abort if permissions cannot be met
128
+ puts "FATAL: directory '#{loldir}' should be present and writeable by user '#{ENV['USER']}'"
129
+ puts 'Try changing the directory permissions to 755'
130
+ exit 1
131
+ end
132
+ else
133
+ FileUtils.mkdir_p loldir
134
+ end
135
+ loldir
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,82 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Lolcommits
3
+ class GitInfo
4
+ include Methadone::CLILogging
5
+ attr_accessor :sha, :message, :repo_internal_path, :repo, :url,
6
+ :author_name, :author_email, :branch
7
+
8
+ GIT_URL_REGEX = %r{.*[:]([\/\w\-]*).git}
9
+
10
+ def initialize
11
+ debug 'GitInfo: parsed the following values from commit:'
12
+ debug "GitInfo: \t#{message}"
13
+ debug "GitInfo: \t#{sha}"
14
+ debug "GitInfo: \t#{repo_internal_path}"
15
+ debug "GitInfo: \t#{repo}"
16
+ debug "GitInfo: \t#{branch}"
17
+ debug "GitInfo: \t#{author_name}" if author_name
18
+ debug "GitInfo: \t#{author_email}" if author_email
19
+ end
20
+
21
+ def branch
22
+ @branch ||= repository.current_branch
23
+ end
24
+
25
+ def message
26
+ @message ||= begin
27
+ message = last_commit.message || ''
28
+ message.split("\n").first
29
+ end
30
+ end
31
+
32
+ def sha
33
+ @sha ||= last_commit.sha[0..10]
34
+ end
35
+
36
+ def repo_internal_path
37
+ @repo_internal_path ||= repository.repo.path
38
+ end
39
+
40
+ def url
41
+ @url ||= begin
42
+ remote_https_url(repository.remote.url) if repository.remote
43
+ end
44
+ end
45
+
46
+ def repo
47
+ @repo ||= begin
48
+ if repository.remote && repository.remote.url
49
+ match = repository.remote.url.match(GIT_URL_REGEX)
50
+ end
51
+
52
+ if match
53
+ match[1]
54
+ elsif !repository.repo.path.empty?
55
+ repository.repo.path.split(File::SEPARATOR)[-2]
56
+ end
57
+ end
58
+ end
59
+
60
+ def author_name
61
+ @author_name ||= last_commit.author.name if last_commit.author
62
+ end
63
+
64
+ def author_email
65
+ @author_email ||= last_commit.author.email if last_commit.author
66
+ end
67
+
68
+ private
69
+
70
+ def remote_https_url(url)
71
+ url.tr(':', '/').tr(/^git@/, 'https://').tr(/\.git$/, '') + '/commit/'
72
+ end
73
+
74
+ def repository(path = '.')
75
+ @repository ||= Git.open(path)
76
+ end
77
+
78
+ def last_commit
79
+ @commit ||= repository.log.first
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,121 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Lolcommits
3
+ class Init
4
+ require 'lolcommits'
5
+ require 'fileutils'
6
+ require 'tmpdir'
7
+
8
+ # rubocop:disable Metrics/MethodLength
9
+ def self.run_setup(lolcommits_binary)
10
+ require 'io/console'
11
+ @lolcommits_binary = lolcommits_binary
12
+ set_lolcommits_env_config
13
+
14
+ puts 'This setup will run through the necessary steps to get you up and running'
15
+ puts 'Please follow the wizard to authenticate Twitter and Gravatar'
16
+ puts "If you don't want to use Gravatar, just don't provide any values"
17
+ puts 'Confirm with Enter'
18
+ STDIN.getch
19
+
20
+ Dir.mktmpdir do |_tmp_dir|
21
+ `git init` # just to make lolcommits believe we're in a git folder
22
+ result = request_auth_tokens
23
+
24
+ if result == false
25
+ puts 'Setup failed - please try again'
26
+ abort
27
+ end
28
+ @config_path = result
29
+ end
30
+
31
+ puts '-'
32
+ puts 'Successfully generated keys... now setting up your git projects:'
33
+ puts '-'
34
+
35
+ puts 'Do you want snapgit to automatically enable itself for all local git repositories? (y/n)'
36
+ if STDIN.getch.strip == 'y'
37
+ enable_for_all_projects
38
+ elsif File.directory?('.git')
39
+ puts 'Do you want to enable snapgit just for the local directory? (y/n)'
40
+ if STDIN.getch.strip == 'y'
41
+ enable_for_local_folder
42
+ return
43
+ end
44
+ else
45
+ puts '-'
46
+ puts 'Please navigate to the project you want to enable snapgit for'
47
+ puts 'and run `snapgit init`'
48
+ abort
49
+ end
50
+ end
51
+ # rubocop:enable Metrics/MethodLength
52
+
53
+ def self.set_lolcommits_env_config
54
+ ENV['LOLCOMMITS_INIT_PARAMS'] = ' --delay 1' # this is required to actually work on a Mac
55
+ ENV['LOLCOMMITS_INIT_PARAMS'] += ' --stealth' # we don't want any output
56
+ ENV['LOLCOMMITS_INIT_PARAMS'] += ' &' # this way the delay is not noticable
57
+ end
58
+
59
+ # @return (success or not)
60
+ def self.request_auth_tokens
61
+ $stdout.sync = true
62
+ Configuration.new.do_configure!('snapgit')
63
+ end
64
+
65
+ def self.enable_for_local_folder
66
+ enable_for_project('.')
67
+ puts "Successfully enabled snapgit 🎉"
68
+ end
69
+
70
+ def self.enable_for_all_projects
71
+ projs = git_projects
72
+ puts projs
73
+ puts 'Do you want to enable snapgit for all those repos? (y/n)'
74
+ abort unless STDIN.getch.strip == 'y'
75
+
76
+ projs.each do |current|
77
+ enable_for_project(current)
78
+ end
79
+ puts "Successfully enabled snapgit for #{projs.count} projects 🎉"
80
+ end
81
+
82
+ def self.enable_for_project(path)
83
+ puts "Enabling snapgit for '#{File.expand_path(path)}'..."
84
+ Dir.chdir(path) do
85
+ # Add the `lolcommits --capture` to the post-hook
86
+ Lolcommits::Installation.do_enable
87
+
88
+ # Copy the config.yml to the ~/.lolcommits/[project] folder
89
+ to_path = Lolcommits::Configuration.new.loldir # this will use the current dir by default
90
+ # rubocop:disable Lint/HandleExceptions
91
+ begin
92
+ FileUtils.cp(@config_path, to_path)
93
+ rescue ArgumentError # if the file is the same
94
+ end
95
+ # rubocop:enable Lint/HandleExceptions
96
+ end
97
+ end
98
+
99
+ def self.git_projects
100
+ puts 'Searching for git repos'
101
+ # We're using README.md assuming that every repo has one
102
+ # This is due to Spotlight not finding hidden files (.git)
103
+ potential = `mdfind -name "README.md" -onlyin ~`.split("\n")
104
+
105
+ # After we have all README.md we look for a .git folder in
106
+ # each of those
107
+ potential.collect do |current|
108
+ path = File.expand_path('..', current)
109
+ path if File.directory?(File.join(path, '.git'))
110
+ end.delete_if(&:nil?)
111
+ end
112
+ end
113
+ end
114
+
115
+ # Cheap monkey patching to not get any output when enabling lolcommits
116
+ module Lolcommits
117
+ class Installation
118
+ def self.info(_str)
119
+ end
120
+ end
121
+ end