update_repo 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 54b552a96cddc68af766238152689994aafe1bb6
4
- data.tar.gz: 747b8dbf6ff74ca6c9269d0f6bcf8b29407a8069
3
+ metadata.gz: 6783e0c8c887cd196fea92efa147a7d258f9ad1a
4
+ data.tar.gz: 7f89ca19fc25897a196e4fe3bcc6a286a6f7c01c
5
5
  SHA512:
6
- metadata.gz: 2c58773f1dcb1f4c1116cfed2961cdebb20e23384b91c563113267ff70c969a76fbc0f5e5d31dbd78bccb37e9cb2df20d4c9ba4a74a3ebe9d703410ee1180a2f
7
- data.tar.gz: 440ec4e0c252ab6670a00816c501e0b1b51567870406babe2587b8d2365484ba3744c29ce286c7a9743b3455a3d4431f3e896d45052469be4ea622cd09d7c950
6
+ metadata.gz: dec5a5aa2174265b867e33721f0270055e9a39054e1bc164201c061ad8051415c5d1e4baf8a0ec9dfd4af0198f154fa8f758a1b9e6b35be737737419d6580803
7
+ data.tar.gz: ada430198bdf7ca638dab031ee2d138c35a5e1a3c16427c0af79ff6af1ce66c68cfdfeb3f5db6f970f779578e77fdf77148d41b46d82e4522a378d259a895142
data/.reek CHANGED
@@ -12,3 +12,6 @@ RepeatedConditional:
12
12
 
13
13
  DuplicateMethodCall:
14
14
  max_calls: 3
15
+
16
+ NestedIterators:
17
+ max_allowed_nesting: 2
data/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  A Simple Gem to keep multiple locally-cloned Git Repositories up to date.
10
10
 
11
- This is the conversion to a Gem of one of my standalone Ruby scripts. Still very much a work in progress but the required basic functionality is there.
11
+ This is the conversion to a Gem of one of my standalone Ruby scripts. Still a work in progress but the required base functionality is there.
12
12
  The script will simply run `git pull` on every local clone of a git repository that it finds under the specified directory or directories.
13
13
 
14
14
  __Note:__ From version 0.9.0 onwards, the default mode of operation is non-verbose. If you wish the same output as previous versions then specify `--verbose` on the command line or `verbose: true` in the configuration file.
@@ -17,7 +17,7 @@ __Note:__ From version 0.9.0 onwards, the default mode of operation is non-verbo
17
17
 
18
18
  #### Pre-requirements
19
19
 
20
- It goes without saying that at the very least a working copy of both [`Git`][git] and [`Ruby`][ruby] need to be installed on your machine. Also, the script has currently only been tested under Linux, not windows.
20
+ It goes without saying that at the very least a working copy of both [`Git`][git] (version 1.8.5 or greater, the script will not run with an older version) and [`Ruby`][ruby] need to be installed on your machine. Also, the script has currently only been tested under Linux, not windows.
21
21
 
22
22
  [git]: http://git-scm.com
23
23
  [ruby]: http://www.ruby-lang.org
@@ -38,7 +38,7 @@ location:
38
38
  - /media/myuser/git-repos
39
39
  - /data/RepoDir
40
40
  ```
41
- This is the most basic example of a configuration file and there are other options that can be added to fine-tune the operation - see the description of configuration options below.
41
+ This is the most basic example of a configuration file and there are other options that can be added to fine-tune the operation - see the description of configuration options below and the [Website](http://updaterepo.seapagan.net) for more information.
42
42
 
43
43
  This file should be located in the users home directory (`~/.updaterepo`).
44
44
 
@@ -48,6 +48,7 @@ $ update_repo
48
48
  ```
49
49
 
50
50
  ## Configuration
51
+ The below is a summary of the most common configuration options, see the [Website](http://updaterepo.seapagan.net) for complete details and usage.
51
52
  #### Configuration file
52
53
  The configuration file defaults to `~/.updaterepo` and is a standard [YAML](http://yaml.org/)-formatted text file. If this configuration file is not found, the script will terminate with an error.
53
54
  The first line must contain the YAML frontmatter of 3 dashes (`---`). After that, the following sections can follow in any order. Only the `location:` section is compulsory, and that must contain at least one entry.
@@ -77,8 +78,18 @@ log: true
77
78
  timestamp: true
78
79
  ```
79
80
 
81
+ `verbose:` - display the output of the git command for each repo, defaults to FALSE (optional)
82
+ ```yaml
83
+ verbose: true
84
+ ```
85
+
86
+ `quiet:` - no output at all, not even the header and footer, defaults to FALSE (optional)
87
+ ```yaml
88
+ quiet: true
89
+ ```
90
+
80
91
  #### Command line switches
81
- Options are not required. If none are specified then the program will read from the standard configuration file (~/.updaterepo) and automatically update the specified Repositories.
92
+ Options are not required. If none are specified then the program will read from the standard configuration file (~/.updaterepo) and automatically update the specified Repositories. Command line options will take preference over those specified in the configuration file.
82
93
 
83
94
  Enter `update_repo --help` at the command prompt to get a list of available options :
84
95
  ```
@@ -93,6 +104,8 @@ Options:
93
104
  --log option is also specified.
94
105
  -r, --dump-remote Create a dump to screen or log listing all the git remote URLS found in
95
106
  the specified directories.
107
+ -V, --verbose Display each repository and the git output to screen
108
+ -q, --quiet Run completely silent, with no output to the terminal (except fatal errors).
96
109
  -v, --version Print version and exit
97
110
  -h, --help Show this message
98
111
  ```
@@ -101,8 +114,7 @@ Options:
101
114
  Add functionality, not in any specific order :
102
115
 
103
116
  - Either add an option 'variants' or similar to allow non-standard git pull commands (eg Ubuntu kernel), or update the 'exceptions' option to do same.
104
- - Add command line options to override configuration, and even specify an alternate config file. Any options so specified will have precedence over settings specified in the configuration file. `[IN PROGRESS]`
105
- - Add command line options for verbose or quiet, with same options in config file.
117
+ - Add command line option to specify an alternate config file.
106
118
  - Add ability to specify a new directory (containing Git repos) to search from the command line, and optionally save this to the standard configuration.
107
119
  - Add new repo from the command line that will be cloned to the default repo directory and then updated as usual. Extra flag added for "add only, clone later" for offline use.
108
120
  - Add flag for 'default' repo directory (or another specific directory - if it does not already exist it will be created and added to the standard list) which will be used for new additions.
@@ -140,7 +152,7 @@ Run `rake` to run the RSpec tests, which also runs `RuboCop`, `Reek` and `inch -
140
152
  5. Create new Pull Request
141
153
 
142
154
  Please note - This Gem currently aims to pass 100% on [RuboCop][rubocop], [Reek][reek] and [Inch-CI][inch] (on pedantic mode), so all pull requests should do likewise. Ask for guidance if needed.
143
- Running `rake` will automatically test all 3 of those along with the RSpec tests. Note that Failures of Rubocop will cause the CI (Travis) to fail, however 'Reek' failures will not.
155
+ Running `rake` will automatically test all 3 of those along with the RSpec tests.
144
156
 
145
157
  [rubocop]: https://github.com/bbatsov/rubocop
146
158
  [reek]: https://github.com/troessner/reek
@@ -110,7 +110,6 @@ EOS
110
110
  opt :dump_tree, 'Create a dump to screen or log, listing all subdirectories found below the specified locations in tree format.', default: false, short: 'u'
111
111
  opt :verbose, 'Display each repository and the git output to screen', default: false, short: 'V'
112
112
  opt :quiet, 'Run completely silent, with no output to the terminal (except fatal errors).', default: false
113
- # opt :silent, 'Completely silent, no output to terminal at all.', default: false
114
113
  end
115
114
  end
116
115
  # rubocop:enable Metrics/MethodLength
@@ -70,14 +70,25 @@ module UpdateRepo
70
70
  # @param [none]
71
71
  # @return [void]
72
72
  def list_failures
73
+ # ensure we don't have duplicate errors from the same repo
74
+ remove_dups
73
75
  print_log "\n\n!! Note : The following #{@metrics[:failed_list].count}",
74
76
  ' repositories ', 'FAILED'.red.underline, ' during this run :'
77
+ # print out any and all errors into a nice list
75
78
  @metrics[:failed_list].each do |failed|
76
79
  print_log "\n [", 'x'.red, "] #{failed[:loc]}"
77
80
  print_log "\n -> ", "\"#{failed[:line].chomp}\"".red
78
81
  end
79
82
  end
80
83
 
84
+ # removes any duplications in the list of failed repos.
85
+ # @param [none]
86
+ # @return [void] modifies the @metrics[:failed_list] in place
87
+ def remove_dups
88
+ # removes duplicate ':loc' values from the Failed list.
89
+ @metrics[:failed_list].uniq! { |error| error[:loc] }
90
+ end
91
+
81
92
  # Print a list of any defined expections that will not be updated.
82
93
  # @return [void]
83
94
  # @param [none]
@@ -13,15 +13,15 @@ module UpdateRepo
13
13
  attr_reader :status
14
14
 
15
15
  # Constructor for the GitControl class.
16
- # @param repo [string] Repo name
16
+ # @param dir [string] The directory location of this local repo.
17
17
  # @param logger [instance] pointer to the Logger class
18
18
  # @param metrics [instance] pointer to the Metrics class
19
19
  # @return [void]
20
20
  # @example
21
21
  # git = GitControl.new(repo_url, @logger, @metrics)
22
- def initialize(repo, logger, metrics)
22
+ def initialize(dir, logger, metrics)
23
23
  @status = { updated: false, failed: false, unchanged: false }
24
- @repo = repo
24
+ @dir = dir
25
25
  @log = logger
26
26
  @metrics = metrics
27
27
  end
@@ -30,11 +30,9 @@ module UpdateRepo
30
30
  # @param [none]
31
31
  # @return [void]
32
32
  def update
33
- print_log '* Checking ', Dir.pwd.green, " (#{repo_url})\n"
34
- Open3.popen3('git pull') do |stdin, stdout, stderr, thread|
35
- stdin.close
36
- do_threads(stdout, stderr)
37
- thread.join
33
+ print_log '* Checking ', @dir.green, " (#{repo_url})\n"
34
+ Open3.popen2e("git -C #{@dir} pull") do |_stdin, stdout_err, _thread|
35
+ stdout_err.each { |line| handle_output(line) }
38
36
  end
39
37
  # reset the updated status in the rare case than both update and failed
40
38
  # are set. This does happen!
@@ -43,31 +41,11 @@ module UpdateRepo
43
41
 
44
42
  private
45
43
 
46
- # Create 2 individual threads to handle both STDOUT and STDERR streams,
47
- # writing to console and log if specified.
48
- # @param stdout [stream] STDOUT Stream from the popen3 call
49
- # @param stderr [stream] STDERR Stream from the popen3 call
50
- # @return [void]
51
- def do_threads(stdout, stderr)
52
- { out: stdout, err: stderr }.each do |key, stream|
53
- Thread.new do
54
- while (line = stream.gets)
55
- handle_err(line) if key == :err
56
- handle_output(line) if key == :out
57
- end
58
- end
59
- end
60
- end
61
-
62
- # output an error line and update the metrics
63
- # @param line [string] The string containing the error message
64
- # @return [void]
65
- def handle_err(line)
66
- return unless line =~ /^fatal:|^error:/
67
- print_log ' ', line.red
68
- @status[:failed] = true
69
- err_loc = Dir.pwd + " (#{@repo})"
70
- @metrics[:failed_list].push(loc: err_loc, line: line)
44
+ # Returns the repo remote url for the repo in @dir
45
+ # @param [none]
46
+ # @return [string]
47
+ def repo_url
48
+ `git -C #{@dir} config remote.origin.url`.chomp
71
49
  end
72
50
 
73
51
  # print a git output line and update the metrics if an update has occurred
@@ -75,9 +53,17 @@ module UpdateRepo
75
53
  # @return [void]
76
54
  # rubocop:disable Metrics/LineLength
77
55
  def handle_output(line)
78
- print_log ' ', line.cyan
79
- @status[:updated] = true if line =~ /^Updating\s[0-9a-f]{7}\.\.[0-9a-f]{7}/
80
- @status[:unchanged] = true if line =~ /^Already up-to-date./
56
+ if line.chomp =~ /^fatal:|^error:/
57
+ print_log ' ', line.red
58
+ @status[:failed] = true
59
+ err_loc = "#{@dir} (#{repo_url})"
60
+ @metrics[:failed_list].push(loc: err_loc, line: line)
61
+ else
62
+ print_log ' ', line.cyan
63
+ @status[:updated] = true if line =~ /^Updating\s[0-9a-f]{7}\.\.[0-9a-f]{7}/
64
+ @status[:unchanged] = true if line =~ /^Already up-to-date./
65
+ end
81
66
  end
67
+ # rubocop:enable Metrics/LineLength
82
68
  end
83
69
  end
@@ -34,7 +34,18 @@ module Helpers
34
34
  @log.output(*string)
35
35
  end
36
36
 
37
- def repo_url
38
- `git config remote.origin.url`.chomp
37
+ # Cross-platform way of finding an executable in the $PATH.
38
+ # From : http://stackoverflow.com/a/5471032/6641755
39
+ #
40
+ # which('ruby') #=> /usr/bin/ruby
41
+ def which(cmd)
42
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
43
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
44
+ exts.each do |ext|
45
+ exe = File.join(path, "#{cmd}#{ext}")
46
+ return exe if File.executable?(exe) && !File.directory?(exe)
47
+ end
48
+ end
49
+ # return nil
39
50
  end
40
51
  end
@@ -25,6 +25,10 @@ module UpdateRepo
25
25
  # open the logfile and set sync mode.
26
26
  @logfile = File.open(filename, 'w')
27
27
  @logfile.sync = true
28
+ @legend = { failed: { char: 'x', color: 'red' },
29
+ updated: { char: '^', color: 'green' },
30
+ unchanged: { char: '.', color: 'white' },
31
+ skipped: { char: 's', color: 'yellow' } }
28
32
  end
29
33
 
30
34
  # generate a filename for the log, with or without a timestamp
@@ -53,21 +57,15 @@ module UpdateRepo
53
57
  @logfile.write(string.join('').gsub(/\e\[(\d+)(;\d+)*m/, ''))
54
58
  end
55
59
 
56
- # function repostat - outputs the passed char at the passed color,
57
- # only if we are not in quiet nor verbose mode.
60
+ # function repostat - outputs a coloured char depending on the status hash,
61
+ # but not if we are in quiet or verbose mode.
58
62
  # @param status [hash] pointer to GitControl.status hash
59
63
  # @return [void]
60
64
  def repostat(status)
61
65
  # only print if not quiet and not verbose!
62
66
  return if @settings[:quiet] || @settings[:verbose]
63
- if status[:failed]
64
- print 'x'.red
65
- elsif status[:updated]
66
- print '^'.green
67
- elsif status[:unchanged]
68
- print '.'
69
- elsif status[:skipped]
70
- print 's'.yellow
67
+ @legend.each do |key, value|
68
+ print value[:char].send(value[:color].to_sym) if status[key]
71
69
  end
72
70
  end
73
71
 
@@ -79,10 +77,8 @@ module UpdateRepo
79
77
  # get calling function - need to skip first 2, also remove 'block in '
80
78
  # prefix if exists
81
79
  calling_fn = caller_locations[2].label.gsub(/block in /, '')
82
-
83
80
  # array with the functions we want to skip
84
- repo_output = %w(do_update handle_output handle_err skip_repo update)
85
-
81
+ repo_output = %w(do_update handle_output skip_repo update)
86
82
  # return the name in string if DOES match.
87
83
  calling_fn if repo_output.include?(calling_fn)
88
84
  end
@@ -1,4 +1,4 @@
1
1
  module UpdateRepo
2
2
  # constant, current version of this Gem
3
- VERSION = '0.9.0'.freeze
3
+ VERSION = '0.9.1'.freeze
4
4
  end
data/lib/update_repo.rb CHANGED
@@ -9,6 +9,7 @@ require 'yaml'
9
9
  require 'colorize'
10
10
  require 'confoog'
11
11
  require 'trollop'
12
+ require 'versionomy'
12
13
 
13
14
  # Overall module with classes performing the functionality
14
15
  # Contains Class UpdateRepo::WalkRepo
@@ -39,6 +40,8 @@ module UpdateRepo
39
40
  # walk_repo.start
40
41
  def start
41
42
  String.disable_colorization = !cmd(:color)
43
+ # check for existence of 'Git' and exit otherwise...
44
+ checkgit
42
45
  # print out our header unless we are dumping / importing ...
43
46
  @cons.show_header unless dumping?
44
47
  config['location'].each do |loc|
@@ -50,6 +53,19 @@ module UpdateRepo
50
53
 
51
54
  private
52
55
 
56
+ def checkgit
57
+ unless which('git')
58
+ print 'Git is not installed on this machine, script cannot '.red,
59
+ "continue.\n".red
60
+ exit 1
61
+ end
62
+ gitver = `git --version`.gsub(/git version /, '').chomp
63
+ return if Versionomy.parse(gitver) >= '1.8.5'
64
+ print 'Git version 1.8.5 or greater must be installed, you have '.red,
65
+ "version #{gitver}!\n".red
66
+ exit 1
67
+ end
68
+
53
69
  def dumping?
54
70
  cmd(:dump) || cmd(:dump_remote) || cmd(:dump_tree)
55
71
  end
@@ -76,17 +92,30 @@ module UpdateRepo
76
92
  # a Git repository then update it (or as directed by command line)
77
93
  # @param dirname [string] Contains the directory to search for Git repos.]
78
94
  # @return [void]
95
+ # rubocop:disable LineLength
79
96
  def recurse_dir(dirname)
97
+ walk_tree(dirname).each do |repo|
98
+ if dumping?
99
+ dump_repo(repo[:path])
100
+ else
101
+ notexception?(repo[:name]) ? update_repo(repo[:path]) : skip_repo(repo[:path])
102
+ end
103
+ end
104
+ end
105
+ # rubocop:enable LineLength
106
+
107
+ # walk the specified tree, return an array of hashes holding valid repos
108
+ # @param dirname [string] Directory to use as the base of the search
109
+ # @return [array] Array of hashes {:path, :name} for discovered Git repos
110
+ def walk_tree(dirname)
111
+ repo_list = []
80
112
  Dir.chdir(dirname) do
81
113
  Dir['**/'].each do |dir|
82
114
  next unless gitdir?(dir)
83
- if dumping?
84
- dump_repo(File.join(dirname, dir))
85
- else
86
- notexception?(dir) ? update_repo(dir) : skip_repo(dir)
87
- end
115
+ repo_list.push(path: File.join(dirname, dir), name: dir)
88
116
  end
89
117
  end
118
+ repo_list
90
119
  end
91
120
 
92
121
  # tests to see if the given directory is an exception and should be skipped
@@ -103,13 +132,11 @@ module UpdateRepo
103
132
  # @example
104
133
  # skip_repo('/Repo/Personal/work-in-progress')
105
134
  def skip_repo(dirpath)
106
- Dir.chdir(dirpath.chomp!('/')) do
107
- repo_url = `git config remote.origin.url`.chomp
108
- print_log '* Skipping ', Dir.pwd.yellow, " (#{repo_url})\n"
109
- @metrics[:skipped] += 1
110
- @log.repostat(skipped: true)
111
- @metrics[:processed] += 1
112
- end
135
+ repo_url = `git -C #{dirpath} config remote.origin.url`.chomp
136
+ print_log '* Skipping ', dirpath.yellow, " (#{repo_url})\n"
137
+ @metrics[:skipped] += 1
138
+ @log.repostat(skipped: true)
139
+ @metrics[:processed] += 1
113
140
  end
114
141
 
115
142
  # Takes the specified Repo outputs information and the repo URL then calls
@@ -118,18 +145,16 @@ module UpdateRepo
118
145
  # @return [void]
119
146
  # @example
120
147
  # update_repo('/Repo/linux/stable')
121
- def update_repo(dirname)
122
- Dir.chdir(dirname.chomp!('/')) do
123
- # create the git instance and then perform the update
124
- git = GitControl.new(repo_url, @log, @metrics)
125
- git.update
126
- @metrics[:processed] += 1
127
- # update the metrics
128
- [:failed, :updated, :unchanged].each do |metric|
129
- @metrics[metric] += 1 if git.status[metric]
130
- end
131
- @log.repostat(git.status)
148
+ def update_repo(dirpath)
149
+ # create the git instance and then perform the update
150
+ git = GitControl.new(dirpath, @log, @metrics)
151
+ git.update
152
+ @metrics[:processed] += 1
153
+ # update the metrics
154
+ [:failed, :updated, :unchanged].each do |metric|
155
+ @metrics[metric] += 1 if git.status[metric]
132
156
  end
157
+ @log.repostat(git.status)
133
158
  end
134
159
 
135
160
  # this function will either dump out a CSV with the directory and remote,
@@ -139,7 +164,7 @@ module UpdateRepo
139
164
  def dump_repo(dir)
140
165
  Dir.chdir(dir.chomp!('/')) do
141
166
  print_log "#{trunc_dir(dir, config['cmd'][:prune])}," if cmd(:dump)
142
- print_log "#{get_repo_url}\n"
167
+ print_log `git -C #{dir} config remote.origin.url`
143
168
  end
144
169
  end
145
170
 
data/update_repo.gemspec CHANGED
@@ -48,6 +48,7 @@ Gem::Specification.new do |spec|
48
48
  spec.add_dependency 'colorize'
49
49
  spec.add_dependency 'confoog'
50
50
  spec.add_dependency 'trollop'
51
+ spec.add_dependency 'versionomy'
51
52
  # on Ruby 1.9.3 we lock the 'term-ansicolor' gem to version 1.3.2
52
53
  spec.add_dependency 'term-ansicolor', '= 1.3.2' if RUBY_VERSION < '2.0'
53
54
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: update_repo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Grant Ramsay
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-07 00:00:00.000000000 Z
11
+ date: 2016-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -234,6 +234,20 @@ dependencies:
234
234
  - - ">="
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: versionomy
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :runtime
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
237
251
  description:
238
252
  email:
239
253
  - seapagan@gmail.com