appraisal2 3.0.9 → 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a27b04028f7284aace3c5b46e35cbbdd3279d7d493c1e6b95a8839931a98ae24
4
- data.tar.gz: 1a3f7d92cb6c41c9112cd037add5806bdfa7b4bab2576a95847b0e3c5395db2c
3
+ metadata.gz: f60cf90f788c79484aa1f01d218259c1cbb9459afe960a368e3de03bd84b7088
4
+ data.tar.gz: 8b96fc10c341d3c395e8e6c210ad4a3c98d36cc60d4e92c82ed13ce4cf774c72
5
5
  SHA512:
6
- metadata.gz: 78978be68901ed5a57ebbfbafeece3752834caedb337cb5e30a5d60eed5a8d89ea23ab5b1930746b5e0d59fd93487878ae5f0bee312fb30c6247bcf65f5e37e8
7
- data.tar.gz: 9828a29976bf915e45e23c5a13275efe1b2124ed4556bcff50e90922cde411ce85f3d46f7a3739c5433fe1387b40e028debd1a3918e1dd2944c2b90c63fc2180
6
+ metadata.gz: 967d0442aafbbead64cb127c57eab72ecc48f85d9d820619f6eb142ff2aaf3c7d6510bddd32c2c8f6524ea072ebfb7e7fc593719276b2f80259948aba1396c5e
7
+ data.tar.gz: 45bba8cce0a182a5eacb7614e0e239906d76f8905c9fe057ac8e91cdb013d17fe48e3ddd1c730b61c907c7c0f6ef1b10b42f1eab10063366a1dc72ff9cb47bbc
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,35 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [3.1.0] - 2026-06-06
34
+
35
+ - TAG: [v3.1.0][3.1.0t]
36
+ - COVERAGE: 90.14% -- 814/903 lines in 29 files
37
+ - BRANCH COVERAGE: 80.00% -- 148/185 branches in 29 files
38
+ - 42.70% documented
39
+
40
+ ### Added
41
+
42
+ - Added Appraisal lifecycle hooks, including `Appraisal.transform_gemfile`
43
+ for plugins that need to normalize generated Appraisal gemfiles in memory
44
+ before Appraisal2 writes them.
45
+ - Added explicit `generate-install` and `generate-update` CLI commands for
46
+ workflows that need to regenerate Appraisal gemfiles before resolving them.
47
+ - Added named appraisal support for `generate`, `generate-install`, and
48
+ `generate-update`.
49
+
50
+ ### Changed
51
+
52
+ - Changed `install` and `update` to resolve existing Appraisal gemfiles without
53
+ rewriting them, while still generating missing Appraisal gemfiles to preserve
54
+ the basic install workflow.
55
+ - Updated the deprecated `rake appraisal:install` task to delegate to
56
+ `appraisal generate-install`, preserving its historical generate-and-install
57
+ behavior.
58
+
59
+ - Documented the Appraisal2 3.1.0 command lifecycle changes, named appraisal
60
+ generation commands, and generated gemfile transform hooks in the README.
61
+
33
62
  ## [3.0.9] - 2026-06-05
34
63
 
35
64
  - TAG: [v3.0.9][3.0.9t]
@@ -316,7 +345,9 @@ Please file a bug if you notice a violation of semantic versioning.
316
345
  - code coverage tracked with Coveralls, QLTY.sh, and the kettle-soup-cover gem
317
346
  - other minor fixes and improvements
318
347
 
319
- [Unreleased]: https://github.com/appraisal-rb/appraisal2/compare/v3.0.9...HEAD
348
+ [Unreleased]: https://github.com/appraisal-rb/appraisal2/compare/v3.1.0...HEAD
349
+ [3.1.0]: https://github.com/appraisal-rb/appraisal2/compare/v3.0.9...v3.1.0
350
+ [3.1.0t]: https://github.com/appraisal-rb/appraisal2/releases/tag/v3.1.0
320
351
  [3.0.9]: https://github.com/appraisal-rb/appraisal2/compare/v3.0.8...v3.0.9
321
352
  [3.0.9t]: https://github.com/appraisal-rb/appraisal2/releases/tag/v3.0.9
322
353
  [3.0.8]: https://github.com/appraisal-rb/appraisal2/compare/v3.0.7...v3.0.8
data/README.md CHANGED
@@ -34,6 +34,14 @@ and [Joe Ferris](https://github.com/jferris), the original author!
34
34
  Appraisal2 adds:
35
35
 
36
36
  - support for `eval_gemfile`
37
+ - explicit `generate`, `install`, and `update` workflows, so CI can resolve
38
+ already-generated appraisal gemfiles without rewriting them
39
+ - `generate-install` and `generate-update` commands for workflows that need to
40
+ regenerate appraisal gemfiles before resolving dependencies
41
+ - named appraisal support for `generate`, `generate-install`, and
42
+ `generate-update`
43
+ - lifecycle hooks, including `Appraisal.transform_gemfile`, for plugins that
44
+ need to normalize generated appraisal gemfiles before Appraisal2 writes them
37
45
  - support for caching gems across appraisals in CI workflows by setting `BUNDLE_PATH` in env
38
46
  - support for [ORE](https://github.com/contriboss/ore-light) as an alternative gem manager (faster than bundler!)
39
47
  - For easy setup in **Gitea** [Actions](https://docs.gitea.com/usage/actions/overview), **Forgejo** [Actions](https://forgejo.org/docs/next/admin/actions/), **Codeberg** [Actions](https://docs.codeberg.org/ci/actions/), or **GitHub** [Actions](https://github.com/marketplace/actions/setup-ruby-with-rv-and-ore) check out [appraisal-rb/setup-ruby-flash](https://github.com/appraisal-rb/setup-ruby-flash)
@@ -53,7 +61,7 @@ Appraisal2 adds:
53
61
  | Works with Truffle Ruby | [![Truffle Ruby 22.3 Compat][💎truby-22.3i]][🚎truby-22.3-wf] [![Truffle Ruby 23.0 Compat][💎truby-23.0i]][🚎truby-23.0-wf] [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎truby-23.1-wf] <br/> [![Truffle Ruby 24.2 Compat][💎truby-24.2i]][🚎truby-24.2-wf] [![Truffle Ruby 25.0 Compat][💎truby-25.0i]][🚎truby-25.0-wf] [![Truffle Ruby current Compat][💎truby-c-i]][🚎9-t-wf]|
54
62
  | Works with MRI Ruby 4 | [![Ruby 4.0 Compat][💎ruby-4.0i]][🚎11-c-wf] [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]|
55
63
  | Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎ruby-3.0-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎ruby-3.1-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎ruby-3.2-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎ruby-3.3-wf] [![Ruby 3.4 Compat][💎ruby-3.4i]][🚎ruby-3.4-wf]|
56
- | Works with MRI Ruby 2 | ![Ruby 2.0 Compat][💎ruby-2.0i] ![Ruby 2.1 Compat][💎ruby-2.1i] ![Ruby 2.2 Compat][💎ruby-2.2i] <br/> [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎ruby-2.4-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎ruby-2.5-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎ruby-2.6-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎ruby-2.7-wf]|
64
+ | Works with MRI Ruby 2 | ![Ruby 2.0 Compat][💎ruby-2.0i] ![Ruby 2.1 Compat][💎ruby-2.1i] ![Ruby 2.2 Compat][💎ruby-2.2i] ![Ruby 2.3 Compat][💎ruby-2.3i] <br/> [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎ruby-2.4-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎ruby-2.5-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎ruby-2.6-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎ruby-2.7-wf]|
57
65
  | Works with MRI Ruby 1 | ![Ruby 1.9 Compat][💎ruby-1.9i]|
58
66
  | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
59
67
  | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
@@ -130,6 +138,46 @@ gem install appraisal2
130
138
 
131
139
  ## ⚙️ Configuration
132
140
 
141
+ Create an `Appraisals` file at the root of your project, then define one or
142
+ more dependency scenarios:
143
+
144
+ ```ruby
145
+ appraise "rails-7" do
146
+ gem "rails", "~> 7.0"
147
+ end
148
+
149
+ appraise "rails-8" do
150
+ gem "rails", "~> 8.0"
151
+ end
152
+ ```
153
+
154
+ Each appraisal starts from your root `Gemfile`, then applies the dependency
155
+ changes declared in the matching `appraise` block. Generated appraisal gemfiles
156
+ are written to `gemfiles/*.gemfile`.
157
+
158
+ ### Generated Gemfile Hooks
159
+
160
+ Appraisal2 3.1.0 adds lifecycle hooks for companion gems and local tooling.
161
+ The primary hook is `Appraisal.transform_gemfile`, which receives generated
162
+ gemfile content before Appraisal2 writes it.
163
+
164
+ ```ruby
165
+ Appraisal.transform_gemfile do |content, context|
166
+ # context.appraisal is the Appraisal::Appraisal instance.
167
+ # context.path is the generated gemfile path.
168
+ content.gsub(%(source "https://rubygems.org"), %(source "https://gem.coop"))
169
+ end
170
+ ```
171
+
172
+ Hooks run in memory. Appraisal2 writes the final transformed content once, after
173
+ all registered transforms have run. A transform may accept only `content`, or it
174
+ may accept `content, context`. Returning `nil` leaves the current content
175
+ unchanged.
176
+
177
+ This is intended for plugin gems that need deterministic generated output, such
178
+ as style normalization of appraisal gemfiles, without monkey-patching Appraisal2
179
+ internals.
180
+
133
181
  ## 🔧 Basic Usage
134
182
 
135
183
  Once you've configured the appraisals you want to use, you need to install the
@@ -137,9 +185,17 @@ dependencies for each appraisal:
137
185
 
138
186
  $ bundle exec appraisal install
139
187
 
140
- This will resolve, install, and lock the dependencies for that appraisal using
141
- bundler. Once you have your dependencies set up, you can run any command in a
142
- single appraisal:
188
+ This resolves, installs, and locks dependencies for each generated appraisal
189
+ gemfile using bundler. If an appraisal gemfile is missing, `install` generates
190
+ that missing gemfile first, preserving the basic setup workflow.
191
+
192
+ When you intentionally want to regenerate every appraisal gemfile before
193
+ installing dependencies, use:
194
+
195
+ $ bundle exec appraisal generate-install
196
+
197
+ Once you have your dependencies set up, you can run any command in a single
198
+ appraisal:
143
199
 
144
200
  $ bundle exec appraisal rails-3 rake test
145
201
 
@@ -173,40 +229,65 @@ default.
173
229
  ```bash
174
230
  appraisal clean # Remove all generated gemfiles and lockfiles from gemfiles folder
175
231
  appraisal generate # Generate a gemfile for each appraisal
232
+ appraisal generate-install # Generate gemfiles, then resolve and install dependencies
233
+ appraisal generate-update [LIST_OF_GEMS] # Generate gemfiles, then update dependencies
176
234
  appraisal help [COMMAND] # Describe available commands or one specific command
177
- appraisal install # Resolve and install dependencies for each appraisal
235
+ appraisal install # Resolve and install dependencies for each generated appraisal gemfile
178
236
  appraisal list # List the names of the defined appraisals
179
- appraisal update [LIST_OF_GEMS] # Remove all generated gemfiles and lockfiles, resolve, and install dependencies again
237
+ appraisal update [LIST_OF_GEMS] # Update dependencies for each generated appraisal gemfile
180
238
  appraisal version # Display the version and exit
181
239
  ```
182
240
 
241
+ Since Appraisal2 3.1.0, `install` and `update` do not rewrite existing appraisal
242
+ gemfiles. They operate on the generated files already present under `gemfiles/`.
243
+ This matters in CI and plugin workflows where generated gemfiles may be
244
+ normalized by hooks or committed as stable inputs.
245
+
246
+ Use the command that matches the lifecycle you want:
247
+
248
+ | Command | Regenerates appraisal gemfiles? | Resolves dependencies? | Typical use |
249
+ |---------|---------------------------------|-------------------------|-------------|
250
+ | `generate` | Yes | No | Refresh generated gemfiles after editing `Appraisals` |
251
+ | `install` | Only missing gemfiles | Yes, via install | CI or local setup using existing generated gemfiles |
252
+ | `update` | Only missing gemfiles | Yes, via update | Refresh lockfiles/dependencies using existing generated gemfiles |
253
+ | `generate-install` | Yes | Yes, via install | First setup, or after intentional Appraisals changes |
254
+ | `generate-update` | Yes | Yes, via update | Regenerate gemfiles, then update dependency locks |
255
+
256
+ The deprecated rake task `rake appraisal:install` now delegates to
257
+ `appraisal generate-install`, preserving its historical generate-and-install
258
+ behavior while the CLI commands remain explicit.
259
+
183
260
  ### Command Options
184
261
 
185
- The `install` and `update` **built-in commands** support several options:
262
+ Built-in dependency commands support the following options:
186
263
 
187
- **Important:** These options apply **only** to Appraisal's `install` and `update` commands.
188
- They do **not** apply when running external commands like `bundle install` or `bundle update`.
264
+ **Important:** These options apply **only** to Appraisal's built-in dependency
265
+ commands. They do **not** apply when running external commands like
266
+ `bundle install` or `bundle update`.
189
267
 
190
268
  | Option | Description |
191
269
  |--------|-------------|
192
- | `--gem-manager`, `-g` | Gem manager to use: `bundler` (default) or `ore` |
193
- | `--jobs`, `-j` | Install gems in parallel using the given number of workers |
194
- | `--retry` | Retry network and git requests that have failed (default: 1) |
195
- | `--without` | A space-separated list of groups to skip during installation |
196
- | `--full-index` | Run bundle install with the full-index argument |
197
- | `--path` | Install gems in the specified directory |
270
+ | `--gem-manager`, `-g` | Gem manager to use: `bundler` (default) or `ore`; applies to `install`, `update`, `generate-install`, and `generate-update` |
271
+ | `--jobs`, `-j` | Install gems in parallel using the given number of workers; applies to `install` and `generate-install` |
272
+ | `--retry` | Retry network and git requests that have failed; applies to `install` and `generate-install` (default: 1) |
273
+ | `--without` | A space-separated list of groups to skip during installation; applies to `install` and `generate-install` |
274
+ | `--full-index` | Run bundle install with the full-index argument; applies to `install` and `generate-install` |
275
+ | `--path` | Install gems in the specified directory; applies to `install` and `generate-install` |
198
276
 
199
277
  ### Using Commands with Named Appraisals
200
278
 
201
279
  #### Using Appraisal's built-in commands with named appraisals
202
280
 
203
- When using Appraisal's `install` or `update` commands with a specific appraisal name,
204
- place the appraisal name first, then the command, then any options:
281
+ When using Appraisal's built-in commands with a specific appraisal name, place
282
+ the appraisal name first, then the command, then any options:
205
283
 
206
284
  ```bash
207
285
  # ✅ Correct order: appraisal <NAME> <COMMAND> [OPTIONS]
286
+ bundle exec appraisal rails-7 generate
208
287
  bundle exec appraisal rails-7 install --gem-manager=ore
288
+ bundle exec appraisal rails-7 generate-install --gem-manager=ore
209
289
  bundle exec appraisal rails-7 update rails --gem-manager=ore
290
+ bundle exec appraisal rails-7 generate-update rails --gem-manager=ore
210
291
  bundle exec appraisal rails-7 install --jobs=4
211
292
 
212
293
  # ❌ Wrong order (won't work)
@@ -219,6 +300,9 @@ More examples with Appraisal's built-in commands:
219
300
  # Install dependencies for a specific appraisal
220
301
  bundle exec appraisal rails-7 install
221
302
 
303
+ # Regenerate and install dependencies for a specific appraisal
304
+ bundle exec appraisal rails-7 generate-install
305
+
222
306
  # Install with options
223
307
  bundle exec appraisal rails-7 install --gem-manager=ore --jobs=4
224
308
 
@@ -228,6 +312,10 @@ bundle exec appraisal rails-7 update
228
312
  # Update specific gems in a specific appraisal
229
313
  bundle exec appraisal rails-7 update rails rack
230
314
  bundle exec appraisal rails-7 update rails rack --gem-manager=ore
315
+
316
+ # Regenerate before updating a specific appraisal
317
+ bundle exec appraisal rails-7 generate-update
318
+ bundle exec appraisal rails-7 generate-update rails rack
231
319
  ```
232
320
 
233
321
  #### Running external commands with named appraisals
@@ -612,6 +700,7 @@ Thanks for RTFM. ☺️
612
700
  [💎ruby-2.0i]: https://img.shields.io/badge/Ruby-2.0_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
613
701
  [💎ruby-2.1i]: https://img.shields.io/badge/Ruby-2.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
614
702
  [💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
703
+ [💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white
615
704
  [💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
616
705
  [💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
617
706
  [💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge&logo=ruby&logoColor=white
@@ -661,7 +750,7 @@ Thanks for RTFM. ☺️
661
750
  [📌gitmoji]: https://gitmoji.dev
662
751
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
663
752
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
664
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.834-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
753
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.894-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
665
754
  [🔐security]: https://github.com/appraisal-rb/appraisal2/blob/main/SECURITY.md
666
755
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
667
756
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -689,7 +778,7 @@ Thanks for RTFM. ☺️
689
778
  | Package | appraisal2 |
690
779
  | Description | 🔍️ Appraisal2 integrates with bundler and rake to test your library against different versions of dependencies in repeatable scenarios called "appraisals." |
691
780
  | Homepage | https://github.com/appraisal-rb/appraisal2 |
692
- | Source | https://github.com/appraisal-rb/appraisal2/tree/v3.0.8 |
781
+ | Source | https://github.com/appraisal-rb/appraisal2/tree/v3.1.0 |
693
782
  | License | `MIT` |
694
783
  | Funding | https://github.com/sponsors/pboling, https://issuehunt.io/u/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/appraisal-rb, https://patreon.com/galtzo, https://polar.sh/pboling, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/appraisal2, https://www.buymeacoffee.com/pboling |
695
784
  <!-- kettle-jem:metadata:end -->
data/SECURITY.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |----------|-----------|
7
- | 3.0.latest | ✅ |
7
+ | 3.1.latest | ✅ |
8
8
 
9
9
  ## Security contact information
10
10
 
@@ -8,6 +8,7 @@ require "pathname"
8
8
  require "appraisal/gemfile"
9
9
  require "appraisal/command"
10
10
  require "appraisal/customize"
11
+ require "appraisal/hooks"
11
12
  require "appraisal/utils"
12
13
  require "appraisal/gem_manager"
13
14
 
@@ -70,14 +71,14 @@ module Appraisal
70
71
  end
71
72
 
72
73
  def write_gemfile
74
+ content = ::Appraisal::Hooks.run_transform_gemfile(self, gemfile_path, generated_gemfile)
73
75
  File.open(gemfile_path, "w") do |file|
74
- signature =
75
- Customize.heading(self) || "This file was generated by Appraisal2"
76
- file.puts([comment_lines(signature), quoted_gemfile].join("\n\n"))
76
+ file.write(content)
77
77
  end
78
78
  end
79
79
 
80
80
  def install(options = {})
81
+ write_gemfile unless File.exist?(gemfile_path)
81
82
  gem_manager = options.delete(:gem_manager) || options.delete("gem_manager") ||
82
83
  options.delete(:"gem-manager") || options.delete("gem-manager")
83
84
  manager = GemManager::Factory.create(gemfile_path, project_root, :manager => gem_manager)
@@ -85,6 +86,7 @@ module Appraisal
85
86
  end
86
87
 
87
88
  def update(gems = [], options = {})
89
+ write_gemfile unless File.exist?(gemfile_path)
88
90
  gem_manager = options[:gem_manager] || options["gem_manager"] ||
89
91
  options[:"gem-manager"] || options["gem-manager"]
90
92
  manager = GemManager::Factory.create(gemfile_path, project_root, :manager => gem_manager)
@@ -136,6 +138,12 @@ module Appraisal
136
138
  name.gsub(/[^\w.]/, "_")
137
139
  end
138
140
 
141
+ def generated_gemfile
142
+ signature =
143
+ Customize.heading(self) || "This file was generated by Appraisal2"
144
+ [comment_lines(signature), quoted_gemfile].join("\n\n") + "\n"
145
+ end
146
+
139
147
  def comment_lines(heading)
140
148
  heading.lines.map do |line|
141
149
  if line.lstrip.empty?
data/lib/appraisal/cli.rb CHANGED
@@ -53,7 +53,7 @@ module Appraisal
53
53
  end
54
54
  end
55
55
 
56
- desc "install", "Resolve and install dependencies for each appraisal"
56
+ desc "install", "Resolve and install dependencies for each generated appraisal gemfile"
57
57
  method_option "jobs",
58
58
  :aliases => "j",
59
59
  :type => :numeric,
@@ -78,8 +78,6 @@ module Appraisal
78
78
  "Bundler will remember this option."
79
79
 
80
80
  def install
81
- invoke(:generate, [], {})
82
-
83
81
  install_options = options.to_h
84
82
  AppraisalFile.each do |appraisal|
85
83
  appraisal.install(install_options)
@@ -87,6 +85,34 @@ module Appraisal
87
85
  end
88
86
  end
89
87
 
88
+ desc "generate-install", "Generate gemfiles, then resolve and install dependencies for each appraisal"
89
+ method_option "jobs",
90
+ :aliases => "j",
91
+ :type => :numeric,
92
+ :default => 1,
93
+ :banner => "SIZE",
94
+ :desc => "Install gems in parallel using the given number of workers."
95
+ method_option "retry",
96
+ :type => :numeric,
97
+ :default => 1,
98
+ :desc => "Retry network and git requests that have failed"
99
+ method_option "without",
100
+ :banner => "GROUP_NAMES",
101
+ :desc => "A space-separated list of groups referencing gems to skip " \
102
+ "during installation. Bundler will remember this option."
103
+ method_option "full-index",
104
+ :type => :boolean,
105
+ :desc => "Run bundle install with the " \
106
+ "full-index argument."
107
+ method_option "path",
108
+ :type => :string,
109
+ :desc => "Install gems in the specified directory. " \
110
+ "Bundler will remember this option."
111
+ def generate_install
112
+ invoke(:generate, [], {})
113
+ invoke(:install, [], options.to_h)
114
+ end
115
+
90
116
  desc "generate", "Generate a gemfile for each appraisal"
91
117
  def generate
92
118
  AppraisalFile.each do |appraisal|
@@ -99,10 +125,8 @@ module Appraisal
99
125
  FileUtils.rm_f(Dir["gemfiles/*.{gemfile,gemfile.lock}"])
100
126
  end
101
127
 
102
- desc "update [LIST_OF_GEMS]", "Remove all generated gemfiles and lockfiles, resolve, and install dependencies again"
128
+ desc "update [LIST_OF_GEMS]", "Update dependencies for each generated appraisal gemfile"
103
129
  def update(*gems)
104
- invoke(:generate, [], {})
105
-
106
130
  gem_manager = options["gem-manager"] || options[:gem_manager]
107
131
  update_options = gem_manager ? {:gem_manager => gem_manager} : {}
108
132
  AppraisalFile.each do |appraisal|
@@ -110,6 +134,12 @@ module Appraisal
110
134
  end
111
135
  end
112
136
 
137
+ desc "generate-update [LIST_OF_GEMS]", "Generate gemfiles, then update dependencies for each appraisal"
138
+ def generate_update(*gems)
139
+ invoke(:generate, [], {})
140
+ update(*gems)
141
+ end
142
+
113
143
  desc "list", "List the names of the defined appraisals"
114
144
  def list
115
145
  AppraisalFile.new.appraisals.each { |appraisal| puts appraisal.name }
@@ -132,8 +162,10 @@ module Appraisal
132
162
  # This handles cases where Thor doesn't pass arguments
133
163
  actual_args = (args.empty? && ARGV.any?) ? ARGV.dup : args
134
164
 
135
- # Check if the first argument is a Thor command (install or update)
136
- if actual_args.first == "install"
165
+ # Check if the first argument is an appraisal subcommand.
166
+ if actual_args.first == "generate"
167
+ matching_appraisal.write_gemfile
168
+ elsif actual_args.first == "install"
137
169
  # Extract Thor options from the remaining arguments
138
170
  # Filter out the command name and pass options to install
139
171
  filtered_args = actual_args[1..-1] || []
@@ -145,6 +177,15 @@ module Appraisal
145
177
  gem_manager = options["gem-manager"] || options[:gem_manager]
146
178
  parsed_options[:gem_manager] = gem_manager if gem_manager && !parsed_options.key?(:gem_manager)
147
179
 
180
+ matching_appraisal.install(parsed_options)
181
+ matching_appraisal.relativize
182
+ elsif actual_args.first == "generate-install"
183
+ filtered_args = actual_args[1..-1] || []
184
+ parsed_options = parse_external_options(filtered_args)
185
+
186
+ gem_manager = options["gem-manager"] || options[:gem_manager]
187
+ parsed_options[:gem_manager] = gem_manager if gem_manager && !parsed_options.key?(:gem_manager)
188
+
148
189
  matching_appraisal.write_gemfile
149
190
  matching_appraisal.install(parsed_options)
150
191
  matching_appraisal.relativize
@@ -157,6 +198,15 @@ module Appraisal
157
198
  gem_manager = options["gem-manager"] || options[:gem_manager]
158
199
  parsed_options[:gem_manager] = gem_manager if gem_manager && !parsed_options.key?(:gem_manager)
159
200
 
201
+ matching_appraisal.update(gems, parsed_options)
202
+ elsif actual_args.first == "generate-update"
203
+ filtered_args = actual_args[1..-1] || []
204
+ gems, parsed_options = extract_gems_and_options(filtered_args)
205
+
206
+ gem_manager = options["gem-manager"] || options[:gem_manager]
207
+ parsed_options[:gem_manager] = gem_manager if gem_manager && !parsed_options.key?(:gem_manager)
208
+
209
+ matching_appraisal.write_gemfile
160
210
  matching_appraisal.update(gems, parsed_options)
161
211
  else
162
212
  # Run as an external command
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appraisal
4
+ # Lifecycle hooks used by companion gems to extend Appraisal without
5
+ # monkey-patching command or generation internals.
6
+ module Hooks
7
+ GEMFILE_TRANSFORM_MUTEX = Mutex.new
8
+ GEMFILE_TRANSFORMS = [].freeze
9
+
10
+ class GemfileContext
11
+ attr_accessor :content
12
+ attr_reader :appraisal, :path
13
+
14
+ def initialize(appraisal, path, content)
15
+ @appraisal = appraisal
16
+ @path = path
17
+ @content = content
18
+ end
19
+ end
20
+
21
+ class << self
22
+ def transform_gemfile(&block)
23
+ raise ArgumentError, "transform_gemfile requires a block" unless block
24
+
25
+ GEMFILE_TRANSFORM_MUTEX.synchronize do
26
+ set_gemfile_transforms(gemfile_transforms + [block])
27
+ end
28
+ end
29
+
30
+ def run_transform_gemfile(appraisal, path, content)
31
+ context = GemfileContext.new(appraisal, path, content)
32
+ gemfile_transforms.each do |hook|
33
+ result = if hook.arity == 1
34
+ hook.call(context.content)
35
+ else
36
+ hook.call(context.content, context)
37
+ end
38
+ context.content = result unless result.nil?
39
+ end
40
+ context.content
41
+ end
42
+
43
+ def reset!
44
+ GEMFILE_TRANSFORM_MUTEX.synchronize do
45
+ set_gemfile_transforms([])
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def gemfile_transforms
52
+ ::Appraisal::Hooks.const_get(:GEMFILE_TRANSFORMS)
53
+ end
54
+
55
+ def set_gemfile_transforms(transforms)
56
+ ::Appraisal::Hooks.__send__(:remove_const, :GEMFILE_TRANSFORMS)
57
+ ::Appraisal::Hooks.const_set(:GEMFILE_TRANSFORMS, transforms.freeze)
58
+ end
59
+ end
60
+ end
61
+
62
+ class << self
63
+ def transform_gemfile(&block)
64
+ Hooks.transform_gemfile(&block)
65
+ end
66
+ end
67
+ end
@@ -19,8 +19,8 @@ module Appraisal
19
19
  desc("DEPRECATED: Resolve and install dependencies for each appraisal")
20
20
  task(:install) do
21
21
  warn("`rake appraisal:install` task is deprecated and will be removed soon. " \
22
- "Please use `appraisal install`.")
23
- exec("bundle exec appraisal install")
22
+ "Please use `appraisal generate-install`.")
23
+ exec("bundle exec appraisal generate-install")
24
24
  end
25
25
 
26
26
  desc("DEPRECATED: Remove all generated gemfiles from gemfiles/ folder")
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Appraisal
4
4
  module Version
5
- VERSION = "3.0.9"
5
+ VERSION = "3.1.0"
6
6
  end
7
7
  VERSION = Version::VERSION # Traditional constant location
8
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Appraisal2
4
4
  module Version
5
- VERSION = "3.0.9"
5
+ VERSION = "3.1.0"
6
6
  end
7
7
  VERSION = Version::VERSION # Traditional Constant Location
8
8
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appraisal2
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.9
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Boling
@@ -421,6 +421,7 @@ files:
421
421
  - lib/appraisal/gemspec.rb
422
422
  - lib/appraisal/git.rb
423
423
  - lib/appraisal/group.rb
424
+ - lib/appraisal/hooks.rb
424
425
  - lib/appraisal/ordered_hash.rb
425
426
  - lib/appraisal/path.rb
426
427
  - lib/appraisal/platform.rb
@@ -436,10 +437,10 @@ licenses:
436
437
  - MIT
437
438
  metadata:
438
439
  homepage_uri: https://appraisal2.galtzo.com
439
- source_code_uri: https://github.com/appraisal-rb/appraisal2/tree/v3.0.9
440
- changelog_uri: https://github.com/appraisal-rb/appraisal2/blob/v3.0.9/CHANGELOG.md
440
+ source_code_uri: https://github.com/appraisal-rb/appraisal2/tree/v3.1.0
441
+ changelog_uri: https://github.com/appraisal-rb/appraisal2/blob/v3.1.0/CHANGELOG.md
441
442
  bug_tracker_uri: https://github.com/appraisal-rb/appraisal2/issues
442
- documentation_uri: https://www.rubydoc.info/gems/appraisal2/3.0.9
443
+ documentation_uri: https://www.rubydoc.info/gems/appraisal2/3.1.0
443
444
  funding_uri: https://github.com/sponsors/pboling
444
445
  wiki_uri: https://github.com/appraisal-rb/appraisal2/wiki
445
446
  news_uri: https://www.railsbling.com/tags/appraisal2
metadata.gz.sig CHANGED
Binary file