lapidarist 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1105156709cc99e5c061836d1815d84721de11b80ae92f90e9404fa9461f7f49
4
- data.tar.gz: b2cbcbe7c210fefcbd870baa5c9de71d5ce4065f9e208a8f36359e6a7daeea05
3
+ metadata.gz: 99fe161d7e3d3242d0d10b602695450f45ce091fff7a7145ed5a4276fe492752
4
+ data.tar.gz: c17bebcb2a4950bff0a72ced8081ff3f66c4b8f1159b69c3f6e2ada8e68a7eba
5
5
  SHA512:
6
- metadata.gz: c4bc4c2ca10de53d0d6af8ff6154290171870029fa72366347577aad742f36e0e6ef7acf43ede35df0c03f789bf016b321748d897d16995c038b068833bf8cd8
7
- data.tar.gz: 62dc6f447328f0ce79287c826a716126a6cffe689216724b58416d2c2a3f330580a9de558cf89829456359b2ddfc80287688585de4e2291ce9fb6116b60fd1c7
6
+ metadata.gz: f3f8ae3c3281d86276683334fd3c26bcb87d00d0473ae880a4d9476e696c3f5562259d125284e4c084a897e526a7b8ebef51c729c84255ce66fad6d30b7a9155
7
+ data.tar.gz: be46e3ffcddcba98f2278e9ef879b2ea1bd63877041ad5b86601d5390bb97da1c5f96c8f028cead290362f689165736030b300b20d39d11021e4627206d5789f
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## master (unreleased)
4
4
 
5
+ * errors return status of 2 instead of 1
6
+ * capture invalid option error, and display message
7
+ * capture interrupt and clean up git commits
8
+
9
+ ### Bug fixes
10
+
11
+ * when the version doesn't change (but dependencies do), do not attempt any more recursive updates
12
+
5
13
  ### New features
6
14
 
7
15
  ## 0.1.1 (2018-08-11)
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CircleCI](https://circleci.com/gh/attack/lapidarist.svg?style=svg)](https://circleci.com/gh/attack/lapidarist)
4
4
 
5
- Take the manual work out of updaeting your ruby gems, and let Lapidarist do the work.
5
+ Take the manual work out of updating your ruby gems, and let Lapidarist do the work.
6
6
 
7
7
  You can run it from the command line yourself to update the gems of your project, or
8
8
  automate it to run and update for you.
@@ -31,11 +31,132 @@ Lapidarist depends on `bundler` and `git`.
31
31
  lapidarist -d . -t 'rspec spec' --all
32
32
  ```
33
33
 
34
+ ### Options
35
+
34
36
  To see all the options available
35
37
  ```sh
36
38
  lapidarist -h
37
39
  ```
38
40
 
41
+ #### `-t`, `--test TEST_SCRIPT`
42
+
43
+ Test script to assert that the gems updates are safe to commit.
44
+ This is run after any batch of updates, or during git bisect to investigate a
45
+ failing update.
46
+
47
+ #### `-a`, `--all`
48
+
49
+ By default, Lapidarist will only update gems that are listed in the Gemfile.
50
+ Use this option to also selectively update gems (or sub-dependencies) that are
51
+ not listed in the Gemfile.
52
+
53
+ #### `-q`, `--quiet`
54
+
55
+ Do not print anything to stdout. This will take precedence over logging
56
+ verbosity levels and debugging output.
57
+
58
+ #### `-v`
59
+
60
+ Increase verbosity of stdout output. Repeat this option up to three times
61
+ for to control the level of detail.
62
+
63
+ #### `-f`, `--commit-flags`
64
+
65
+ When Lapidarist commits gem updates extra flags can be requested to append
66
+ to this command. A common use case is to bypass local git hooks with `--no-verify`.
67
+
68
+ #### `-l`
69
+
70
+ Path to log file. This is location where Lapidarist will write a log with full
71
+ verbosity. This file will be reset for each run.
72
+
73
+ #### `-n`
74
+
75
+ By default, Lapidarist will attempt to update all outdated gems. Use this option
76
+ to limit the number of gems that are updated to a maximum number.
77
+
78
+ #### `--one`, `-n 1`
79
+
80
+ This is the same as `-n 1`, and limits Lapidarist to only update one gem.
81
+
82
+ #### `-g`, `--group`
83
+
84
+ Limit gems to be updated to a specified group(s). Multiple groups can be can
85
+ be permitted by using this option multiple times.
86
+
87
+ #### `--major`, `--minor`, `--patch`
88
+
89
+ Limit gem updates to a specific maximum level. This option is passed directly to
90
+ `bundle update` and follows the logic controlled by bundler.
91
+
92
+ #### `-r`, `--recursive`
93
+
94
+ By default, Lapidarist will only try to update a gem once. With this option,
95
+ if an update fails then Lapidarist will try again using a lower maximum level.
96
+ For example, if a gem had updates available at a patch level, a minor level and
97
+ a major level, and an update failed when updating to the major level, then with
98
+ this option Lapidarist will try again at the minor then patch levels until all
99
+ levels are exhausted or an update is accepted.
100
+
101
+ This option will respect the maximum update level option, and will only try
102
+ lower available levels, if any.
103
+
104
+ #### `-o`, `--ordered`
105
+
106
+ By default, Lapidarist will randomize the order of gems it attempts to update.
107
+ This option can be used to prevent randomization and force Lapidarist to respect
108
+ the order from `bundle outdated`, which is essentially in alphabetical order.
109
+
110
+ #### `--seed`
111
+
112
+ By default, Lapidarist will randomize the order of gems it attempts to update
113
+ with a randomly generated seed. This option can be used to control the seed and
114
+ thus the resulting random order.
115
+
116
+ #### `--promote`
117
+
118
+ By default, Lapidarist will randomize the order of all gems it attempts to update.
119
+ With this option gems can be promoted to the top of the order so that the specified
120
+ gem(s) will be updated first before any non-promoted gems.
121
+
122
+ This option can be a comma delimited list or the result of using this option
123
+ multiple times.
124
+
125
+ #### `--demote`
126
+
127
+ By default, Lapidarist will randomize the order of all gems it attempts to update.
128
+ With this option gems can be demoted to the bottom of the order so that the specified
129
+ gem(s) will be updated last after any non-demoted gems.
130
+
131
+ This option can be a comma delimited list or the result of using this option
132
+ multiple times.
133
+
134
+ If a gem is both promoted and demoted, then demoted will be preferred and the
135
+ promotion will be ignored.
136
+
137
+ #### `--only`
138
+
139
+ By default, Lapidarist will attempt to update all outdated gems. With this
140
+ option Lapidarist will only attempt to update the outdated gems that match
141
+ those specified, and ignore updating any other gems.
142
+
143
+ This option can be a comma delimited list or the result of using this option
144
+ multiple times.
145
+
146
+ #### `--except`
147
+
148
+ By default, Lapidarist will attempt to update all outdated gems. With this
149
+ option Lapidarist will attempt to update the all outdated gems except those that
150
+ match the ones specified.
151
+
152
+ This option can be a comma delimited list or the result of using this option
153
+ multiple times.
154
+
155
+ If both `--only` and `--except` are given, then a hybrid of the two options will
156
+ be used, essentially behaving like `--only` but excluding any gems provided
157
+ using `--except` (ie `lapidarist --only foo,bar --except foo` is equivalent to
158
+ `lapidarist --only bar`)
159
+
39
160
  ## Development
40
161
 
41
162
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -19,12 +19,20 @@ require_relative 'lapidarist/test_command'
19
19
  require_relative 'lapidarist/update'
20
20
  require_relative 'lapidarist/outdated'
21
21
  require_relative 'lapidarist/sha'
22
+ require_relative 'lapidarist/find_failure'
22
23
 
24
+ require_relative 'lapidarist/threads'
23
25
  require_relative 'lapidarist/summary'
24
26
  require_relative 'lapidarist/status'
25
27
  require_relative 'lapidarist/cli'
26
28
 
27
29
  module Lapidarist
30
+ STATUS_SUCCESS = 0
31
+ STATUS_FAILED = 1
32
+ STATUS_ERROR = 2
33
+
34
+ class Abort < StandardError; end
35
+
28
36
  class << self
29
37
  def config
30
38
  @config ||= Lapidarist::Configuration.new
@@ -33,5 +41,9 @@ module Lapidarist
33
41
  def logger
34
42
  @logger ||= Lapidarist::Logger.new.tap { |l| l.setup }
35
43
  end
44
+
45
+ def threads
46
+ @threads ||= Lapidarist::Threads.new
47
+ end
36
48
  end
37
49
  end
@@ -4,7 +4,6 @@ module Lapidarist
4
4
  @args = args
5
5
  @git = GitCommand.new
6
6
  @test = TestCommand.new
7
- @outdated = Outdated.new
8
7
  @update = Update.new
9
8
  @sha = Sha.new
10
9
  end
@@ -12,16 +11,15 @@ module Lapidarist
12
11
  def run
13
12
  Options.new(args).parse
14
13
  Lapidarist.logger.header('Starting lapidarist')
15
- Lapidarist.logger.debug("directory: #{Lapidarist.config.directory}", :options)
16
- Lapidarist.logger.debug("test_script: #{Lapidarist.config.test_script}", :options)
14
+ trap_interrupt
17
15
 
18
16
  unless git.clean?
19
17
  Lapidarist.logger.footer('stopping, there are uncommitted changes')
20
- return 1
18
+ return STATUS_ERROR
21
19
  end
22
20
 
23
21
  sha.record_good
24
- gems = outdated.run
22
+ gems = Lapidarist::Outdated.new.run
25
23
 
26
24
  status = nil
27
25
  attempt = 0
@@ -50,42 +48,48 @@ module Lapidarist
50
48
  status = Status.new(gems, attempt)
51
49
  break
52
50
  else
53
- Lapidarist.logger.footer('test failed, investigating failure')
51
+ Lapidarist.logger.footer('test failed')
54
52
  end
55
53
 
56
- failed_gem =
57
- if updated_gems.one?
58
- updated_but_failed_gem = updated_gems.first
59
- git.reset_hard('HEAD^')
60
-
61
- Gem.from(
62
- updated_but_failed_gem,
63
- attempt: attempt,
64
- status: :failed,
65
- updated_version: updated_but_failed_gem.latest_attempt.version
66
- )
67
- else
68
- failed_gem_name = git.bisect(sha.last_good, test)
69
- updated_but_failed_gem = updated_gems.detect { |g| g.name == failed_gem_name }
70
- gems = gems.merge(updated_gems.take(sha.new_commit_count))
71
- sha.record_good
72
-
73
- Gem.from(
74
- updated_but_failed_gem,
75
- attempt: attempt,
76
- status: :failed,
77
- updated_version: updated_but_failed_gem.latest_attempt.version
78
- )
79
- end
80
- gems = gems.merge(failed_gem)
54
+ failed_gem = Lapidarist::FindFailure.new(
55
+ gems: updated_gems,
56
+ attempt: attempt,
57
+ last_good_sha: sha.last_good
58
+ ).run
59
+ gems = gems.merge(updated_gems.take(sha.new_commit_count)).merge(failed_gem)
60
+ sha.record_good
61
+
62
+ if Lapidarist.config.debug
63
+ Summary.new(gems).display_debug
64
+ end
81
65
  end
82
66
 
83
67
  Summary.new(gems).display
84
68
  return status.to_i
69
+
70
+ rescue OptionParser::InvalidOption => e
71
+ warn e.message
72
+ warn 'For usage information, use --help'
73
+ return STATUS_ERROR
74
+ rescue Lapidarist::Abort => e
75
+ git.reset_hard(sha.last_good)
76
+ Summary.new(gems).display
77
+ return STATUS_ERROR
78
+ end
79
+
80
+ def trap_interrupt
81
+ Signal.trap('INT') do
82
+ warn
83
+ warn 'Cleaning up and exiting... Interrupt again to exit immediately.'
84
+
85
+ Lapidarist.threads.stop
86
+
87
+ raise Lapidarist::Abort
88
+ end
85
89
  end
86
90
 
87
91
  private
88
92
 
89
- attr_reader :args, :git, :test, :outdated, :update, :sha
93
+ attr_reader :args, :git, :test, :update, :sha
90
94
  end
91
95
  end
@@ -1,4 +1,5 @@
1
1
  require 'ostruct'
2
+ require 'pathname'
2
3
 
3
4
  module Lapidarist
4
5
  class Configuration < OpenStruct
@@ -0,0 +1,35 @@
1
+ module Lapidarist
2
+ class FindFailure
3
+ def initialize(gems:, attempt:, last_good_sha:)
4
+ @gems = gems
5
+ @attempt = attempt
6
+ @last_good_sha = last_good_sha
7
+ @git = GitCommand.new
8
+ @test = TestCommand.new
9
+ end
10
+
11
+ def run
12
+ Lapidarist.logger.header('Investigating failure')
13
+
14
+ updated_but_failed_gem =
15
+ if gems.one?
16
+ git.reset_hard('HEAD^')
17
+ gems.first
18
+ else
19
+ failed_gem_name = git.bisect(last_good_sha, test)
20
+ gems.detect { |gem| gem.name == failed_gem_name }
21
+ end
22
+
23
+ Gem.from(
24
+ updated_but_failed_gem,
25
+ attempt: attempt,
26
+ status: :failed,
27
+ updated_version: updated_but_failed_gem.latest_attempt.version
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :gems, :attempt, :last_good_sha, :git, :test
34
+ end
35
+ end
@@ -131,7 +131,6 @@ module Lapidarist
131
131
  private
132
132
 
133
133
  def version_changed?
134
- # use VersionChange
135
134
  updated_version && installed_version != updated_version
136
135
  end
137
136
 
@@ -28,6 +28,7 @@ module Lapidarist
28
28
  MAJOR = Level.new(name: :major, index: 1)
29
29
  MINOR = Level.new(name: :minor, index: 2)
30
30
  PATCH = Level.new(name: :patch, index: 3)
31
+ NONE = Level.new(name: "", index: 4)
31
32
 
32
33
  LEVELS = [MAJOR, MINOR, PATCH].freeze
33
34
  end
@@ -12,7 +12,7 @@ module Lapidarist
12
12
  Lapidarist.config.directory = Pathname.new(d)
13
13
  end
14
14
 
15
- opts.on("-t", "--test TEST_SCRIPT", "Test script given to git bisect.") do |t|
15
+ opts.on("-t", "--test TEST_SCRIPT", "Test script to assert that the gems updates are safe to commit.") do |t|
16
16
  Lapidarist.config.test_script = t
17
17
  end
18
18
 
@@ -28,7 +28,7 @@ module Lapidarist
28
28
  Lapidarist.config.verbosity += 1
29
29
  end
30
30
 
31
- opts.on("-f", "--commit-flags flags", "Append flags to the commit command.") do |t|
31
+ opts.on("-f", "--commit-flags FLAGS", "Append flags to the commit command.") do |t|
32
32
  Lapidarist.config.commit_flags = t
33
33
  end
34
34
 
@@ -8,7 +8,10 @@ module Lapidarist
8
8
  def record_good
9
9
  good_sha = git.head
10
10
  Lapidarist.logger.debug("good sha: #{good_sha}")
11
- @good_shas << good_sha
11
+
12
+ unless last_good == good_sha
13
+ @good_shas << good_sha
14
+ end
12
15
  end
13
16
 
14
17
  def last_good
@@ -14,13 +14,15 @@ module Lapidarist
14
14
  Lapidarist.logger.info "COMMAND > `#{command}`", 1
15
15
 
16
16
  if block_given?
17
- Open3.popen2e(command, chdir: Lapidarist.config.directory) do |_std_in, std_out_err|
17
+ Open3.popen2e(command, chdir: Lapidarist.config.directory) do |_std_in, std_out_err, wait_thr|
18
+ Lapidarist.threads << wait_thr
18
19
  yield(std_out_err)
19
20
  end
20
21
  else
21
22
  out_err = []
22
23
 
23
24
  status = Open3.popen2e(command, chdir: Lapidarist.config.directory) do |_std_in, std_out_err, wait_thr|
25
+ Lapidarist.threads << wait_thr
24
26
  while line = std_out_err.gets
25
27
  Lapidarist.logger.std_out_err(line, label || command)
26
28
  out_err << line
@@ -36,12 +38,14 @@ module Lapidarist
36
38
 
37
39
  def pipe_multiple_commands(*commands)
38
40
  if block_given?
39
- Open3.pipeline_r(*commands, chdir: Lapidarist.config.directory) do |std_out, _ts|
41
+ Open3.pipeline_r(*commands, chdir: Lapidarist.config.directory) do |std_out, ts|
42
+ Lapidarist.threads << ts
40
43
  yield(std_out)
41
44
  end
42
45
  else
43
46
  output = ''
44
- Open3.pipeline_r(*commands, chdir: Lapidarist.config.directory) do |std_out, _ts|
47
+ Open3.pipeline_r(*commands, chdir: Lapidarist.config.directory) do |std_out, ts|
48
+ Lapidarist.threads << ts
45
49
  output = std_out.read
46
50
  end
47
51
  output
@@ -6,7 +6,11 @@ module Lapidarist
6
6
  end
7
7
 
8
8
  def to_i
9
- gems.updated.any? || attempt == 1
9
+ if gems.updated.any? || attempt == 1
10
+ STATUS_SUCCESS
11
+ else
12
+ STATUS_FAILED
13
+ end
10
14
  end
11
15
 
12
16
  private
@@ -9,17 +9,15 @@ module Lapidarist
9
9
  Lapidarist.logger.summary 'Summary'
10
10
  Lapidarist.logger.summary '-'*50
11
11
  Lapidarist.logger.summary "#{object_count(gems.updated, 'gem', 'gems')} updated, #{object_count(gems.failed, 'gem', 'gems')} failed and #{object_count(gems.skipped, 'gem', 'gems')} skipped in #{object_count(gems.attempts, 'attempt', 'attempts')}"
12
- gems.each do |gem|
13
- gem.attempts.each do |i, data|
14
- case data.status
15
- when :updated
16
- Lapidarist.logger.summary " + updated #{gem.name} from #{gem.installed_version} to #{data.version}"
17
- when :failed
18
- Lapidarist.logger.summary " x failed #{gem.name} from #{gem.installed_version} to #{data.version}"
19
- when :skipped
20
- Lapidarist.logger.summary " - skipped #{gem.name} (#{data.reason})"
21
- end
22
- end
12
+ summarize_attempts do |summary|
13
+ Lapidarist.logger.summary summary
14
+ end
15
+ end
16
+
17
+ def display_debug
18
+ Lapidarist.logger.debug "#{object_count(gems.updated, 'gem', 'gems')} updated, #{object_count(gems.failed, 'gem', 'gems')} failed and #{object_count(gems.skipped, 'gem', 'gems')} skipped in #{object_count(gems.attempts, 'attempt', 'attempts')}"
19
+ summarize_attempts do |summary|
20
+ Lapidarist.logger.debug summary
23
21
  end
24
22
  end
25
23
 
@@ -41,5 +39,22 @@ module Lapidarist
41
39
  "#{length} #{plural}"
42
40
  end
43
41
  end
42
+
43
+ def summarize_attempts
44
+ gems.each do |gem|
45
+ gem.attempts.each do |i, data|
46
+ summary =
47
+ case data.status
48
+ when :updated
49
+ " + updated #{gem.name} from #{gem.installed_version} to #{data.version}"
50
+ when :failed
51
+ " x failed #{gem.name} from #{gem.installed_version} to #{data.version}"
52
+ when :skipped
53
+ " - skipped #{gem.name} (#{data.reason})"
54
+ end
55
+ yield summary
56
+ end
57
+ end
58
+ end
44
59
  end
45
60
  end
@@ -0,0 +1,42 @@
1
+ module Lapidarist
2
+ class Threads
3
+ def initialize
4
+ @threads = []
5
+ @abort = false
6
+ end
7
+
8
+ def <<(thread)
9
+ @threads += Array(thread)
10
+ end
11
+
12
+ def stop
13
+ if aborting?
14
+ kill
15
+ exit! STATUS_ERROR
16
+ else
17
+ @abort = true
18
+ abort
19
+ end
20
+ end
21
+
22
+ def aborting?
23
+ @abort
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :threads
29
+
30
+ def alive
31
+ threads.select { |thread| thread.alive? }
32
+ end
33
+
34
+ def abort
35
+ alive.each { |thread| Process.kill("INT", thread.pid) }
36
+ end
37
+
38
+ def kill
39
+ alive.each { |thread| Process.kill("KILL", thread.pid) }
40
+ end
41
+ end
42
+ end
@@ -1,3 +1,3 @@
1
1
  module Lapidarist
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -33,6 +33,12 @@ module Lapidarist
33
33
  Lapidarist::MINOR
34
34
  elsif updated_segments && updated_segments[0] == installed_segments[0] && updated_segments[1] == installed_segments[1] && updated_segments[2] > installed_segments[2]
35
35
  Lapidarist::PATCH
36
+ elsif updated_segments && (
37
+ (updated_segments[0] == installed_segments[0] && updated_segments[1] == installed_segments[1] && updated_segments[2] <= installed_segments[2]) ||
38
+ (updated_segments[0] == installed_segments[0] && updated_segments[1] <= installed_segments[1]) ||
39
+ updated_segments[0] <= installed_segments[0]
40
+ )
41
+ Lapidarist::NONE
36
42
  end
37
43
  end
38
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lapidarist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Gangl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-11 00:00:00.000000000 Z
11
+ date: 2018-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -78,6 +78,7 @@ files:
78
78
  - lib/lapidarist/bundle_command.rb
79
79
  - lib/lapidarist/cli.rb
80
80
  - lib/lapidarist/configuration.rb
81
+ - lib/lapidarist/find_failure.rb
81
82
  - lib/lapidarist/gem.rb
82
83
  - lib/lapidarist/gem_version.rb
83
84
  - lib/lapidarist/gems.rb
@@ -93,6 +94,7 @@ files:
93
94
  - lib/lapidarist/status.rb
94
95
  - lib/lapidarist/summary.rb
95
96
  - lib/lapidarist/test_command.rb
97
+ - lib/lapidarist/threads.rb
96
98
  - lib/lapidarist/update.rb
97
99
  - lib/lapidarist/version.rb
98
100
  - lib/lapidarist/version_change.rb