rascal 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 +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +110 -0
- data/Guardfile +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +12 -0
- data/bin/_guard-core +29 -0
- data/bin/console +14 -0
- data/bin/cucumber +29 -0
- data/bin/guard +29 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/exe/rascal +13 -0
- data/lib/rascal/cli/base.rb +39 -0
- data/lib/rascal/cli/clean.rb +16 -0
- data/lib/rascal/cli/main.rb +45 -0
- data/lib/rascal/cli/shell.rb +16 -0
- data/lib/rascal/cli.rb +10 -0
- data/lib/rascal/docker/container.rb +121 -0
- data/lib/rascal/docker/interface.rb +111 -0
- data/lib/rascal/docker/network.rb +42 -0
- data/lib/rascal/docker/volume.rb +42 -0
- data/lib/rascal/docker.rb +18 -0
- data/lib/rascal/environment.rb +47 -0
- data/lib/rascal/environments_definition/gitlab.rb +117 -0
- data/lib/rascal/environments_definition.rb +23 -0
- data/lib/rascal/io_helper.rb +30 -0
- data/lib/rascal/service.rb +25 -0
- data/lib/rascal/version.rb +3 -0
- data/lib/rascal.rb +11 -0
- data/rascal.gemspec +37 -0
- metadata +97 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
module Rascal
|
2
|
+
module Docker
|
3
|
+
class Container
|
4
|
+
include IOHelper
|
5
|
+
|
6
|
+
def initialize(name, image)
|
7
|
+
@name = name
|
8
|
+
@prefixed_name = "#{NAME_PREFIX}#{name}"
|
9
|
+
@image = image
|
10
|
+
end
|
11
|
+
|
12
|
+
def download_missing
|
13
|
+
unless image_exists?
|
14
|
+
say "Downloading image for #{@name}"
|
15
|
+
Docker.interface.run(
|
16
|
+
'pull',
|
17
|
+
@image,
|
18
|
+
stdout: stdout,
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def running?
|
24
|
+
if id
|
25
|
+
container_info = Docker.interface.run(
|
26
|
+
'container',
|
27
|
+
'inspect',
|
28
|
+
id,
|
29
|
+
output: :json
|
30
|
+
).first
|
31
|
+
!!container_info.dig('State', 'Running')
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def exists?
|
38
|
+
!!id
|
39
|
+
end
|
40
|
+
|
41
|
+
def start(network: nil, network_alias: nil)
|
42
|
+
say "Starting container for #{@name}"
|
43
|
+
create(network: network, network_alias: network_alias) unless exists?
|
44
|
+
Docker.interface.run(
|
45
|
+
'container',
|
46
|
+
'start',
|
47
|
+
id,
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def create(network: nil, network_alias: nil)
|
52
|
+
@id = Docker.interface.run(
|
53
|
+
'container',
|
54
|
+
'create',
|
55
|
+
'--name', @prefixed_name,
|
56
|
+
*(['--network', network.id] if network),
|
57
|
+
*(['--network-alias', network_alias] if network_alias),
|
58
|
+
@image,
|
59
|
+
output: :id
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_and_attach(*command, env: {}, network: nil, volumes: [], working_dir: nil, allow_failure: false)
|
64
|
+
Docker.interface.run_and_attach(@image, *command,
|
65
|
+
env: env,
|
66
|
+
stdout: stdout,
|
67
|
+
stderr: stderr,
|
68
|
+
stdin: stdin,
|
69
|
+
network: network&.id,
|
70
|
+
volumes: volumes,
|
71
|
+
working_dir: working_dir,
|
72
|
+
allow_failure: allow_failure
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def clean
|
77
|
+
stop_container if running?
|
78
|
+
remove_container if exists?
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def id
|
84
|
+
@id ||= Docker.interface.run(
|
85
|
+
'container',
|
86
|
+
'ps',
|
87
|
+
'--all',
|
88
|
+
'--quiet',
|
89
|
+
'--filter', "name=^/#{@prefixed_name}$",
|
90
|
+
output: :id
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def image_exists?
|
95
|
+
Docker.interface.run(
|
96
|
+
'image',
|
97
|
+
'inspect',
|
98
|
+
@image,
|
99
|
+
output: :json,
|
100
|
+
allow_failure: true,
|
101
|
+
).first
|
102
|
+
end
|
103
|
+
|
104
|
+
def stop_container
|
105
|
+
Docker.interface.run(
|
106
|
+
'container',
|
107
|
+
'stop',
|
108
|
+
id,
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def remove_container
|
113
|
+
Docker.interface.run(
|
114
|
+
'container',
|
115
|
+
'rm',
|
116
|
+
id,
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Rascal
|
5
|
+
module Docker
|
6
|
+
class Interface
|
7
|
+
class Error < Rascal::Error; end
|
8
|
+
|
9
|
+
def run(*command, output: :ignore, stdout: nil, redirect_io: {}, allow_failure: false)
|
10
|
+
save_stdout = ''
|
11
|
+
save_stderr = ''
|
12
|
+
exit_status = nil
|
13
|
+
popen3('docker', *stringify_command(command)) do |docker_stdin, docker_stdout, docker_stderr, wait_thr|
|
14
|
+
docker_stdin.close
|
15
|
+
output_threads = [
|
16
|
+
read_lines(docker_stdout, save_stdout, stdout),
|
17
|
+
read_lines(docker_stderr, save_stderr),
|
18
|
+
]
|
19
|
+
exit_status = wait_thr.value
|
20
|
+
output_threads.each(&:join)
|
21
|
+
end
|
22
|
+
unless allow_failure || exit_status.success?
|
23
|
+
raise Error, "docker command '#{command.join(' ')}' failed with error:\n#{save_stderr}"
|
24
|
+
end
|
25
|
+
case output
|
26
|
+
when :json
|
27
|
+
begin
|
28
|
+
JSON.parse(save_stdout)
|
29
|
+
rescue JSON::ParserError
|
30
|
+
raise Error, "could not parse output of docker command '#{command.join(' ')}':\n#{save_stdout}"
|
31
|
+
end
|
32
|
+
when :id
|
33
|
+
save_stdout[/[0-9a-f]+/]
|
34
|
+
when :ignore
|
35
|
+
nil
|
36
|
+
else
|
37
|
+
raise ArgumentError, 'unknown option for :output'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_and_attach(image, *command, stdout: nil, stderr: nil, stdin: nil, env: {}, network: nil, volumes: [], working_dir: nil, allow_failure: false)
|
42
|
+
process_redirections = {}
|
43
|
+
args = []
|
44
|
+
if stdout
|
45
|
+
process_redirections[:out] = stdout
|
46
|
+
args += ['-a', 'STDOUT']
|
47
|
+
end
|
48
|
+
if stderr
|
49
|
+
process_redirections[:err] = stderr
|
50
|
+
args += ['-a', 'STDERR']
|
51
|
+
end
|
52
|
+
if stdin
|
53
|
+
process_redirections[:in] = stdin
|
54
|
+
args += ['-a', 'STDIN', '--interactive', '--tty']
|
55
|
+
end
|
56
|
+
if working_dir
|
57
|
+
args += ['-w', working_dir.to_s]
|
58
|
+
end
|
59
|
+
volumes.each do |volume|
|
60
|
+
args += ['-v', volume.to_param]
|
61
|
+
end
|
62
|
+
env.each do |key, value|
|
63
|
+
args += ['-e', "#{key}=#{value}"]
|
64
|
+
end
|
65
|
+
if network
|
66
|
+
args += ['--network', network.to_s]
|
67
|
+
end
|
68
|
+
exit_status = spawn(
|
69
|
+
env,
|
70
|
+
'docker',
|
71
|
+
'container',
|
72
|
+
'run',
|
73
|
+
'--rm',
|
74
|
+
*args,
|
75
|
+
image.to_s,
|
76
|
+
*stringify_command(command),
|
77
|
+
process_redirections,
|
78
|
+
)
|
79
|
+
unless allow_failure || exit_status.success?
|
80
|
+
raise Error, "docker container run failed"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def stringify_command(command)
|
87
|
+
command.collect(&:to_s)
|
88
|
+
end
|
89
|
+
|
90
|
+
def spawn(*command)
|
91
|
+
pid = Process.spawn(*command)
|
92
|
+
Process.wait(pid)
|
93
|
+
$?
|
94
|
+
end
|
95
|
+
|
96
|
+
def popen3(*command, &block)
|
97
|
+
Open3.popen3(*command, &block)
|
98
|
+
end
|
99
|
+
|
100
|
+
def read_lines(io, save_to, output_to = nil)
|
101
|
+
Thread.new do
|
102
|
+
io.each_line do |l|
|
103
|
+
output_to&.write(l)
|
104
|
+
save_to << l
|
105
|
+
end
|
106
|
+
rescue IOError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rascal
|
2
|
+
module Docker
|
3
|
+
class Network
|
4
|
+
def initialize(name)
|
5
|
+
@name = name
|
6
|
+
@prefixed_name = "#{NAME_PREFIX}#{name}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
Docker.interface.run(
|
11
|
+
'network',
|
12
|
+
'create',
|
13
|
+
@prefixed_name,
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def exists?
|
18
|
+
!!id
|
19
|
+
end
|
20
|
+
|
21
|
+
def clean
|
22
|
+
if exists?
|
23
|
+
Docker.interface.run(
|
24
|
+
'network',
|
25
|
+
'rm',
|
26
|
+
id,
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
@id ||= Docker.interface.run(
|
33
|
+
'network',
|
34
|
+
'ls',
|
35
|
+
'--quiet',
|
36
|
+
'--filter', "name=^#{@prefixed_name}$",
|
37
|
+
output: :id,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rascal
|
2
|
+
module Docker
|
3
|
+
module Volume
|
4
|
+
class Base
|
5
|
+
end
|
6
|
+
|
7
|
+
class Named < Base
|
8
|
+
def initialize(name, container_path)
|
9
|
+
@prefixed_name = "#{NAME_PREFIX}#{name}"
|
10
|
+
@container_path = container_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_param
|
14
|
+
"#{@prefixed_name}:#{@container_path}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def clean
|
18
|
+
Docker.interface.run(
|
19
|
+
'volume',
|
20
|
+
'rm',
|
21
|
+
@prefixed_name,
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Bind < Base
|
27
|
+
def initialize(local_path, container_path)
|
28
|
+
@local_path = local_path
|
29
|
+
@container_path = container_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_param
|
33
|
+
"#{@local_path}:#{@container_path}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def clean
|
37
|
+
# nothing to do
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rascal
|
2
|
+
module Docker
|
3
|
+
NAME_PREFIX = 'rascal-'
|
4
|
+
|
5
|
+
autoload :Container, 'rascal/docker/container'
|
6
|
+
autoload :Interface, 'rascal/docker/interface'
|
7
|
+
autoload :Network, 'rascal/docker/network'
|
8
|
+
autoload :Volume, 'rascal/docker/volume'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_writer :interface
|
12
|
+
|
13
|
+
def interface
|
14
|
+
@interface ||= Interface.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Rascal
|
2
|
+
class Environment
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, image:, env_variables: {}, services: [], volumes: [], before_shell: [], working_dir: nil)
|
6
|
+
@name = name
|
7
|
+
@network = Docker::Network.new(name)
|
8
|
+
@container = Docker::Container.new(name, image)
|
9
|
+
@env_variables = env_variables
|
10
|
+
@services = services
|
11
|
+
@volumes = volumes
|
12
|
+
@working_dir = working_dir
|
13
|
+
@before_shell = before_shell
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_shell
|
17
|
+
download_missing
|
18
|
+
start_services
|
19
|
+
command = [*@before_shell, 'bash'].join(';')
|
20
|
+
@container.run_and_attach('bash', '-c', command,
|
21
|
+
env: @env_variables,
|
22
|
+
network: @network,
|
23
|
+
volumes: @volumes,
|
24
|
+
working_dir: @working_dir,
|
25
|
+
allow_failure: true
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def clean
|
30
|
+
@services.each(&:clean)
|
31
|
+
@network.clean
|
32
|
+
@volumes.each(&:clean)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def download_missing
|
38
|
+
@container.download_missing
|
39
|
+
@services.each(&:download_missing)
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_services
|
43
|
+
@network.create unless @network.exists?
|
44
|
+
@services.each { |s| s.start_if_stopped(network: @network) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Rascal
|
4
|
+
module EnvironmentsDefinition
|
5
|
+
class Gitlab
|
6
|
+
class << self
|
7
|
+
def detect(path)
|
8
|
+
if path.directory?
|
9
|
+
path = path.join('.gitlab-ci.yml')
|
10
|
+
end
|
11
|
+
if path.file?
|
12
|
+
new(path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Config
|
18
|
+
def initialize(config, prefix)
|
19
|
+
@config = config
|
20
|
+
@prefix = prefix
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(key, *default)
|
24
|
+
if @config.has_key?(key)
|
25
|
+
@config[key]
|
26
|
+
elsif default.size > 0
|
27
|
+
default.first
|
28
|
+
else
|
29
|
+
raise Error.new("missing config for '#{@prefix}.#{key}'")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def initialize(config_path)
|
36
|
+
@info = parse_definition(config_path.read)
|
37
|
+
@repo_dir = config_path.parent
|
38
|
+
@rascal_config = @info.fetch('.rascal', {})
|
39
|
+
end
|
40
|
+
|
41
|
+
def environment(name)
|
42
|
+
environments.detect do |e|
|
43
|
+
e.name == name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def available_environment_names
|
48
|
+
environments.collect(&:name).sort
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def parse_definition(yaml)
|
54
|
+
YAML.safe_load(yaml, [], [], true)
|
55
|
+
end
|
56
|
+
|
57
|
+
def environments
|
58
|
+
@environments ||= begin
|
59
|
+
@info.collect do |key, environment_config|
|
60
|
+
config = Config.new(deep_merge(environment_config, @rascal_config), key)
|
61
|
+
docker_repo_dir = config.get('repo_dir')
|
62
|
+
unless key.start_with?('.')
|
63
|
+
Environment.new(key,
|
64
|
+
image: config.get('image'),
|
65
|
+
env_variables: (config.get('variables', {})),
|
66
|
+
services: build_services(key, config.get('services', [])),
|
67
|
+
volumes: [build_repo_volume(docker_repo_dir), *build_volumes(key, config.get('volumes', {}))],
|
68
|
+
before_shell: config.get('before_shell', []),
|
69
|
+
working_dir: docker_repo_dir,
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end.compact
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def deep_merge(hash1, hash2)
|
77
|
+
if hash1.is_a?(Hash) && hash2.is_a?(Hash)
|
78
|
+
result = {}
|
79
|
+
hash1.each do |key1, value1|
|
80
|
+
if hash2.has_key?(key1)
|
81
|
+
result[key1] = deep_merge(value1, hash2[key1])
|
82
|
+
else
|
83
|
+
result[key1] = value1
|
84
|
+
end
|
85
|
+
end
|
86
|
+
hash2.each do |key2, value2|
|
87
|
+
result[key2] ||= value2
|
88
|
+
end
|
89
|
+
result
|
90
|
+
else
|
91
|
+
hash2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_services(name, services)
|
96
|
+
services.collect do |service_config|
|
97
|
+
service_alias = service_config['alias']
|
98
|
+
Service.new("#{name}_#{service_alias}",
|
99
|
+
alias_name: service_config['alias'],
|
100
|
+
image: service_config['name'],
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_repo_volume(docker_repo_dir)
|
106
|
+
Docker::Volume::Bind.new(@repo_dir, docker_repo_dir)
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_volumes(name, volume_config)
|
110
|
+
volume_config.collect do |volume_name, docker_path|
|
111
|
+
Docker::Volume::Named.new("#{name}-#{volume_name}", docker_path)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rascal
|
2
|
+
module EnvironmentsDefinition
|
3
|
+
autoload :Gitlab, 'rascal/environments_definition/gitlab'
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def detect(working_dir)
|
7
|
+
definition_formats.each do |format|
|
8
|
+
definition = format.detect(working_dir)
|
9
|
+
return definition if definition
|
10
|
+
end
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def definition_formats
|
17
|
+
[
|
18
|
+
Gitlab
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rascal
|
2
|
+
module IOHelper
|
3
|
+
class << self
|
4
|
+
attr_accessor :stdout, :stdin, :stderr
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@stdout = $stdout
|
8
|
+
@stderr = $stderr
|
9
|
+
@stdin = $stdin
|
10
|
+
end
|
11
|
+
end
|
12
|
+
setup
|
13
|
+
|
14
|
+
def say(message)
|
15
|
+
stdout.puts(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def stdout
|
19
|
+
IOHelper.stdout
|
20
|
+
end
|
21
|
+
|
22
|
+
def stderr
|
23
|
+
IOHelper.stderr
|
24
|
+
end
|
25
|
+
|
26
|
+
def stdin
|
27
|
+
IOHelper.stdin
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rascal
|
2
|
+
class Service
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, image:, alias_name:)
|
6
|
+
@name = name
|
7
|
+
@container = Docker::Container.new(name, image)
|
8
|
+
@alias = alias_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def download_missing
|
12
|
+
@container.download_missing
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_if_stopped(network: nil)
|
16
|
+
unless @container.running?
|
17
|
+
@container.start(network: network, network_alias: @alias)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def clean
|
22
|
+
@container.clean
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/rascal.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "rascal/version"
|
2
|
+
|
3
|
+
module Rascal
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
autoload :Docker, 'rascal/docker'
|
7
|
+
autoload :Environment, 'rascal/environment'
|
8
|
+
autoload :EnvironmentsDefinition, 'rascal/environments_definition'
|
9
|
+
autoload :IOHelper, 'rascal/io_helper'
|
10
|
+
autoload :Service, 'rascal/service'
|
11
|
+
end
|
data/rascal.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "rascal/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rascal"
|
8
|
+
spec.version = Rascal::VERSION
|
9
|
+
spec.authors = ["Tobias Kraze"]
|
10
|
+
spec.email = ["tobias.kraze@makandra.de"]
|
11
|
+
|
12
|
+
spec.summary = "Spin up CI environments locally."
|
13
|
+
spec.homepage = "https://github.com/makandra/rascal"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/makandra/rascal"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/makandra/rascal/blob/master/CHANGELOG.md"
|
22
|
+
else
|
23
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
24
|
+
"public gem pushes."
|
25
|
+
end
|
26
|
+
|
27
|
+
# Specify which files should be added to the gem when it is released.
|
28
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
29
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
30
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
31
|
+
end
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib"]
|
35
|
+
|
36
|
+
spec.add_dependency "thor", "~> 0.20.3"
|
37
|
+
end
|