bundler-gem_bytes 0.2.0 → 0.2.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 +4 -4
- data/.release-please-manifest.json +3 -0
- data/CHANGELOG.md +21 -0
- data/README.md +92 -10
- data/Rakefile +8 -0
- data/lib/bundler/gem_bytes/actions/gemspec/attribute.rb +51 -0
- data/lib/bundler/gem_bytes/actions/gemspec/attribute_node.rb +32 -0
- data/lib/bundler/gem_bytes/actions/gemspec/delete_dependency.rb +106 -0
- data/lib/bundler/gem_bytes/actions/gemspec/dependency.rb +73 -0
- data/lib/bundler/gem_bytes/actions/gemspec/dependency_node.rb +32 -0
- data/lib/bundler/gem_bytes/actions/gemspec/gem_specification.rb +121 -0
- data/lib/bundler/gem_bytes/actions/gemspec/upsert_dependency.rb +210 -0
- data/lib/bundler/gem_bytes/actions/gemspec.rb +281 -0
- data/lib/bundler/gem_bytes/actions.rb +22 -32
- data/lib/bundler/gem_bytes/bundler_command.rb +5 -7
- data/lib/bundler/gem_bytes/script_executor.rb +26 -6
- data/lib/bundler/gem_bytes/version.rb +1 -1
- data/lib/bundler/gem_bytes.rb +1 -1
- data/release-please-config.json +22 -0
- metadata +15 -11
- data/lib/bundler/gem_bytes/gemspec/delete_dependency.rb +0 -216
- data/lib/bundler/gem_bytes/gemspec/upsert_dependency.rb +0 -336
- data/lib/bundler/gem_bytes/gemspec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad62897e0c19142e68da32d5cc2b63e1c2622beeaa9ecda235b48679f50ab3e2
|
4
|
+
data.tar.gz: e3a1340866ca56329fc9d03e4acae7dd1357ac288551ac6c196c79180d5818ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64d2885ac473cea7a79cd7a48a04681ea9d60d0b3e027db73f64b622c827d49656aaf06c7c02b6f4bc46ff2c058b5b6b547f2545714a8d3886c55409a2b70f0e
|
7
|
+
data.tar.gz: 0f226e9914850042449dfd32b190f1f5f3b129ab16b334afa624489bf8633222ba18104017d4489fa820dcd12bb907ea3c89c8aeea80f7bc5a798d763e758fef
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,27 @@ All notable changes to the process_executer gem will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.2.2](https://github.com/main-branch/bundler-gem_bytes/compare/v0.2.1...v0.2.2) (2025-04-16)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Automate commit-to-publish workflow version file path wrong ([4220bd4](https://github.com/main-branch/bundler-gem_bytes/commit/4220bd4428cc4841d56c65fb86540d9237b0944b))
|
14
|
+
|
15
|
+
## [0.2.1](https://github.com/main-branch/bundler-gem_bytes/compare/v0.2.0...v0.2.1) (2025-04-16)
|
16
|
+
|
17
|
+
|
18
|
+
### Features
|
19
|
+
|
20
|
+
* Add a gemspec action to make modification to a project gemspec ([d83d145](https://github.com/main-branch/bundler-gem_bytes/commit/d83d14598922fe8b54de12b92232e8cae6e6e668))
|
21
|
+
* Add the remove_dependency sub-action to the gemspec action ([ba1ff7b](https://github.com/main-branch/bundler-gem_bytes/commit/ba1ff7b8401ff7dd29c37f457da14452ce55c8f2))
|
22
|
+
|
23
|
+
|
24
|
+
### Bug Fixes
|
25
|
+
|
26
|
+
* Allow dependencies to have multiple version constraints ([4217151](https://github.com/main-branch/bundler-gem_bytes/commit/421715118af964e5e53ae35cda445e5b2dfd4f48))
|
27
|
+
* Automate commit-to-publish workflow ([3d018da](https://github.com/main-branch/bundler-gem_bytes/commit/3d018da4b2ac3caceaa81dd12a0b7771fc03db18))
|
28
|
+
|
8
29
|
## v0.2.0 (2024-10-30)
|
9
30
|
|
10
31
|
[Full Changelog](https://github.com/main-branch/bundler-gem_bytes/compare/v0.1.0..v0.2.0)
|
data/README.md
CHANGED
@@ -7,9 +7,6 @@ Version](https://badge.fury.io/rb/bundler-gem_bytes.svg)](https://badge.fury.io/
|
|
7
7
|
Log](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://rubydoc.info/gems/bundler-gem_bytes/file/CHANGELOG.md)
|
8
8
|
[](https://github.com/main-branch/bundler-gem_bytes/actions/workflows/continuous-integration.yml)
|
10
|
-
[](https://codeclimate.com/github/main-branch/bundler-gem_bytes/maintainability)
|
11
|
-
[](https://codeclimate.com/github/main-branch/bundler-gem_bytes/test_coverage)
|
13
10
|
[](https://conventionalcommits.org)
|
15
12
|
[](https://main-branch.slack.com/archives/C07RKRKTLDT)
|
@@ -36,14 +33,15 @@ own script**
|
|
36
33
|
|
37
34
|
* [Installation](#installation)
|
38
35
|
* [Usage](#usage)
|
39
|
-
|
40
|
-
|
36
|
+
* [Example](#example)
|
37
|
+
* [Handling Errors](#handling-errors)
|
41
38
|
* [Development](#development)
|
42
|
-
|
43
|
-
|
39
|
+
* [How this gem works](#how-this-gem-works)
|
40
|
+
* [Debugging](#debugging)
|
41
|
+
* [Releasing](#releasing)
|
44
42
|
* [Contributing](#contributing)
|
45
|
-
|
46
|
-
|
43
|
+
* [Commit message guidelines](#commit-message-guidelines)
|
44
|
+
* [Pull request guidelines](#pull-request-guidelines)
|
47
45
|
* [License](#license)
|
48
46
|
* [Code of Conduct](#code-of-conduct)
|
49
47
|
|
@@ -81,6 +79,55 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
81
79
|
workflow. You can also run `bin/console` for an interactive prompt that will allow
|
82
80
|
you to experiment.
|
83
81
|
|
82
|
+
### How this gem works
|
83
|
+
|
84
|
+
1. The user runs the `bundler gem-bytes` command from the command line, passing the
|
85
|
+
path or URL to a GemBytes script:
|
86
|
+
|
87
|
+
```shell
|
88
|
+
bundler gem-bytes [SCRIPT]
|
89
|
+
```
|
90
|
+
|
91
|
+
2. The `plugins.rb` file (in the root directory of this project) defines
|
92
|
+
`Bundler::GemBytes::BundlerCommand` class as the handler for the `gem-bytes`
|
93
|
+
bundler command:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
require 'bundler/gem_bytes'
|
97
|
+
|
98
|
+
# Register Bundler::GemBytes::BundlerCommand as the handler for the `gem-bytes`
|
99
|
+
# bundler command
|
100
|
+
|
101
|
+
Bundler::Plugin::API.command('gem-bytes', Bundler::GemBytes::BundlerCommand)
|
102
|
+
```
|
103
|
+
|
104
|
+
3. Bundler invokes the gem-bytes plugin by creating an instance of
|
105
|
+
`Bundler::GemBytes::BundlerCommand` and then calling `#exec(command, args)` on
|
106
|
+
that instance. Where:
|
107
|
+
|
108
|
+
* `command` is the bundler command given on the command line. It will always be
|
109
|
+
"gem-bytes".
|
110
|
+
* `args` is the array of any other arguments given on the command line after the
|
111
|
+
command. In this case, we expect the script path or URI.
|
112
|
+
|
113
|
+
4. The `BundlerCommand` instance creates a `Bundler::GemBytes::ScriptExecutor`
|
114
|
+
instance and calls `#execute(path_or_uri)` on that instance. This method in turn
|
115
|
+
calls `Thor::Actions#apply(path_or_uri)` to load the external script and execute
|
116
|
+
it in the context of the `ScriptExecutor` instance.
|
117
|
+
|
118
|
+
The `#apply` method, part of the `Thor::Actions` module, loads and executes the
|
119
|
+
script within the context of the `ScriptExecutor` instance.
|
120
|
+
|
121
|
+
If an error occurs during script execution, `BundlerCommand` catches the error, outputs an error message to `stderr`, and exits with a status code of `1`.
|
122
|
+
|
123
|
+
5. The `ScriptExecutor` class provides the environment/binding in which the GemBytes
|
124
|
+
script is executed, allowing the script to use instance methods and context from
|
125
|
+
`ScriptExecutor`. In addition to core Ruby and Active Support, the API available
|
126
|
+
to this script includes methods from both the
|
127
|
+
[`Thor::Actions`](https://github.com/rails/thor/wiki/Actions) and
|
128
|
+
`Bundler::GemBytes::Actions` modules, which provide utilities for file
|
129
|
+
manipulation, template generation, and other tasks essential for script execution.
|
130
|
+
|
84
131
|
### Debugging
|
85
132
|
|
86
133
|
To debug this gem it is recommended that you create a test project and install
|
@@ -100,7 +147,9 @@ BUNDLE_IGNORE_CONFIG=TRUE bundle plugin install --path ../.. bundler-gem_bytes
|
|
100
147
|
|
101
148
|
# 4. Create a gembytes script to add a development dependency on rubocop
|
102
149
|
cat <<SCRIPT > gem_bytes_script.rb
|
103
|
-
|
150
|
+
gemspec do
|
151
|
+
add_development_dependency "rubocop", "~> 1.68"
|
152
|
+
end
|
104
153
|
SCRIPT
|
105
154
|
|
106
155
|
# 5. Modify code, set breakpoints, or add binding.{irb|pry} calls to the source
|
@@ -173,3 +222,36 @@ License](https://opensource.org/licenses/MIT).
|
|
173
222
|
Everyone interacting in the Bundler::GemBytes project's codebases, issue trackers,
|
174
223
|
chat rooms and mailing lists is expected to follow the [code of
|
175
224
|
conduct](https://github.com/main-branch/bundler-gem_bytes/blob/main/CODE_OF_CONDUCT.md).
|
225
|
+
|
226
|
+
gemspec path do |spec_var, spec|
|
227
|
+
add_dependency 'example', '~> 1.1'
|
228
|
+
add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4'
|
229
|
+
add_development_dependency "rubocop", "~> 1.68"
|
230
|
+
|
231
|
+
remove_dependency "example"
|
232
|
+
|
233
|
+
attr "description", "#{spec.description}. Enhanced by GemBytes."
|
234
|
+
attr "author", ENV['USER']
|
235
|
+
attr "files", "Dir['lib/**/*.rb'] + Dir['bin/*']", quote: false
|
236
|
+
attr "authors", <% spec.authors %>.append('GemBytes').inspect, quote: false
|
237
|
+
|
238
|
+
remove_attr "license"
|
239
|
+
|
240
|
+
metadata "homepage_url", "https://github.com/example/"
|
241
|
+
|
242
|
+
remove_metadata "wiki_uri"
|
243
|
+
|
244
|
+
in_block("if RUBY_PLATFORM != 'java'", "end") do
|
245
|
+
development_dependency 'redcarpet', '~> 3.5'
|
246
|
+
development_dependency 'yard', '~> 0.9'
|
247
|
+
development_dependency 'yardstick', '~> 0.9'
|
248
|
+
end
|
249
|
+
|
250
|
+
code <<~CODE
|
251
|
+
if RUBY_PLATFORM != 'java'
|
252
|
+
<%= spec_var %>.add_development_dependency 'redcarpet', '~> 3.5'
|
253
|
+
<%= spec_var %>.add_development_dependency 'yard', '~> 0.9'
|
254
|
+
<%= spec_var %>.add_development_dependency 'yardstick', '~> 0.9'
|
255
|
+
end
|
256
|
+
CODE
|
257
|
+
end
|
data/Rakefile
CHANGED
@@ -25,6 +25,13 @@ rescue Bundler::BundlerError => e
|
|
25
25
|
exit e.status_code
|
26
26
|
end
|
27
27
|
|
28
|
+
# Make it so that calling `rake release` just calls `rake release:rubygems_push` to
|
29
|
+
# avoid creating and pushing a new tag.
|
30
|
+
|
31
|
+
Rake::Task['release'].clear
|
32
|
+
desc 'Customized release task to avoid creating a new tag'
|
33
|
+
task release: 'release:rubygem_push'
|
34
|
+
|
28
35
|
CLEAN << 'pkg'
|
29
36
|
CLOBBER << 'Gemfile.lock'
|
30
37
|
|
@@ -56,6 +63,7 @@ unless RUBY_PLATFORM == 'java'
|
|
56
63
|
|
57
64
|
YARD::Rake::YardocTask.new(:build) do |t|
|
58
65
|
t.files = %w[lib/**/*.rb examples/**/*]
|
66
|
+
t.options = ['--markup-provider', 'redcarpet', '--markup', 'markdown']
|
59
67
|
t.stats_options = ['--list-undoc']
|
60
68
|
end
|
61
69
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser/current'
|
4
|
+
require 'rubocop-ast'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module GemBytes
|
9
|
+
module Actions
|
10
|
+
class Gemspec < Parser::TreeRewriter
|
11
|
+
# Holds the components of a attribute: a name and a value
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
#
|
15
|
+
# Attributes in the gemspec look like the following:
|
16
|
+
#
|
17
|
+
# ```ruby
|
18
|
+
# Gem::Specification.new do |spec|
|
19
|
+
# spec.name = 'test'
|
20
|
+
# end
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
# @!attribute [r] name
|
24
|
+
# The name of the attribute
|
25
|
+
# @example
|
26
|
+
# attribute.name #=> "test"
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
# @api public
|
30
|
+
#
|
31
|
+
# @!attribute [r] value
|
32
|
+
# The value of the attribute expressed as an AST tree
|
33
|
+
# @example
|
34
|
+
# attribute.value.to_sexp #=> 's(:str, "my_description")'
|
35
|
+
# @return [Parser::AST::Node]
|
36
|
+
# @api public
|
37
|
+
#
|
38
|
+
# @!method to_a()
|
39
|
+
# Converts the attribute into an array
|
40
|
+
# @example
|
41
|
+
# dependency.to_a #=> ["description", [:str, "my_description"]]
|
42
|
+
# @return [Array]
|
43
|
+
# @api public
|
44
|
+
#
|
45
|
+
Attribute = Struct.new(:name, :value) do
|
46
|
+
def to_a = [name, value.to_sexp_array]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser/current'
|
4
|
+
require 'rubocop-ast'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module GemBytes
|
9
|
+
module Actions
|
10
|
+
class Gemspec < Parser::TreeRewriter
|
11
|
+
# Maps a dependency declaration to the AST that represents it
|
12
|
+
# @api public
|
13
|
+
#
|
14
|
+
# @!attribute [r] node
|
15
|
+
# The AST node for the attribute
|
16
|
+
# @example
|
17
|
+
# attribute_node.node #=> ...
|
18
|
+
# @return [Parser::AST::Node]
|
19
|
+
# @api public
|
20
|
+
#
|
21
|
+
# @!attribute [r] attribute
|
22
|
+
# The components of the attribute from the AST node
|
23
|
+
# @example
|
24
|
+
# attribute_node.attribute.to_a #=> ["description", [:str, "My deescription"]]
|
25
|
+
# @return [Dependency]
|
26
|
+
# @api public
|
27
|
+
#
|
28
|
+
AttributeNode = Struct.new(:node, :attribute)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser/current'
|
4
|
+
require 'rubocop-ast'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module GemBytes
|
9
|
+
module Actions
|
10
|
+
class Gemspec < Parser::TreeRewriter
|
11
|
+
# Remove a dependency in a gemspec
|
12
|
+
#
|
13
|
+
# If a dependency on the given gem is not found, this action does nothing.
|
14
|
+
#
|
15
|
+
# If one or more dependencies are found on the same gem as gem_name,
|
16
|
+
# the are removed from the gemspec.
|
17
|
+
#
|
18
|
+
# The gemspec is updated via calls to the tree_rewriter object.
|
19
|
+
#
|
20
|
+
# @!attribute [r] tree_rewriter
|
21
|
+
# The object that updates the source
|
22
|
+
# @return [Parser::TreeRewriter]
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
# @!attribute [r] gemspec_block
|
26
|
+
# The root AST node of the Gem::Specification block from the gemspec
|
27
|
+
# @return [Parser::AST::Node]
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
# @!attribute [r] receiver_name
|
31
|
+
# The name of the receiver for the Gem::Specification block
|
32
|
+
# @return [Symbol]
|
33
|
+
# @api private
|
34
|
+
#
|
35
|
+
# @!attribute [r] dependencies
|
36
|
+
# The dependency declarations found in the gemspec file
|
37
|
+
# @return [Array<DependencyNode>]
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
# @!attribute [r] gem_name
|
41
|
+
# The name of the gem to remove dependency on
|
42
|
+
# @return [String]
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
class DeleteDependency
|
47
|
+
# Initializes the delete dependency action
|
48
|
+
# @param tree_rewriter [Parser::TreeRewriter] The object that updates the source
|
49
|
+
# @param gemspec_block [Parser::AST::Node] The Gem::Specification block
|
50
|
+
# @param receiver_name [Symbol] The name of the receiver for the Gem::Specification block
|
51
|
+
# @param dependencies [Array<DependencyNode>] The dependency declarations found in the gemspec file
|
52
|
+
# @api private
|
53
|
+
def initialize(tree_rewriter, gemspec_block, receiver_name, dependencies)
|
54
|
+
@tree_rewriter = tree_rewriter
|
55
|
+
@gemspec_block = gemspec_block
|
56
|
+
@receiver_name = receiver_name
|
57
|
+
@dependencies = dependencies
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :tree_rewriter, :gemspec_block, :receiver_name, :dependencies, :gem_name
|
61
|
+
|
62
|
+
# Adds or updates a dependency to the Gem::Specification block
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# delete_dependency = DeleteDependency.new(tree_rewriter, gemspec_block, receiver_name, dependencies)
|
66
|
+
# gem_name = 'rubocop'
|
67
|
+
# depete_dependency.call(gem_name)
|
68
|
+
# @param gem_name [String] The name of the gem to remove dependency on
|
69
|
+
# @return [void]
|
70
|
+
# @api public
|
71
|
+
def call(gem_name)
|
72
|
+
@gem_name = gem_name
|
73
|
+
matching_dependencies = dependencies.select { |d| d.dependency.gem_name == gem_name }
|
74
|
+
|
75
|
+
delete_dependencies(matching_dependencies) if matching_dependencies.any?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Removes the matching dependencies from the gemspec
|
79
|
+
# @param matching_dependencies [Array<DependencyNode>] The existing dependencies that match gem_name
|
80
|
+
# @return [void]
|
81
|
+
# @api private
|
82
|
+
def delete_dependencies(matching_dependencies)
|
83
|
+
matching_dependencies.each do |found_dependency|
|
84
|
+
tree_rewriter.replace(full_line_range(found_dependency), '')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Expand the range for a node to include any leading whitespace and newline
|
91
|
+
# @param dependency_node [DependencyNode] The node to remove
|
92
|
+
# @return [Parser::Source::Range] The range of the whole line including whitespace
|
93
|
+
# @api private
|
94
|
+
def full_line_range(dependency_node)
|
95
|
+
range = dependency_node.node.loc.expression
|
96
|
+
source_buffer = range.source_buffer
|
97
|
+
# The whole line including leading and trailing whitespace
|
98
|
+
line_range = source_buffer.line_range(range.line)
|
99
|
+
# Expand the range to include the leading newline
|
100
|
+
line_range.with(begin_pos: line_range.begin_pos - 1)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser/current'
|
4
|
+
require 'rubocop-ast'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module GemBytes
|
9
|
+
module Actions
|
10
|
+
class Gemspec < Parser::TreeRewriter
|
11
|
+
# Holds the components of a dependency declaration
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
#
|
15
|
+
# A dependency declaration is a call to `add_dependency`,
|
16
|
+
# `add_runtime_dependency`, or `add_development_dependency` in the
|
17
|
+
# Gem::Specification block.
|
18
|
+
#
|
19
|
+
# For example, the following is a dependency declaration:
|
20
|
+
#
|
21
|
+
# ```ruby
|
22
|
+
# spec.add_dependency 'rubocop', '~> 1.0'
|
23
|
+
# ```
|
24
|
+
#
|
25
|
+
# Would be represented by a Dependency object with the following
|
26
|
+
# attributes:
|
27
|
+
# * method_name: :add_dependency
|
28
|
+
# * gem_name: 'rubocop'
|
29
|
+
# * version_constraint: '~> 1.0'
|
30
|
+
#
|
31
|
+
# @!attribute [r] method_name
|
32
|
+
# The name of the method called to add the dependency
|
33
|
+
#
|
34
|
+
# Must be one of the following:
|
35
|
+
#
|
36
|
+
# * :add_dependency
|
37
|
+
# * :add_runtime_dependency
|
38
|
+
# * :add_development_dependency
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# dependency.method_name #=> :add_dependency
|
42
|
+
#
|
43
|
+
# @return [Symbol]
|
44
|
+
# @api public
|
45
|
+
#
|
46
|
+
# @!attribute [r] gem_name
|
47
|
+
# The name of the gem being depended on
|
48
|
+
# @example
|
49
|
+
# dependency.gem_name #=> "rubocop"
|
50
|
+
# @return [String]
|
51
|
+
# @api public
|
52
|
+
#
|
53
|
+
# @!attribute [r] version_constraints
|
54
|
+
# The version constraints for the dependency
|
55
|
+
# @example
|
56
|
+
# dependency.version_constraints #=> ["~> 1.68", ">= 1.68.3"]
|
57
|
+
# @return [Array<String>]
|
58
|
+
# @api public
|
59
|
+
#
|
60
|
+
# @!method to_a()
|
61
|
+
# Converts the dependency into an array
|
62
|
+
# @example
|
63
|
+
# dependency.to_a #=> [:add_runtime_dependency, "rubocop", "~> 1.68"]
|
64
|
+
# @return [Array]
|
65
|
+
# @api public
|
66
|
+
#
|
67
|
+
Dependency = Struct.new(:method_name, :gem_name, :version_constraints) do
|
68
|
+
def to_a = [method_name, gem_name, *version_constraints]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser/current'
|
4
|
+
require 'rubocop-ast'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module GemBytes
|
9
|
+
module Actions
|
10
|
+
class Gemspec < Parser::TreeRewriter
|
11
|
+
# Maps a dependency declaration to the AST that represents it
|
12
|
+
# @api public
|
13
|
+
#
|
14
|
+
# @!attribute [r] node
|
15
|
+
# The AST node for the dependency declaration
|
16
|
+
# @example
|
17
|
+
# dependency_node.node #=> ...
|
18
|
+
# @return [Parser::AST::Node]
|
19
|
+
# @api public
|
20
|
+
#
|
21
|
+
# @!attribute [r] dependency
|
22
|
+
# The components of the dependency declaration from the AST node
|
23
|
+
# @example
|
24
|
+
# dependency_node.dependency.to_a #=> [:add_dependency, 'rubocop', '~> 1.68']
|
25
|
+
# @return [Dependency]
|
26
|
+
# @api public
|
27
|
+
#
|
28
|
+
DependencyNode = Struct.new(:node, :dependency)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module GemBytes
|
5
|
+
module Actions
|
6
|
+
class Gemspec < Parser::TreeRewriter
|
7
|
+
# Holds information about the gemspec file
|
8
|
+
# @!attribute [r] gemspec_object
|
9
|
+
# @api private
|
10
|
+
class GemSpecification
|
11
|
+
# Create a new GemSpecification object
|
12
|
+
#
|
13
|
+
# @param source [String] The contents of the gemspec file
|
14
|
+
# @param source_path [String] The path to the gemspec file
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def initialize(source, source_path)
|
18
|
+
@source = source
|
19
|
+
@source_path = source_path
|
20
|
+
|
21
|
+
@dependencies = []
|
22
|
+
@attributes = []
|
23
|
+
|
24
|
+
@source_buffer, @source_ast = parse(source, source_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
# The contents of the gemspec file
|
28
|
+
# @example
|
29
|
+
# data.source # => "Gem::Specification.new do |spec|\n spec.name = 'example'\nend"
|
30
|
+
# @return [String]
|
31
|
+
attr_reader :source
|
32
|
+
|
33
|
+
# The path to the gemspec file (used for error reporting)
|
34
|
+
# @example
|
35
|
+
# data.source_path # => "example.gemspec"
|
36
|
+
# @return [String]
|
37
|
+
attr_reader :soure_path
|
38
|
+
|
39
|
+
# The source buffer used when updating the gemspec file
|
40
|
+
# @return [Parser::Source::Buffer]
|
41
|
+
# @api private
|
42
|
+
attr_reader :source_buffer
|
43
|
+
|
44
|
+
# The parsed AST for the gemspec file source
|
45
|
+
# @example
|
46
|
+
# data.source_ast # => (send nil :puts)
|
47
|
+
# @return [Parser::AST::Node]
|
48
|
+
attr_reader :source_ast
|
49
|
+
|
50
|
+
# The Gem::Specification object
|
51
|
+
# @example
|
52
|
+
# data.gemspec_object # => #<Gem::Specification:0x00007f9b1b8b3f10>
|
53
|
+
# @return [Gem::Specification]
|
54
|
+
def gemspec_object
|
55
|
+
@gemspec_object ||= load_gem_specification
|
56
|
+
end
|
57
|
+
|
58
|
+
# The name of the Gem::Specification object within the gemspec block
|
59
|
+
# @example When the gemspec block starts `Gem::Specification.new do |spec|`
|
60
|
+
# data.gemspec_object_name # => :spec
|
61
|
+
# @return [Symbol]
|
62
|
+
attr_accessor :gemspec_object_name
|
63
|
+
|
64
|
+
# The AST node for the Gem::Specification block within the source
|
65
|
+
# @return [Parser::AST::Node]
|
66
|
+
attr_accessor :gemspec_ast
|
67
|
+
|
68
|
+
# The dependencies found in the gemspec file
|
69
|
+
# @return [Array<Dependency>]
|
70
|
+
attr_reader :dependencies
|
71
|
+
|
72
|
+
# The attributes found in the gemspec file
|
73
|
+
# @return [Array<Attribute>]
|
74
|
+
attr_reader :attributes
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Parses the given code into an AST
|
79
|
+
# @param source [String] The code to parse
|
80
|
+
# @param source_path [String] The path to the file being parsed (used for error messages only)
|
81
|
+
# @return [Array<Parser::AST::Node, Parser::Source::Buffer>] The AST and buffer
|
82
|
+
# @api private
|
83
|
+
def parse(source, source_path)
|
84
|
+
source_buffer = Parser::Source::Buffer.new(source_path, source: source)
|
85
|
+
processed_source = RuboCop::AST::ProcessedSource.new(source, ruby_version, source_path)
|
86
|
+
unless processed_source.valid_syntax?
|
87
|
+
raise "Invalid syntax in #{source_path}\n#{processed_source.diagnostics.map(&:render).join("\n")}"
|
88
|
+
end
|
89
|
+
|
90
|
+
source_ast = processed_source.ast
|
91
|
+
[source_buffer, source_ast]
|
92
|
+
end
|
93
|
+
|
94
|
+
# The currently running Ruby version as a float (MAJOR.MINOR only)
|
95
|
+
#
|
96
|
+
# @return [Float] The Ruby version number, e.g., 3.0
|
97
|
+
# @api private
|
98
|
+
def ruby_version = RUBY_VERSION.match(/^(?<version>\d+\.\d+)/)['version'].to_f
|
99
|
+
|
100
|
+
# Load the gemspec file into a Gem::Specification object
|
101
|
+
# @return [Gem::Specification] The Gem::Specification object
|
102
|
+
# @api private
|
103
|
+
def load_gem_specification
|
104
|
+
# Store the current $LOAD_PATH
|
105
|
+
original_load_path = $LOAD_PATH.dup
|
106
|
+
|
107
|
+
# Temporarily add 'lib' to the $LOAD_PATH
|
108
|
+
lib_path = File.expand_path('lib', Dir.pwd)
|
109
|
+
$LOAD_PATH.unshift(lib_path)
|
110
|
+
|
111
|
+
# Evaluate the gemspec file
|
112
|
+
eval(source, binding, '.').tap do # rubocop:disable Security/Eval
|
113
|
+
# Restore the original $LOAD_PATH
|
114
|
+
$LOAD_PATH.replace(original_load_path)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|