gem_isolator 0.2.1
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +114 -0
- data/lib/gem_isolator.rb +10 -0
- data/lib/gem_isolator/isolation.rb +42 -0
- data/lib/gem_isolator/isolation/environment.rb +27 -0
- data/lib/gem_isolator/isolation/gemfile.rb +26 -0
- data/lib/gem_isolator/isolation/sandbox.rb +48 -0
- data/lib/gem_isolator/version.rb +4 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 09ed7c8a98c30e7514975a626310801132f33458
|
4
|
+
data.tar.gz: ac418d8c8c303f4463a1de38bb3132591c4c08ff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1eda65173f7419182d89c00ffe2a3d71004e9d66f8fa6d27c482d11cce5035859d31b721c4e4a419991fc1dd3ad36f3faba0ac17034deaedd631f5db0a8627da
|
7
|
+
data.tar.gz: b3f5df4b52ba9d37910e4ea8f4d5e63693b187b5dc2e35e0dc1babb0ac41c860c1c2aeadd8516a4db3c04cc17ff7f14c75041fa5de2c0e41bdc6bc53f885a414
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Cezary Baginski
|
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,114 @@
|
|
1
|
+
[](http://badge.fury.io/rb/gem_isolator) [](http://travis-ci.org/e2/gem_isolator)
|
2
|
+
|
3
|
+
# GemIsolator
|
4
|
+
|
5
|
+
Allows running commands in an isolated set of gems.
|
6
|
+
|
7
|
+
Useful for testing gem dependencies in your project and reproducing related bugs.
|
8
|
+
|
9
|
+
NOTE: It currently requires Bundler to setup the isolated environment, but
|
10
|
+
Bundler isn't necessary to run your Ruby code in isolation.
|
11
|
+
|
12
|
+
## Requirements
|
13
|
+
|
14
|
+
For supported Ruby versions, check the .travis.yml file.
|
15
|
+
|
16
|
+
(For supporting older versions, open an issue - with some info about why you are unable to upgrade).
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'gem_isolator'
|
24
|
+
```
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
E.g. in an RSpec test:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
cmd = "ruby #{File.expand_path('lib/foo.rb')}"
|
36
|
+
GemIsolator.isolate(gems: %w(bar >=3.2.1)) do |env, isolation|
|
37
|
+
expect(isolation.system(env, cmd)).to eq(true)
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
1. 'lib/foo.rb' is relative to your project's current directory.
|
42
|
+
2. `system` acts just like `Kernel.system`.
|
43
|
+
3. you can pass custom environment variables, e.g `env.merge('FOO' => '1')`
|
44
|
+
4. `:gems` is a list of gems you want installed
|
45
|
+
|
46
|
+
## Features
|
47
|
+
|
48
|
+
- [x] sets up temp dir with bundled gems
|
49
|
+
- [x] allows defining which gems you need installed in the sandbox
|
50
|
+
- [x] allows overriding environment
|
51
|
+
- [x] allows running commands via Bundler or plain RubyGems
|
52
|
+
- [x] allows multiple commands within same sandbox
|
53
|
+
- [x] fails if sandbox can't be initialized
|
54
|
+
- [ ] seemlessly allows you to run same tests using Docker
|
55
|
+
- [ ] smart caching and/or proxy for faster gem installations
|
56
|
+
- [ ] allows reusing same sandbox object between tests
|
57
|
+
- [ ] release a 1.x as soon as API is useful and comfortable enough
|
58
|
+
|
59
|
+
## Things to be careful about
|
60
|
+
|
61
|
+
You can run commands in 3 ways:
|
62
|
+
|
63
|
+
1. `system('foo')` will search for 'foo' among the binaries of installed isolated gems first
|
64
|
+
2. `system('bin/foo')` a binstubbed version of binary from isolated gems (uses Bundler)
|
65
|
+
3. `system('bundle exec foo')` uses Bundler instead of RubyGems
|
66
|
+
|
67
|
+
Capturing output is not yet supported (open a PR if you're interested).
|
68
|
+
|
69
|
+
NOTE: Inside the block, the current directory is set to a temporary directory,
|
70
|
+
so it's best to expand your paths outside the block.
|
71
|
+
|
72
|
+
NOTE: Variables other than those reset by `Bundler.with_clean_env` are left
|
73
|
+
alone, so it's up to you to reset them. (Open an issue if you want to have a
|
74
|
+
"clean slate").
|
75
|
+
|
76
|
+
NOTE: You might prefer to use caching, by e.g. setting a gem source or proxy.
|
77
|
+
Open a feature request if you're interested.
|
78
|
+
|
79
|
+
NOTE: `:gems` option is an array (for each gem) of arrays (Gemfile `gem` keyword arguments). `.inspect` is used to stringify, so things should work as expected, even if you use a hash.
|
80
|
+
|
81
|
+
|
82
|
+
## Debugging
|
83
|
+
|
84
|
+
Just use system() commands to find out what's going on, e.g.:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
cmd = "ruby #{File.expand_path('lib/foo.rb')}"
|
88
|
+
GemIsolator.isolate(gems: [%w(bar ~>1.2)]) do |env, isolation|
|
89
|
+
# debugging
|
90
|
+
isolation.system("pwd")
|
91
|
+
isolation.system("gem env")
|
92
|
+
isolation.system("gem list")
|
93
|
+
isolation.system("cat Gemfile")
|
94
|
+
isolation.system("cat Gemfile.lock")
|
95
|
+
isolatior.system("ls -l")
|
96
|
+
isolatior.system("find bundle -name 'foo'")
|
97
|
+
|
98
|
+
expect(isolation.system(env, cmd)).to eq(true)
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
## Development
|
103
|
+
|
104
|
+
Run `rake spec` to run the tests.
|
105
|
+
|
106
|
+
## Contributing
|
107
|
+
|
108
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/gem_isolator.
|
109
|
+
|
110
|
+
If there are no other major open pull requests (or active branches), feel free to refactor/reorganize the project as you like.
|
111
|
+
|
112
|
+
## License
|
113
|
+
|
114
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/gem_isolator.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Show warnings about vulnerabilities, bugs and outdated Rubies, since previous
|
4
|
+
# versions aren't tested or officially supported.
|
5
|
+
require 'ruby_dep/warning'
|
6
|
+
RubyDep::Warning.new.show_warnings
|
7
|
+
|
8
|
+
require 'gem_isolator/isolation/environment'
|
9
|
+
require 'gem_isolator/isolation/sandbox'
|
10
|
+
|
11
|
+
module GemIsolator
|
12
|
+
# Main class for setting up sandbox/isolation environment
|
13
|
+
class Isolation
|
14
|
+
def initialize(options)
|
15
|
+
gem_defs = options.fetch(:gems, [[]])
|
16
|
+
within_sandbox do
|
17
|
+
@sandbox = Sandbox.new(gems: gem_defs)
|
18
|
+
sandbox.setup
|
19
|
+
environment = Environment.new(gem_home: sandbox.gem_home).to_hash
|
20
|
+
yield(environment, self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def system(*args)
|
25
|
+
sandbox.system(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :sandbox
|
31
|
+
|
32
|
+
def within_sandbox
|
33
|
+
Bundler.with_clean_env do
|
34
|
+
Dir.mktmpdir do |path|
|
35
|
+
Dir.chdir(path) do
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GemIsolator
|
3
|
+
class Isolation
|
4
|
+
# RubyGems isolating environment
|
5
|
+
class Environment
|
6
|
+
def initialize(options)
|
7
|
+
@gem_home = options.fetch(:gem_home)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
@environment ||= {
|
12
|
+
'GEM_HOME' => gem_home.to_s,
|
13
|
+
'GEM_PATH' => gem_home.to_s,
|
14
|
+
'PATH' => "#{bin_path}:#{ENV['PATH']}"
|
15
|
+
}.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :gem_home
|
21
|
+
|
22
|
+
def bin_path
|
23
|
+
@bin_path ||= gem_home + 'bin'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GemIsolator
|
3
|
+
class Isolation
|
4
|
+
# Creates a Gemfile
|
5
|
+
class Gemfile
|
6
|
+
def initialize(options)
|
7
|
+
gem_defs = options.fetch(:gems)
|
8
|
+
@content = generate(gem_defs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
IO.write('Gemfile', @content)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def generate(gem_defs)
|
18
|
+
content = "source 'https://rubygems.org';\n"
|
19
|
+
gem_defs.each do |requirements|
|
20
|
+
content += "gem #{requirements.map(&:inspect).join(', ')};\n"
|
21
|
+
end
|
22
|
+
content
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'gem_isolator/isolation/gemfile'
|
3
|
+
|
4
|
+
module GemIsolator
|
5
|
+
class Isolation
|
6
|
+
# Class for installing gems into the sandbox in the current directory
|
7
|
+
class Sandbox
|
8
|
+
class Error < RuntimeError
|
9
|
+
class BundlerFailed < Error; end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@gem_defs = options.fetch(:gems)
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup
|
17
|
+
Gemfile.new(gems: @gem_defs).create
|
18
|
+
case Kernel.system(*%w(bundle install --standalone --binstubs))
|
19
|
+
when false
|
20
|
+
raise Error::BundlerFailed, 'sandbox setup with bundler failed'
|
21
|
+
when nil
|
22
|
+
raise Errno::ENOENT, "cannot run command 'bundle'"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def gem_home
|
27
|
+
@gem_home ||= gem_base_path + ruby_dir
|
28
|
+
end
|
29
|
+
|
30
|
+
def system(*args)
|
31
|
+
Kernel.system(*args)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def gem_base_path
|
37
|
+
@gem_base_path ||= Pathname.pwd + 'bundle/ruby'
|
38
|
+
end
|
39
|
+
|
40
|
+
def ruby_dir
|
41
|
+
subdirs = gem_base_path.children
|
42
|
+
unknown_dirs = subdirs[1..-1]
|
43
|
+
return subdirs.first if unknown_dirs.empty?
|
44
|
+
raise ArgumentError, "Unknown gem home subdirs: #{unknown_dirs.inspect}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gem_isolator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cezary Baginski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby_dep
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.3.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.3.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '11.1'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '11.1'
|
47
|
+
description: Good for testing dependencies of a gem and/or different gem version combinations
|
48
|
+
email:
|
49
|
+
- cezary@chronomantic.net
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- LICENSE.txt
|
55
|
+
- README.md
|
56
|
+
- lib/gem_isolator.rb
|
57
|
+
- lib/gem_isolator/isolation.rb
|
58
|
+
- lib/gem_isolator/isolation/environment.rb
|
59
|
+
- lib/gem_isolator/isolation/gemfile.rb
|
60
|
+
- lib/gem_isolator/isolation/sandbox.rb
|
61
|
+
- lib/gem_isolator/version.rb
|
62
|
+
homepage: https://github.com/e2/gem_isolator
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.2'
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.2.5
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.6.4
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Use an isolated set of gems in your tests
|
89
|
+
test_files: []
|