Mange-git-remote-monitor 0.1.1 → 0.1.2

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,26 @@
1
+ VERSION 0.1.2
2
+ =============
3
+ Major refactoring for upcoming beta. A few bug fixes, too.
4
+
5
+ * Added note type to Meow for more client customization.
6
+ * Refactored the notification system into a plug-in based system.
7
+ It will be much easier to test and extend this, and getting it to work with new notification systems on
8
+ other platforms is going to be a piece of cake from now on.
9
+ * New options for executable: --list-plugins, --version
10
+ * You can now specify a DIR for the monitor executable
11
+ * BUG: Human-readable error when trying to fetch in a repo without remotes
12
+ * BUG: Error out when entering a directory which is not part of a repository
13
+
14
+ VERSION 0.1.1
15
+ =============
16
+ Quick patch release to fix serious error in the gem creation.
17
+
18
+ * BUG: Only the executable was built into the gem
19
+ * BUG: The executable did not try to load the lib when running gem executable
20
+
21
+ VERSION 0.1.0
22
+ =============
23
+ * Basic support for notifications
24
+ * Basic fetch wrapper to git
25
+ * Basic monitor, waiting 20 seconds between each fetch
26
+ * Simple comparing remotes with local possible
data/Rakefile CHANGED
@@ -26,12 +26,15 @@ end
26
26
 
27
27
  Spec::Rake::SpecTask.new do |t|
28
28
  t.libs << 'lib'
29
+ t.spec_files = FileList['spec/**/*.rb']
29
30
  t.verbose = false
30
31
  t.rcov = false
31
32
  end
32
33
 
33
34
  Spec::Rake::SpecTask.new('rcov') do |t|
34
35
  t.libs << 'lib'
36
+ t.spec_files = FileList['spec/**/*.rb']
37
+ t.rcov_opts = ['--exclude', 'spec,rcov']
35
38
  t.verbose = false
36
39
  t.rcov = true
37
40
  end
@@ -44,4 +47,4 @@ Rake::RDocTask.new do |rdoc|
44
47
  rdoc.rdoc_files.include('lib/**/*.rb')
45
48
  end
46
49
 
47
- task :default => :test
50
+ task :default => :spec
data/TODO CHANGED
@@ -2,22 +2,23 @@ TODO
2
2
  ====
3
3
 
4
4
  Before beta:
5
- * More specs
6
- * Need to figure out how to simulate the stuff better; reduce inter-dependencies
7
- * Command line arguments
8
- * Specify a project name
9
- * Specify path
10
- * Specify fetch interval
5
+ * Command line arguments
6
+ * Specify a project name
11
7
  * Daemonize the program
12
8
  * Look at real Git libraries; might be better than calling the system
13
-
9
+
14
10
  Before 1.0:
15
11
  * Read/Save configuration with git-config(1)
16
12
  * Make it easy to quit the daemons (git-remote-monitor --kill-all, or similar)
17
-
13
+
18
14
  Before 1.1:
19
15
  * Extend to more notification systems than Meow (Growl on Mac OS X)
20
16
  * knotify
21
17
  * Whatever Gnome uses
22
18
  * Windows possible?
23
- * XMPP? Too crazy?
19
+ * Email?
20
+ * HTTP request to a URL?
21
+ * Run generic command?
22
+ * XMPP? Too crazy?
23
+ * Let notification plugins have configuration settings
24
+ * Project name/Notifier plugin structure. Have a wildcard project for default settings.
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 1
2
+ :patch: 2
3
3
  :major: 0
4
4
  :minor: 1
@@ -26,9 +26,9 @@
26
26
  #
27
27
 
28
28
  # Add ../lib/ as a load path in case we are doing development
29
- $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
29
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
30
30
 
31
- REQUIRED_GEMS = ['rubygems', 'meow']
31
+ REQUIRED_GEMS = ['rubygems']
32
32
  begin
33
33
  REQUIRED_GEMS.each { |g| require g }
34
34
  rescue LoadError => e
@@ -45,19 +45,27 @@ require 'rdoc/usage'
45
45
 
46
46
  $usage = %Q{
47
47
  = Synopsis
48
- Monitors the current branch's remote for changes at a fixed interval. Changes will be fetched and you will be
49
- notified about how many commits you are behind.
48
+ Monitors the HEAD's (current branch) remote for changes at a fixed interval. Changes will be fetched
49
+ and you will be notified about them in any notification system you specify.
50
50
 
51
51
  = Usage
52
- git remote-monitor [OPTIONS]
52
+ git remote-monitor [OPTIONS] [DIR]
53
+
54
+ When no DIR is specified, current working directory will be used.
53
55
 
54
56
  == Options
55
57
  -h, --help:
56
58
  displays this usage information
57
59
 
60
+ -V, --version:
61
+ displays version information
62
+
58
63
  -i TIME, --interval=TIME:
59
64
  time, in seconds, between each fetch. Defaults to 60 seconds.
60
65
 
66
+ -p, --list-plugins:
67
+ list all notification plugins and show which ones are active on your system, then quit.
68
+
61
69
  == Daemon
62
70
  Since this utility is in an early stage, no daemon capabilities have yet been added. It is recommended that
63
71
  you start the command in the background (append a single & to the end of the command line) so you can keep
@@ -71,7 +79,7 @@ using your term to other things while the program is running.
71
79
  # RubyGems will call this from a wrapper, and #usage is hardcoded to look
72
80
  # at the top-level file instead of the current one. I have changed this
73
81
  # code to instead just parse a string.
74
- def usage_and_exit!
82
+ def usage_information
75
83
  markup = SM::SimpleMarkup.new
76
84
  flow_convertor = SM::ToFlow.new
77
85
 
@@ -80,8 +88,33 @@ def usage_and_exit!
80
88
  options = RI::Options.instance
81
89
  formatter = options.formatter.new(options, "")
82
90
  formatter.display_flow(flow)
91
+ end
92
+
93
+ def version_information
94
+ require 'yaml'
95
+ version = YAML::load_file(File.join(GitRemoteMonitor.root, 'VERSION.yml'))
96
+ stability = (version[:minor] % 2 == 0) ? "stable" : "unstable"
97
+ puts "#{File.basename($0)} version #{version[:major]}.#{version[:minor]}.#{version[:patch]} (#{stability})"
98
+ puts "Run in ruby #{RUBY_VERSION} (#{RUBY_PLATFORM}) released #{RUBY_RELEASE_DATE}, patchlevel #{RUBY_PATCHLEVEL}"
99
+ end
100
+
101
+ def list_plugins
102
+ loaded = GitRemoteMonitor::Notifiers.notifier_plugins
103
+ available = GitRemoteMonitor::Notifiers.available_notifiers
104
+ preferred = GitRemoteMonitor::Notifiers.preferred_notifier
83
105
 
84
- exit(0)
106
+ puts "Plugins currently in this version of git-remote-monitor:"
107
+ loaded.each do |plugin|
108
+ status = if plugin == preferred
109
+ " (Available, Active)"
110
+ elsif available.include?(plugin)
111
+ " (Available)"
112
+ else
113
+ ""
114
+ end
115
+ plugin_name = plugin.to_s.sub(/^.*::/, '')
116
+ puts "\t* #{plugin_name}#{status}"
117
+ end
85
118
  end
86
119
 
87
120
  #
@@ -89,14 +122,23 @@ end
89
122
  #
90
123
  def parse_command_line_arguments!
91
124
  opts = GetoptLong.new(
92
- [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
93
- [ '--interval', '-i', GetoptLong::REQUIRED_ARGUMENT]
125
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
126
+ [ '--version', '-V', GetoptLong::NO_ARGUMENT ],
127
+ [ '--list-plugins', '-p', GetoptLong::NO_ARGUMENT ],
128
+ [ '--interval', '-i', GetoptLong::REQUIRED_ARGUMENT]
94
129
  )
95
130
 
96
131
  opts.each do |opt, arg|
97
132
  case opt
98
133
  when '--help'
99
- usage_and_exit!
134
+ usage_information
135
+ exit(0)
136
+ when '--version'
137
+ version_information
138
+ exit(0)
139
+ when '--list-plugins'
140
+ list_plugins
141
+ exit(0)
100
142
  when '--interval'
101
143
  interval = arg.to_i
102
144
  unless interval > 10
@@ -106,17 +148,25 @@ def parse_command_line_arguments!
106
148
  $interval = interval
107
149
  end
108
150
  end
151
+
152
+ # ARGV now contains all non-params. Check for only one extra param, max. This is the DIR.
153
+ if $ARGV.size > 1
154
+ $stderr.puts "Only one directory may be specified!"
155
+ Kernel.exit(1)
156
+ elsif $ARGV.size == 1
157
+ $dir = $ARGV.first
158
+ end
109
159
  end
110
160
 
111
161
  # Default values for the command line options
112
162
  $interval = 60
163
+ $dir = Dir.pwd
113
164
 
114
165
  begin
115
166
  parse_command_line_arguments!
116
167
 
117
168
  notifier = GitRemoteMonitor::Notifier.new
118
- # Hardcoded values for now
119
- monitor = GitRemoteMonitor::Monitor.new(Dir.pwd, $interval, notifier)
169
+ monitor = GitRemoteMonitor::Monitor.new($dir, $interval, notifier)
120
170
 
121
171
  monitor.start_monitoring!
122
172
  rescue => e
@@ -20,9 +20,13 @@
20
20
  # THE SOFTWARE.
21
21
 
22
22
  module GitRemoteMonitor
23
- PATH = File.join(File.dirname(__FILE__), '..')
23
+
24
+ def self.root
25
+ File.expand_path(File.join(File.dirname(__FILE__), '..'))
26
+ end
27
+
24
28
  end
25
29
 
26
- ['git_wrapper', 'monitor', 'notifier'].each do |file|
30
+ %w(git_wrapper notifiers monitor status notifier).each do |file|
27
31
  require File.join(File.dirname(__FILE__), 'git_remote_monitor', file)
28
32
  end
@@ -62,11 +62,31 @@ class GitRemoteMonitor::GitWrapper
62
62
  # The method will return true if a fetch was made or false if nothing was fetched.
63
63
  #
64
64
  def fetch!
65
+ raise "This repository have no remotes to fetch from!" unless has_remotes?
65
66
  # If the output was empty, no actual fetch was done
66
67
  return !call_fetch.strip.empty?
67
68
  end
68
69
 
69
- private
70
+ #
71
+ # Returns number of defined remotes or nil if no remotes are defined. Useful for
72
+ # if statements before doing fetches and whatnot.
73
+ #
74
+ # Works on current working dir, like most of the methods here.
75
+ #
76
+ def has_remotes?
77
+ remotes = call_remotes_list.strip.split("\n").size
78
+ return nil if remotes == 0
79
+ remotes
80
+ end
81
+
82
+ #
83
+ # Returns true if current working dir is inside a git repository. False otherwise.
84
+ #
85
+ def in_repo?
86
+ call_repo_discovery
87
+ end
88
+
89
+ protected
70
90
  # Do the actual system call for "git fetch", returning output
71
91
  def call_fetch
72
92
  output = `git fetch 2>&1`
@@ -97,5 +117,16 @@ class GitRemoteMonitor::GitWrapper
97
117
  def call_get_remote(local_name)
98
118
  `git config branch.#{local_name}.remote`
99
119
  end
120
+
121
+ def call_remotes_list
122
+ `git branch -r --no-color`
123
+ end
124
+
125
+ # Runs a simple plumbing command to quickly see if there is a repository in current working dir.
126
+ # Returns true or false depending on success in calling the command.
127
+ def call_repo_discovery
128
+ `git show-ref`
129
+ $?.success?
130
+ end
100
131
  end
101
132
  end
@@ -24,37 +24,47 @@
24
24
  # instance about the changes later on.
25
25
  #
26
26
  class GitRemoteMonitor::Monitor
27
+ attr_reader :name, :directory
28
+
27
29
  def initialize(directory, interval, notifier, name = nil)
28
30
  @directory = directory
29
31
  @name = name || File.basename(directory)
30
32
  @interval = interval.to_i
31
33
  @notifier = notifier
32
34
 
33
- move_to_directory!(@directory)
35
+ self.class.move_to_directory!(@directory)
34
36
  end
35
37
 
36
38
  def start_monitoring!
37
- while true
39
+ loop do
38
40
  notify! if GitRemoteMonitor::GitWrapper.fetch!
39
41
  sleep @interval
42
+ break unless should_loop_monitoring?
40
43
  end
41
44
  end
42
45
 
43
46
  protected
44
-
47
+
48
+ # This method is added only for easier testing. Might be usable in the future, though.
49
+ def should_loop_monitoring?
50
+ true
51
+ end
52
+
45
53
  def notify!
46
54
  # Find out the status
47
55
  local = GitRemoteMonitor::GitWrapper.local_branch
48
56
  remote = GitRemoteMonitor::GitWrapper.remote_branch
49
57
  my_commits, their_commits = GitRemoteMonitor::GitWrapper.get_tracking_info(local, remote)
50
- @notifier.notify_commits(@name, local, my_commits, their_commits)
51
- end
52
-
53
- def move_to_directory!(directory)
58
+ @notifier.notify_commits(self, local, my_commits, their_commits)
59
+ end
60
+
61
+ def self.move_to_directory!(directory)
54
62
  unless File.exist?(directory) and File.directory?(directory)
55
63
  raise "#{directory} not found or not a directory"
56
64
  end
57
65
  Dir.chdir(directory)
66
+ raise "#{File.expand_path(directory)} is not part of a git repository" unless GitRemoteMonitor::GitWrapper.in_repo?
58
67
  end
68
+
59
69
 
60
70
  end
@@ -19,42 +19,21 @@
19
19
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
20
  # THE SOFTWARE.
21
21
 
22
+ #
23
+ # In its current form, this class is not much more than simple glue between the monitor and the
24
+ # whole notification system. Its primary use right now is to separate the logic properly. It
25
+ # will hopefully be refactored quite a bit in the future.
26
+ #
22
27
  class GitRemoteMonitor::Notifier
23
28
 
24
- def initialize()
25
- # TODO: Auto-discover notification systems and load the one the user have
26
- # For now, just use meow. REMEMBER: Remove 'meow' from the bin/ script when
27
- # this requirement changes here!
28
-
29
- require 'meow'
30
- @notifier = ::Meow.new('git-remote-monitor')
29
+ def initialize
30
+ @notifier = GitRemoteMonitor::Notifiers.preferred_notifier.new
31
31
  end
32
32
 
33
- def notify_commits(name, branch_name, local_commits, remote_commits)
34
- message = get_message(local_commits, remote_commits)
35
- if message
36
- @notifier.notify("#{name} (#{branch_name})", message, :icon => "#{GitRemoteMonitor::PATH}/images/git-logo.png")
37
- end
33
+ # FIXME: This method should probably be refactored later on. We shouldn't need this many arguments.
34
+ def notify_commits(monitor, branch_name, local_commits, remote_commits)
35
+ status = GitRemoteMonitor::Status.new(monitor, branch_name, local_commits, remote_commits)
36
+ @notifier.send_notification!(status)
38
37
  end
39
38
 
40
- protected
41
- def get_message(local_commits, remote_commits)
42
- # If there aren't any commits on the remote, we just ignore
43
- # this difference. It's because we don't want to notify the user
44
- # that they are x commits before the remote.
45
- unless remote_commits == 0
46
-
47
- # Now, we can have this scenarios:
48
- # 1. The branch has not diverged; user has no commits, but remote has.
49
- # 2. The branch has diverged; both the user and the remote has commits
50
- message = if local_commits > 0
51
- "Diverged! Remote has #{remote_commits} new commits, and you have #{local_commits} commits to push."
52
- else
53
- "Remote got updated! Now #{remote_commits} commits in front of you."
54
- end
55
- return message
56
- end
57
- nil
58
- end
59
-
60
39
  end
@@ -0,0 +1,52 @@
1
+ # The MIT License
2
+ # Copyright © 2009 Magnus Bergmark
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+
22
+ #
23
+ # This class is a mediator between the monitor and the actual notifier plugins.
24
+ # It will give you information about which plugin the monitor should use and also
25
+ # have the capability to tell you about the available plugins and all the defined
26
+ # ones.
27
+ #
28
+ class GitRemoteMonitor::Notifiers
29
+ class << self
30
+
31
+ # Returns the first plugin that is available; does not load any unneccessary stuff
32
+ # from plugins that will not be used.
33
+ def preferred_notifier
34
+ @preferred_notifier ||= notifier_plugins.find { |plugin| plugin.available? }
35
+ end
36
+
37
+ # Returns an array of all plugins that are available for use; will load a lot of gems
38
+ # and libraries which you may not want to use.
39
+ def available_notifiers
40
+ @available_notifiers ||= notifier_plugins.find_all { |plugin| plugin.available? }
41
+ end
42
+
43
+ # Returns an array of all defined plugins as classes.
44
+ def notifier_plugins
45
+ [Meow]
46
+ end
47
+
48
+ end
49
+ end
50
+
51
+ require 'git_remote_monitor/notifiers/base'
52
+ require 'git_remote_monitor/notifiers/meow'