lock-gemfile 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: 70164942502370d3ca05b5e8ed259e798dd21b6891d7e0bb6324daf444312dc1
4
+ data.tar.gz: cd2765434d8e9cbb9b73d657d0e36bbc78e10d3fb5d32254c4b8c9f3cf632553
5
+ SHA512:
6
+ metadata.gz: 24b9c62a24cc58031f3575abdc194a6bcb0621144e92e22e7c81194333a4220bdb15f00d30b9d996e70ec4bd20cc84c79bb5a100303d98b9948537e3e25eb98d
7
+ data.tar.gz: bd3338d1f6d64ac2d223884aeb5f9ec59fa185b53e5c00f576ac4aae27fb3d38ffd1f040d17ac96416aac532ccec9600c4442c89ec656c4531bdfed75b124c82
data/.rubocop.yml ADDED
@@ -0,0 +1,25 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Enabled: false
14
+ Metrics/MethodLength:
15
+ Enabled: false
16
+ Metrics/AbcSize:
17
+ Enabled: false
18
+ Metrics/CyclomaticComplexity:
19
+ Enabled: false
20
+ Metrics/PerceivedComplexity:
21
+ Enabled: false
22
+
23
+ Style/StringConcatenation:
24
+ Enabled: false
25
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-07-05
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Durable Programming Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # Lock::Gemfile
2
+
3
+ Lock::Gemfile is a Ruby library that provides functionality to update a Gemfile with locked versions - typically, from a corresponding Gemfile.lock file, but you can also provide arbitrary versions as well.
4
+
5
+ ## Installation
6
+
7
+ Install using RubyGems:
8
+
9
+ ```
10
+ $ gem install lock-gemfile
11
+ ```
12
+
13
+ Alternatively, if you intend to use this as a library, you can add this to your Gemfile:
14
+
15
+ ```
16
+ $ bundle add lock-gemfile
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Command-line Interface
22
+
23
+ Lock::Gemfile provides a command-line interface (CLI) through the `bin/lock-gemfile` script. You can run the script with the following command:
24
+
25
+ ```
26
+ $ lock-gemfile update GEMFILE [options]
27
+ ```
28
+
29
+ Replace `GEMFILE` with the path to your Gemfile.
30
+
31
+ #### Options
32
+
33
+ - `-w`, `--[no-]write`: Write the updated Gemfile back to disk (default: `false`).
34
+ - `-p`, `--[no-]pessimistic`: Use pessimistic version constraints (`~>`) (default: `true`).
35
+
36
+ #### Examples
37
+
38
+ Update a Gemfile and print the result to the console:
39
+
40
+ ```
41
+ $ lock-gemfile update Gemfile
42
+ ```
43
+
44
+ Update a Gemfile and write the changes back to the file:
45
+
46
+ ```
47
+ $ lock-gemfile update Gemfile --write
48
+ ```
49
+
50
+ Update a Gemfile using exact version constraints:
51
+
52
+ ```
53
+ $ lock-gemfile update Gemfile --no-pessimistic
54
+ ```
55
+
56
+ ### Library API
57
+
58
+ You can also use Lock::Gemfile as a library in your own Ruby code.
59
+
60
+ ```ruby
61
+ require 'lock/gemfile'
62
+
63
+ # Read the content of the Gemfile
64
+ gemfile_content = File.read('Gemfile')
65
+
66
+ # Parse the corresponding Gemfile.lock using Bundler's LockfileParser
67
+ lockfile = Bundler::LockfileParser.new(Bundler.read_file('Gemfile.lock'))
68
+
69
+ # Create a hash to store the desired versions of each gem
70
+ desired_versions = {}
71
+ lockfile.specs.each do |spec|
72
+ desired_versions[spec.name] = spec.version
73
+ end
74
+
75
+ # Create a buffer to hold the Gemfile content
76
+ buffer = Parser::Source::Buffer.new('(gemfile)')
77
+ buffer.source = gemfile_content
78
+
79
+ # Create a new Ruby parser
80
+ parser = Parser::CurrentRuby.new
81
+ # Parse the Gemfile content into an Abstract Syntax Tree (AST)
82
+ ast = parser.parse(buffer)
83
+
84
+ # Create a new instance of the Lock::Gemfile::Rewriter
85
+ rewriter = Lock::Gemfile::Rewriter.new
86
+ # Set the desired versions from the lockfile
87
+ rewriter.lockfile = desired_versions
88
+ # Set the pessimistic option
89
+ rewriter.pessimistic = true
90
+
91
+ # Rewrite the Gemfile AST with the locked versions
92
+ transformed_code = rewriter.rewrite(buffer, ast)
93
+
94
+ # Print the transformed Gemfile content
95
+ puts transformed_code
96
+ ```
97
+
98
+ ## How It Works
99
+
100
+ Lock::Gemfile uses the following steps to update a Gemfile with locked versions:
101
+
102
+ 1. Read the content of the specified Gemfile.
103
+ 2. Parse the corresponding Gemfile.lock using Bundler's LockfileParser.
104
+ 3. Create a hash to store the desired versions of each gem based on the lockfile.
105
+ 4. Create a buffer to hold the Gemfile content and parse it into an AST using the Parser gem.
106
+ 5. Create an instance of the Lock::Gemfile::Rewriter and set the desired versions and pessimistic option.
107
+ 6. Rewrite the Gemfile AST with the locked versions using the rewriter.
108
+ 7. Transform the modified AST back into source code.
109
+ 8. Print the transformed Gemfile content to the console or write it back to the file, depending on the options.
110
+
111
+ The core of the library is the Lock::Gemfile::Rewriter class, which is a subclass of Parser::TreeRewriter. It traverses the AST and looks for `gem` method calls. For each `gem` call found, it checks if a version specifier is already present. If not, it retrieves the locked version from the provided lockfile hash and inserts a version specifier string after the gem name. The version specifier can be either pessimistic or exact, depending on the value of the `pessimistic` attribute.
112
+
113
+ ## Development
114
+
115
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
116
+
117
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
118
+
119
+
120
+ ## License
121
+
122
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
123
+
124
+ ## Commercial Support
125
+
126
+ Commercial support for lock-gemfile and related tools is available from Durable Programming, LLC. You can contact us at [durableprogramming.com](https://www.durableprogramming.com).
127
+
128
+ ![Durable Programming, LLC Logo](https://durableprogramming.com/wp-content/uploads/2022/04/Full-logo-2color-1024x392.png)
129
+
130
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
data/bin/lock-gemfile ADDED
@@ -0,0 +1,98 @@
1
+ #!/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # lock-gemfile
5
+ #
6
+ # This script updates a Gemfile with locked versions from the
7
+ # corresponding Gemfile.lock file. It uses the Parser gem to parse the
8
+ # Gemfile into an Abstract Syntax Tree (AST), and then uses a custom
9
+ # TreeRewriter to modify the AST, inserting the locked versions from
10
+ # the lockfile. The modified AST is then transformed back into source
11
+ # code and either printed to the console or written back to the Gemfile,
12
+ # depending on the provided options.
13
+ #
14
+ # Usage:
15
+ # gemfile_updater.rb update GEMFILE [options]
16
+ #
17
+ # Options:
18
+ # -w, [--write], [--no-write] # Write the updated Gemfile back
19
+ # to disk (default: false) -p, [--pessimistic], [--no-pessimistic] #
20
+ # Use pessimistic version constraints (~>) (default: true)
21
+ #
22
+ # Examples:
23
+ # gemfile_updater.rb update Gemfile gemfile_updater.rb update Gemfile
24
+ # --write gemfile_updater.rb update Gemfile --no-pessimistic
25
+ #
26
+ # Dependencies:
27
+ # - parser - bundler - thor
28
+
29
+ require "parser/current"
30
+ require "bundler"
31
+ require "thor"
32
+
33
+ require_relative "../lib/lock/gemfile/rewriter"
34
+
35
+ class GemfileUpdater < Thor
36
+ #
37
+ # This class update a Gemfile with locked versions - designed to pull
38
+ # from the corresponding Gemfile.lock file, it can also accept an
39
+ # arbitrary hash of versions.. It uses the Parser gem to parse the
40
+ # Gemfile into an Abstract Syntax Tree (AST), and then uses a custom
41
+ # TreeRewriter to modify the AST, inserting the locked versions from
42
+ # the lockfile. The modified AST is then transformed back into source
43
+ # code and either printed to the console or written back to the Gemfile,
44
+ # depending on the provided options.
45
+ #
46
+ desc "update GEMFILE", "Update Gemfile with locked versions"
47
+
48
+ option :write, type: :boolean, default: false, aliases: "-w"
49
+ option :pessimistic, type: :boolean, default: true, aliases: "-p"
50
+
51
+ def update(gemfile)
52
+ # Read the content of the specified Gemfile
53
+ gemfile_content = File.read(gemfile)
54
+
55
+ # Parse the corresponding Gemfile.lock using Bundler's LockfileParser
56
+ lockfile = Bundler::LockfileParser.new(Bundler.read_file(gemfile + ".lock"))
57
+
58
+ # Create a hash to store the desired versions of each gem
59
+ desired_versions = {}
60
+
61
+ # Iterate over each gem specification in the lockfile
62
+ lockfile.specs.each do |spec|
63
+ # Store the gem name and its locked version in the desired_versions hash
64
+ desired_versions[spec.name] = spec.version
65
+ end
66
+
67
+ # Create a buffer to hold the Gemfile content
68
+ buffer = Parser::Source::Buffer.new("(gemfile)")
69
+ buffer.source = gemfile_content
70
+
71
+ # Create a new Ruby parser
72
+ parser = Parser::CurrentRuby.new
73
+ # Parse the Gemfile content into an Abstract Syntax Tree (AST)
74
+ ast = parser.parse(buffer)
75
+
76
+ # Create a new instance of the Lock::Gemfile::Rewriter
77
+ rewriter = Lock::Gemfile::Rewriter.new
78
+ # Set the desired versions from the lockfile
79
+ rewriter.lockfile = desired_versions
80
+ # Set the pessimistic option based on the command-line argument
81
+ rewriter.pessimistic = options[:pessimistic]
82
+
83
+ # Rewrite the Gemfile AST with the locked versions
84
+ transformed_code = rewriter.rewrite(buffer, ast)
85
+
86
+ # Print the transformed Gemfile content
87
+ puts transformed_code
88
+
89
+ # If the write option is not specified, exit the method
90
+ return unless options[:write]
91
+
92
+ # Write the transformed Gemfile content back to the original file
93
+ File.write(gemfile, transformed_code)
94
+ end
95
+ end
96
+
97
+ # Start the GemfileUpdater CLI with the provided command-line arguments
98
+ GemfileUpdater.start(ARGV)
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parser/current"
4
+
5
+ module Lock
6
+ module Gemfile
7
+ # The Lock::Gemfile::Rewriter class is a subclass of
8
+ # Parser::TreeRewriter that rewrites a Gemfile's Abstract Syntax
9
+ # Tree (AST) to include locked gem versions from a corresponding
10
+ # Gemfile.lock file.
11
+ #
12
+ # The rewriter traverses the AST and looks for `gem` method calls. For
13
+ # each `gem` call found, it checks if a version specifier is already
14
+ # present. If not, it retrieves the locked version from the provided
15
+ # lockfile hash and inserts a version specifier string after the
16
+ # gem name.
17
+ #
18
+ # The version specifier can be either pessimistic or exact, depending
19
+ # on the value of the `pessimistic` attribute. If `pessimistic` is
20
+ # true (default), the version specifier will be prefixed with "~>",
21
+ # otherwise it will be an exact version.
22
+ #
23
+ # Example usage:
24
+ #
25
+ # parser = Parser::CurrentRuby.new ast = parser.parse(buffer)
26
+ #
27
+ # rewriter = Lock::Gemfile::Rewriter.new rewriter.lockfile = {
28
+ # "rails" => "6.1.0", "puma" => "5.0.4"
29
+ # } rewriter.pessimistic = true
30
+ #
31
+ # modified_ast = rewriter.rewrite(buffer, ast)
32
+ #
33
+ # Attributes:
34
+ # lockfile (Hash): A hash containing gem names as keys and their
35
+ # locked versions as values. pessimistic (Boolean): Determines
36
+ # whether to use pessimistic version specifiers. Default is true.
37
+ #
38
+ # Methods:
39
+ # on_send(node): Called when a `:send` node is encountered in the
40
+ # AST. Checks if the node represents a `gem` method call
41
+ # and inserts the locked version specifier if
42
+ # applicable.
43
+ class Rewriter < Parser::TreeRewriter
44
+ attr_accessor :lockfile, :pessimistic
45
+
46
+ # Handles `:send` nodes in the AST, which represent method calls.
47
+ #
48
+ # If the node is a `gem` method call and doesn't already have
49
+ # a version specifier, retrieves the locked version from the
50
+ # `lockfile` hash and inserts a version specifier string.
51
+ #
52
+ # The version specifier can be either pessimistic or exact,
53
+ # depending on the value of `pessimistic`.
54
+ #
55
+ # Arguments:
56
+ #
57
+ # node (Parser::AST::Node): The `:send` node being processed.
58
+ #
59
+ def on_send(node)
60
+ return unless node.type == :send && node.children[1] == :gem
61
+
62
+ gem_name = node.children[2].children[0]
63
+
64
+ old_version_specifier = node.children[3]
65
+ already_has_version_specifier = old_version_specifier && old_version_specifier.type == :str
66
+
67
+ return if already_has_version_specifier
68
+
69
+ lockfile_gem_details = lockfile[gem_name]
70
+ new_version_specifier = lockfile_gem_details&.to_s
71
+ prefix = if pessimistic
72
+ "~> "
73
+ else
74
+ ""
75
+ end
76
+
77
+ return unless new_version_specifier
78
+
79
+ insert_after(node.children[2].location.end, ", '#{prefix}#{new_version_specifier}'")
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lock
4
+ module Gemfile
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gemfile/version"
4
+
5
+ module Lock
6
+ module Gemfile
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ module Lock
2
+ module Gemfile
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lock-gemfile
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Durable Programming Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: This gem provides a command-line tool to update a Gemfile with locked
56
+ versions from the corresponding Gemfile.lock file.
57
+ email:
58
+ - djberube@durableprogramming.com
59
+ executables:
60
+ - lock-gemfile
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".rubocop.yml"
65
+ - CHANGELOG.md
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/lock-gemfile
70
+ - lib/lock/gemfile.rb
71
+ - lib/lock/gemfile/rewriter.rb
72
+ - lib/lock/gemfile/version.rb
73
+ - sig/lock/gemfile.rbs
74
+ homepage: https://github.com/durableprogramming/lock-gemfile
75
+ licenses:
76
+ - MIT
77
+ metadata:
78
+ homepage_uri: https://github.com/durableprogramming/lock-gemfile
79
+ source_code_uri: https://github.com/durableprogramming/lock-gemfile
80
+ changelog_uri: https://github.com/durableprogramming/lock-gemfile/blob/main/CHANGELOG.md
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 3.0.0
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.3.26
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A tool to update Gemfile with locked versions
100
+ test_files: []