bumpit 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 694d73b95855e15d8cb8cbb1a1875dfcff628a7ca89d00cd9b824797df4a7e7b
4
+ data.tar.gz: 1e61d9c60c989436b2dd6990e6a49ffedc418613254e661d192d9870eaae4c3c
5
+ SHA512:
6
+ metadata.gz: b8d873aede2f02470a26806390a24cd1316d3d1150b567ba9b5c9acf85e69a2f3ef3477ceac00137a1ea1c17da9f25018f1e4c8cf0ef48d29cf3103b8f286fda
7
+ data.tar.gz: e04b18b5cf89a647f1a3e1433bd3a16f5725fc80af659b9e7699e75b7de7845624b30cba8269f28f53d148a888dcfad8eb45a8f283eb9ea4a71c07866d7015e7
data/exe/bumpit ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bumpit"
6
+ require "optparse"
7
+
8
+ options = {}
9
+
10
+ OptionParser.new do |parser|
11
+ parser.on("-c", "--commit", "Output a commit message.") do |commit|
12
+ options[:commit] = commit
13
+ end
14
+
15
+ parser.on("-h", "--help", "Prints this help message.") do
16
+ puts <<~HELP
17
+ USAGE
18
+ $ bumpit [options]
19
+
20
+ OPTIONS
21
+ #{parser.summarize.join.strip}
22
+
23
+ EXAMPLES
24
+ $ bumpit --commit --pristine --verify="bundle exec rake && yarn test"
25
+
26
+ HELP
27
+
28
+ exit
29
+ end
30
+
31
+ parser.on("-p", "--pristine", "Don't bump if not in a clean state.") do |pristine|
32
+ options[:pristine] = pristine
33
+ end
34
+
35
+ parser.on("--verify [COMMAND]", String, "Run a command to verify changes.") do |verify|
36
+ options[:verify] = verify
37
+ end
38
+ end.parse!
39
+
40
+ Bumpit.new(**options).call
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler"
4
+
5
+ # :nocov:
6
+ class Bumpit
7
+ module Managers
8
+ class Base
9
+ # Return if an executable exists or not.
10
+ #
11
+ # @return [Boolean]
12
+ def self.executable?(executable)
13
+ !::Bundler.which(executable).nil?
14
+ end
15
+
16
+ protected
17
+
18
+ # Silence the output of the provided block.
19
+ #
20
+ # @param block [Block] The block to silence output for.
21
+ # @return [void]
22
+ def silence_output(&)
23
+ original_stdout = $stdout.clone
24
+ original_stderr = $stderr.clone
25
+
26
+ $stdout = $stderr = File.new(File::NULL, "w")
27
+
28
+ yield
29
+ ensure
30
+ $stdout = original_stdout
31
+ $stderr = original_stderr
32
+ end
33
+
34
+ # Convert an array to a sentence.
35
+ #
36
+ # @param [Array] array The array to convert to a sentence.
37
+ # @return [String]
38
+ def to_sentence(array)
39
+ case array.length
40
+ when 0
41
+ +""
42
+ when 1
43
+ array.first
44
+ when 2
45
+ "#{array[0]} and #{array[1]}"
46
+ else
47
+ "#{array[0...-1].join(", ")}, and #{array[-1]}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ # :nocov:
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/cli/update"
4
+ require "pathname"
5
+
6
+ class Bumpit
7
+ module Managers
8
+ class Bundler < Base
9
+ DEPENDENCY_MATCH = /gem "([^"]+)"/
10
+ FILENAMES = %w(Gemfile Gemfile.lock).freeze
11
+ OUTDATED_COMMAND = "bundle outdated --only-explicit --parseable 2>/dev/null"
12
+ OUTDATED_MATCHER = /\A(.+) \(newest (.+), installed (.+), requested = (.+)\)\z/
13
+
14
+ # Determine if the manager is valid.
15
+ #
16
+ # @return [Boolean]
17
+ def self.valid?
18
+ executable?("bundle") &&
19
+ FILENAMES.all? do |filename|
20
+ File.exist?(Pathname.new(Dir.pwd).join(filename))
21
+ end
22
+ end
23
+
24
+ # Bump the dependencies, if there are any.
25
+ #
26
+ # @return [void]
27
+ def bump
28
+ if outdated.any?
29
+ write_contents
30
+ bundler_update
31
+ end
32
+ end
33
+
34
+ # Return a message for which dependencies were bumped.
35
+ #
36
+ # @return [String]
37
+ def message
38
+ if outdated.any?
39
+ "Updates #{to_sentence(outdated.keys.sort)} in Ruby."
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ # Reset the existing cache and settings and run update for Bundler.
46
+ #
47
+ # @return [void]
48
+ def bundler_update
49
+ silence_output do
50
+ ::Bundler.clear_gemspec_cache
51
+ ::Bundler.reset!
52
+ ::Bundler.reset_settings_and_root!
53
+
54
+ ::Bundler::CLI::Update.new({ all: true }, []).run
55
+ end
56
+ end
57
+
58
+ # Return the contents of the Gemfile.
59
+ #
60
+ # @return [String] The contents of the Gemfile.
61
+ def contents
62
+ @contents ||= File.read("Gemfile")
63
+ end
64
+
65
+ # Return the modified contents of the Gemfile.
66
+ #
67
+ # @return [Array]
68
+ def modified_contents
69
+ contents.split("\n").map do |line|
70
+ _, name = line.match(DEPENDENCY_MATCH).to_a
71
+ dependency = outdated[name]
72
+
73
+ if dependency
74
+ line.gsub(dependency[:current], dependency[:latest])
75
+ else
76
+ line
77
+ end
78
+ end
79
+ end
80
+
81
+ # Return the outdated dependencies.
82
+ #
83
+ # @return [Hash]
84
+ def outdated
85
+ @outdated ||= `#{OUTDATED_COMMAND}`.split("\n").each.with_object({}) do |dependency, result|
86
+ if dependency.match(OUTDATED_MATCHER)
87
+ _, name, latest, _, current = dependency.match(OUTDATED_MATCHER).to_a
88
+
89
+ result[name] = { current: current, latest: latest }
90
+ end
91
+ end
92
+ end
93
+
94
+ # Write the file with the bumped dependencies.
95
+ #
96
+ # @return [void]
97
+ def write_contents
98
+ File.write("Gemfile", "#{modified_contents.join("\n")}\n")
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "pathname"
5
+
6
+ class Bumpit
7
+ module Managers
8
+ class Yarn < Base
9
+ FILENAMES = %w(package.json yarn.lock).freeze
10
+ OUTDATED_COMMAND = "npm outdated --json"
11
+
12
+ # Determine if the manager is valid.
13
+ #
14
+ # @return [Boolean]
15
+ def self.valid?
16
+ executable?("npm") &&
17
+ executable?("yarn") &&
18
+ FILENAMES.all? do |filename|
19
+ File.exist?(Pathname.new(Dir.pwd).join(filename))
20
+ end
21
+ end
22
+
23
+ # Bump the dependencies, if there are any.
24
+ #
25
+ # @return [void]
26
+ def bump
27
+ if outdated.any?
28
+ package_update
29
+ yarn_update
30
+ end
31
+ end
32
+
33
+ # Return a message for which dependencies were bumped.
34
+ #
35
+ # @return [String]
36
+ def message
37
+ if outdated.any?
38
+ "Updates #{to_sentence(outdated.keys.sort)} in JavaScript."
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # Update the package.json dependencies.
45
+ #
46
+ # @return [void]
47
+ def package_update
48
+ `yarn up --exact "*"`
49
+ end
50
+
51
+ # Return the outdated dependencies.
52
+ #
53
+ # @return [Hash]
54
+ def outdated
55
+ @outdated ||= JSON.parse(`#{OUTDATED_COMMAND}`)
56
+ .each
57
+ .with_object({}) do |(name, details), result|
58
+ result[name] = { current: details["current"], latest: details["latest"] }
59
+ end
60
+ end
61
+
62
+ # Update all Yarn dependencies.
63
+ #
64
+ # @return [void]
65
+ def yarn_update
66
+ `rm yarn.lock`
67
+ `yarn`
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bumpit
4
+ module Managers
5
+ end
6
+ end
7
+
8
+ require_relative "managers/base"
9
+ require_relative "managers/bundler"
10
+ require_relative "managers/yarn"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bumpit
4
+ VERSION = "0.1.0"
5
+ end
data/lib/bumpit.rb ADDED
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "english"
4
+
5
+ require_relative "bumpit/managers"
6
+ require_relative "bumpit/version"
7
+
8
+ class Bumpit
9
+ WORD_WRAP_MATCHER = /(.{1,78})(?:[^\S\n]+\n?|\n*\Z|\n)|\n/
10
+ WORD_WRAP_PREFIX = "* "
11
+
12
+ # Initialize an instance.
13
+ #
14
+ # @param commit [Boolean] Whether or not to commit the changes.
15
+ # @param pristine [Boolean] Whether or not to require a pristine directory.
16
+ # @param verify [String] Optional command to verify the changes.
17
+ # @return [void]
18
+ def initialize(commit: false, pristine: false, verify: nil)
19
+ @commit = commit
20
+ @pristine = pristine
21
+ @verify = verify
22
+ end
23
+
24
+ # Bump it!
25
+ #
26
+ # @return [void]
27
+ def call
28
+ ensure_pristine!
29
+ bump!
30
+ verify!
31
+ commit!
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :commit, :pristine, :verify
37
+
38
+ # Find valid dependency managers and bump their dependencies.
39
+ #
40
+ # @return [void]
41
+ def bump!
42
+ managers.each(&:bump)
43
+ end
44
+
45
+ # Print a commit message, if requested and messages are present.
46
+ #
47
+ # @return [void]
48
+ def commit!
49
+ if commit
50
+ messages = managers.filter_map(&:message).flatten
51
+
52
+ if messages.any?
53
+ puts "Update dependencies.\n\n"
54
+ puts(messages.map { |message| word_wrap(message) })
55
+ end
56
+ end
57
+ end
58
+
59
+ # Ensure the working directory is pristine, if requested.
60
+ #
61
+ # @return [void]
62
+ def ensure_pristine!
63
+ if pristine && `git status --porcelain`.strip != ""
64
+ warn "Working directory must be pristine to run."
65
+ exit 1
66
+ end
67
+ end
68
+
69
+ # Return instances of valid managers.
70
+ #
71
+ # @return [Array]
72
+ def managers
73
+ @managers ||= [
74
+ Managers::Bundler,
75
+ Managers::Yarn
76
+ ].select(&:valid?).map(&:new)
77
+ end
78
+
79
+ # Verify the changes via a system command, if requested.
80
+ #
81
+ # @return [void]
82
+ def verify!
83
+ if verify
84
+ system(verify)
85
+
86
+ unless $CHILD_STATUS.success?
87
+ warn "The `#{verify}` verification command failed."
88
+
89
+ exit $CHILD_STATUS.exitstatus
90
+ end
91
+ end
92
+ end
93
+
94
+ # Wrap text to fit within 80 characters.
95
+ #
96
+ # @param [String] text The text to wrap.
97
+ # @return [String] The wrapped text.
98
+ def word_wrap(text)
99
+ WORD_WRAP_PREFIX + text.gsub(WORD_WRAP_MATCHER, "\\1\n#{" " * WORD_WRAP_PREFIX.size}").strip
100
+ end
101
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bumpit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tristan Dunn
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: bundler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.6'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.6'
26
+ - !ruby/object:Gem::Dependency
27
+ name: json
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.10'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.10'
40
+ description: Automatically bump dependencies in multiple package managers.
41
+ email: hello@tristandunn.com
42
+ executables:
43
+ - bumpit
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - exe/bumpit
48
+ - lib/bumpit.rb
49
+ - lib/bumpit/managers.rb
50
+ - lib/bumpit/managers/base.rb
51
+ - lib/bumpit/managers/bundler.rb
52
+ - lib/bumpit/managers/yarn.rb
53
+ - lib/bumpit/version.rb
54
+ homepage: https://github.com/tristandunn/bumpit
55
+ licenses:
56
+ - MIT
57
+ metadata:
58
+ bug_tracker_uri: https://github.com/tristandunn/bumpit/issues
59
+ changelog_uri: https://github.com/tristandunn/bumpit/blob/main/CHANGELOG.md
60
+ rubygems_mfa_required: 'true'
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.6.7
76
+ specification_version: 4
77
+ summary: Automatically bump dependencies in multiple package managers.
78
+ test_files: []