appraisal2 3.0.5 → 3.0.7

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.
data/RUBOCOP.md ADDED
@@ -0,0 +1,71 @@
1
+ # RuboCop Usage Guide
2
+
3
+ ## Overview
4
+
5
+ A tale of two RuboCop plugin gems.
6
+
7
+ ### RuboCop Gradual
8
+
9
+ This project uses `rubocop_gradual` instead of vanilla RuboCop for code style checking. The `rubocop_gradual` tool allows for gradual adoption of RuboCop rules by tracking violations in a lock file.
10
+
11
+ ### RuboCop LTS
12
+
13
+ This project uses `rubocop-lts` to ensure, on a best-effort basis, compatibility with Ruby >= 1.9.2.
14
+ RuboCop rules are meticulously configured by the `rubocop-lts` family of gems to ensure that a project is compatible with a specific version of Ruby. See: https://rubocop-lts.gitlab.io for more.
15
+
16
+ ## Checking RuboCop Violations
17
+
18
+ To check for RuboCop violations in this project, always use:
19
+
20
+ ```bash
21
+ bundle exec rake rubocop_gradual:check
22
+ ```
23
+
24
+ **Do not use** the standard RuboCop commands like:
25
+ - `bundle exec rubocop`
26
+ - `rubocop`
27
+
28
+ ## Understanding the Lock File
29
+
30
+ The `.rubocop_gradual.lock` file tracks all current RuboCop violations in the project. This allows the team to:
31
+
32
+ 1. Prevent new violations while gradually fixing existing ones
33
+ 2. Track progress on code style improvements
34
+ 3. Ensure CI builds don't fail due to pre-existing violations
35
+
36
+ ## Common Commands
37
+
38
+ - **Check violations**
39
+ - `bundle exec rake rubocop_gradual`
40
+ - `bundle exec rake rubocop_gradual:check`
41
+ - **(Safe) Autocorrect violations, and update lockfile if no new violations**
42
+ - `bundle exec rake rubocop_gradual:autocorrect`
43
+ - **Force update the lock file (w/o autocorrect) to match violations present in code**
44
+ - `bundle exec rake rubocop_gradual:force_update`
45
+
46
+ ## Workflow
47
+
48
+ 1. Before submitting a PR, run `bundle exec rake rubocop_gradual:autocorrect`
49
+ a. or just the default `bundle exec rake`, as autocorrection is a pre-requisite of the default task.
50
+ 2. If there are new violations, either:
51
+ - Fix them in your code
52
+ - Run `bundle exec rake rubocop_gradual:force_update` to update the lock file (only for violations you can't fix immediately)
53
+ 3. Commit the updated `.rubocop_gradual.lock` file along with your changes
54
+
55
+ ## Never add inline RuboCop disables
56
+
57
+ Do not add inline `rubocop:disable` / `rubocop:enable` comments anywhere in the codebase (including specs, except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). We handle exceptions in two supported ways:
58
+
59
+ - Permanent/structural exceptions: prefer adjusting the RuboCop configuration (e.g., in `.rubocop.yml`) to exclude a rule for a path or file pattern when it makes sense project-wide.
60
+ - Temporary exceptions while improving code: record the current violations in `.rubocop_gradual.lock` via the gradual workflow:
61
+ - `bundle exec rake rubocop_gradual:autocorrect` (preferred; will autocorrect what it can and update the lock only if no new violations were introduced)
62
+ - If needed, `bundle exec rake rubocop_gradual:force_update` (as a last resort when you cannot fix the newly reported violations immediately)
63
+
64
+ In general, treat the rules as guidance to follow; fix violations rather than ignore them. For example, RSpec conventions in this project expect `described_class` to be used in specs that target a specific class under test.
65
+
66
+ ## Benefits of rubocop_gradual
67
+
68
+ - Allows incremental adoption of code style rules
69
+ - Prevents CI failures due to pre-existing violations
70
+ - Provides a clear record of code style debt
71
+ - Enables focused efforts on improving code quality over time
data/SECURITY.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |----------|-----------|
7
- | 1.latest | ✅ |
7
+ | 3.0.latest | ✅ |
8
8
 
9
9
  ## Security contact information
10
10
 
@@ -18,4 +18,4 @@ If you are interested in support for versions older than the latest release,
18
18
  please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
19
19
  or find other sponsorship links in the [README].
20
20
 
21
- [README]: README.md
21
+ [README]: README.md
data/certs/pboling.pem ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl
3
+ ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
4
+ A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM
5
+ DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
6
+ LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA
7
+ uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61
8
+ LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5
9
+ mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN
10
+ coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV
11
+ FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj
12
+ yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1
13
+ to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD
14
+ qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj
15
+ fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ
16
+ HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
17
+ A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
18
+ ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9
19
+ wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR
20
+ L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm
21
+ GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k
22
+ kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq
23
+ QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA
24
+ 0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p
25
+ DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt
26
+ L9nRqA==
27
+ -----END CERTIFICATE-----
@@ -109,7 +109,7 @@ module Appraisal
109
109
  File.open(lockfile_path, "w") do |file|
110
110
  file.write(lockfile_content.gsub(
111
111
  / #{current_directory}/,
112
- " #{relative_path}",
112
+ " #{relative_path}"
113
113
  ))
114
114
  end
115
115
  end
@@ -7,18 +7,18 @@ module Appraisal
7
7
  class BundlerDSL
8
8
  attr_reader :dependencies
9
9
 
10
- PARTS = %w[
11
- source
12
- ruby_version
13
- gits
14
- paths
15
- dependencies
16
- groups
17
- platforms
18
- source_blocks
19
- install_if
20
- gemspec
21
- eval_gemfile
10
+ PARTS = [
11
+ "source",
12
+ "ruby_version",
13
+ "gits",
14
+ "paths",
15
+ "dependencies",
16
+ "groups",
17
+ "platforms",
18
+ "source_blocks",
19
+ "install_if",
20
+ "gemspec",
21
+ "eval_gemfile"
22
22
  ]
23
23
 
24
24
  def initialize
@@ -7,6 +7,32 @@ module Appraisal
7
7
  class Command
8
8
  attr_reader :command, :env, :gemfile
9
9
 
10
+ # BUNDLE_* environment variables that must be preserved for proper bundler operation
11
+ # and test isolation. These are preserved when using with_bundler_env to ensure:
12
+ # - Bundler version switching works (BUNDLE_GEMFILE)
13
+ # - Test isolation is maintained (BUNDLE_APP_CONFIG, etc.)
14
+ # - User settings are respected (BUNDLE_PATH, BUNDLE_USER_CACHE, etc.)
15
+ #
16
+ # NOTE: BUNDLE_LOCKFILE is NOT preserved because:
17
+ # - Bundler automatically infers lockfile from BUNDLE_GEMFILE (e.g., foo.gemfile -> foo.gemfile.lock)
18
+ # - Forcing BUNDLE_LOCKFILE breaks appraisal's ability to create per-gemfile lockfiles
19
+ # - Each appraisal needs its own lockfile, not the root Gemfile.lock
20
+ PRESERVED_BUNDLE_VARS = [
21
+ "BUNDLE_GEMFILE",
22
+ "BUNDLE_APP_CONFIG",
23
+ "BUNDLE_PATH",
24
+ "BUNDLE_USER_CONFIG",
25
+ "BUNDLE_USER_CACHE",
26
+ "BUNDLE_USER_PLUGIN",
27
+ "BUNDLE_IGNORE_FUNDING_REQUESTS",
28
+ "BUNDLE_DISABLE_SHARED_GEMS"
29
+ ].freeze
30
+
31
+ PRESERVED_RUNTIME_VARS = [
32
+ "PATH",
33
+ "GEM_PATH"
34
+ ].freeze
35
+
10
36
  def initialize(command, options = {})
11
37
  @gemfile = options[:gemfile]
12
38
  @env = options.fetch(:env, {})
@@ -15,19 +41,22 @@ module Appraisal
15
41
  end
16
42
 
17
43
  def run
18
- # Capture BUNDLE_PATH from the current environment before with_original_env scrubs it
19
- bundle_path = ENV["BUNDLE_PATH"]
20
44
  run_env = test_environment.merge(env)
21
45
 
22
46
  if @skip_bundle_exec
23
47
  execute(run_env)
24
48
  else
25
- # This will wipe out BUNDLE_* variables
26
- Bundler.with_original_env do
27
- # Restore BUNDLE_PATH if it was set
28
- # BUNDLE_PATH is often used for caching between appraisals
29
- ENV["BUNDLE_PATH"] = bundle_path if bundle_path
49
+ # For bundler version switching to work reliably, we need to preserve
50
+ # the appraisal Gemfile while avoiding the active parent Bundler process.
51
+ # However, we still need to isolate from the parent's bundler state
52
+ # to avoid conflicts.
53
+ #
54
+ # Solution: Use a selective environment approach instead of with_original_env,
55
+ # which strips all BUNDLE_* variables and breaks version switching.
56
+ with_bundler_env do
57
+ apply_run_env(run_env)
30
58
  ensure_bundler_is_available
59
+ ensure_locked_bundler_is_available
31
60
  execute(run_env)
32
61
  end
33
62
  end
@@ -35,10 +64,60 @@ module Appraisal
35
64
 
36
65
  private
37
66
 
67
+ # Provide a clean environment while preserving Bundler's version switching
68
+ # inputs and test isolation settings.
69
+ #
70
+ # The current Ruby process has bundler activated, which adds bundler/setup to RUBYOPT.
71
+ # When we run a subprocess, we need to remove that activation so the subprocess bundler
72
+ # can start fresh. However, we keep all test isolation variables intact.
73
+ def with_bundler_env
74
+ backup_env = ENV.to_h
75
+
76
+ begin
77
+ clean_env = if Bundler.respond_to?(:original_env)
78
+ Bundler.original_env.to_h
79
+ else
80
+ backup_env.to_h
81
+ end
82
+
83
+ # Avoid leaking a global BUNDLE_LOCKFILE into subprocesses.
84
+ clean_env.delete("BUNDLE_LOCKFILE")
85
+
86
+ preserved_vars = PRESERVED_BUNDLE_VARS + PRESERVED_RUNTIME_VARS
87
+
88
+ preserved_vars.each do |var|
89
+ clean_env[var] = backup_env[var] if backup_env[var]
90
+ end
91
+
92
+ # Remove bundler/setup from RUBYOPT so subprocess doesn't auto-load bundler
93
+ if backup_env["RUBYOPT"]
94
+ rubyopt = backup_env["RUBYOPT"].split(" ")
95
+ rubyopt.reject! { |opt| opt == "-rbundler/setup" || opt.include?("bundler/setup") }
96
+ clean_env["RUBYOPT"] = rubyopt.join(" ") unless rubyopt.empty?
97
+ end
98
+
99
+ # Remove Bundler activation markers from the subprocess environment.
100
+ # BUNDLE_BIN_PATH pins the already-active Bundler executable, so keeping
101
+ # it would bypass the BUNDLED WITH version selected below.
102
+ clean_env.delete("BUNDLE_BIN_PATH")
103
+ clean_env.delete("BUNDLER_SETUP")
104
+ clean_env.delete("BUNDLER_VERSION")
105
+
106
+ ENV.replace(clean_env)
107
+
108
+ yield
109
+ ensure
110
+ ENV.replace(backup_env)
111
+ end
112
+ end
113
+
38
114
  def execute(run_env)
39
115
  announce
40
116
 
41
117
  ENV["BUNDLE_GEMFILE"] = gemfile
118
+ if (bundler_version = locked_bundler_version)
119
+ ENV["BUNDLER_VERSION"] = bundler_version
120
+ end
42
121
  ENV["APPRAISAL_INITIALIZED"] = "1"
43
122
  run_env.each_pair do |key, value|
44
123
  ENV[key] = value
@@ -47,6 +126,12 @@ module Appraisal
47
126
  exit(1) unless Kernel.system(command_as_string)
48
127
  end
49
128
 
129
+ def apply_run_env(run_env)
130
+ run_env.each_pair do |key, value|
131
+ ENV[key] = value
132
+ end
133
+ end
134
+
50
135
  def ensure_bundler_is_available
51
136
  # Check if any version of bundler is available
52
137
  return if system(%(gem list --silent -i bundler))
@@ -65,6 +150,36 @@ manually.
65
150
  exit(1)
66
151
  end
67
152
 
153
+ def ensure_locked_bundler_is_available
154
+ locked_version = locked_bundler_version
155
+ return unless locked_version
156
+
157
+ return if system(%(gem list --silent -i bundler -v "#{locked_version}"))
158
+
159
+ puts ">> Bundler #{locked_version} not found, attempting to install..."
160
+ return if system("gem install bundler -v #{Shellwords.escape(locked_version)} --no-document")
161
+
162
+ puts
163
+ puts <<-ERROR.rstrip
164
+ Bundler #{locked_version} installation failed.
165
+ Please try running:
166
+ `gem install bundler -v #{locked_version}`
167
+ manually.
168
+ ERROR
169
+ exit(1)
170
+ end
171
+
172
+ def locked_bundler_version
173
+ return unless gemfile
174
+
175
+ lockfile_path = "#{gemfile}.lock"
176
+ return unless File.exist?(lockfile_path)
177
+
178
+ lockfile_content = File.read(lockfile_path)
179
+ match = lockfile_content.match(/BUNDLED WITH\s*\n\s*([^\s]+)/)
180
+ match && match[1]
181
+ end
182
+
68
183
  def announce
69
184
  if gemfile
70
185
  puts ">> BUNDLE_GEMFILE=#{gemfile} #{command_as_string}"
@@ -85,7 +200,7 @@ manually.
85
200
  if command_starts_with_bundle?(original_command)
86
201
  original_command
87
202
  elsif original_command.is_a?(Array)
88
- %w[bundle exec] + original_command
203
+ ["bundle", "exec"] + original_command
89
204
  else
90
205
  "bundle exec #{original_command}"
91
206
  end
@@ -104,7 +219,7 @@ manually.
104
219
 
105
220
  {
106
221
  "GEM_HOME" => ENV["GEM_HOME"],
107
- "GEM_PATH" => "",
222
+ "GEM_PATH" => ""
108
223
  }
109
224
  end
110
225
  end
@@ -40,7 +40,7 @@ module Appraisal
40
40
  :lockfile => "#{gemfile.send(:gemfile_name)}.lock",
41
41
  :lockfile_path => gemfile.send(:lockfile_path),
42
42
  :relative_gemfile_path => gemfile.relative_gemfile_path,
43
- :relative_lockfile_path => "#{gemfile.relative_gemfile_path}.lock",
43
+ :relative_lockfile_path => "#{gemfile.relative_gemfile_path}.lock"
44
44
  )
45
45
  end
46
46
  end
@@ -10,7 +10,7 @@ module Appraisal
10
10
  class Factory
11
11
  ADAPTERS = {
12
12
  "bundler" => BundlerAdapter,
13
- "ore" => OreAdapter,
13
+ "ore" => OreAdapter
14
14
  }.freeze
15
15
 
16
16
  DEFAULT_MANAGER = "bundler"
@@ -24,7 +24,7 @@ module Appraisal
24
24
 
25
25
  def exported_options
26
26
  @options.merge(
27
- :path => Utils.prefix_path(@options[:path]),
27
+ :path => Utils.prefix_path(@options[:path])
28
28
  )
29
29
  end
30
30
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Appraisal
4
4
  module Version
5
- VERSION = "3.0.5"
5
+ VERSION = "3.0.7"
6
6
  end
7
7
  VERSION = Version::VERSION # Traditional constant location
8
8
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appraisal2
4
+ module Version
5
+ VERSION = "3.0.7"
6
+ end
7
+ VERSION = Version::VERSION # Traditional Constant Location
8
+ end
data/lib/appraisal2.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "version_gem"
4
+ require_relative "appraisal2/version"
5
+
3
6
  require "appraisal/version"
4
7
  require "appraisal/task"
5
8
 
6
9
  Appraisal::Task.new
10
+
11
+ Appraisal2::Version.class_eval do
12
+ extend VersionGem::Basic
13
+ end
@@ -0,0 +1,6 @@
1
+ module Appraisal2
2
+ module Version
3
+ VERSION: String
4
+ end
5
+ VERSION: String
6
+ end
data.tar.gz.sig CHANGED
Binary file