rascal 0.1.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +2 -2
- data/README.md +31 -7
- data/lib/rascal/cli.rb +5 -4
- data/lib/rascal/cli/base.rb +28 -9
- data/lib/rascal/cli/clean.rb +9 -2
- data/lib/rascal/cli/main.rb +25 -3
- data/lib/rascal/cli/update.rb +24 -0
- data/lib/rascal/docker/container.rb +54 -17
- data/lib/rascal/docker/interface.rb +21 -53
- data/lib/rascal/docker/volume.rb +16 -2
- data/lib/rascal/environment.rb +15 -7
- data/lib/rascal/environments_definition/gitlab.rb +38 -13
- data/lib/rascal/service.rb +9 -3
- data/lib/rascal/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd535de9c0092cf4ab69d5e279e8cb0e19ec682d74e915844a781db8e7e2bb53
|
4
|
+
data.tar.gz: ce84b237c9302f56f810a03b3bf95711b279f3a931687d5d29bd21bbb4f325ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2d95fdaf90bab2efc7e3987d142f37653526f30e6026a8a2d056cf85c8bb780b47251d85e8102faac8a0d4b715459c1dcec2d7e5f20d8fb413150778872844d
|
7
|
+
data.tar.gz: c5f35334569ea8be40d4f474aef6734c52289f33517dc7c96cbae432a2c02fccbb5bd906c1273ff077ddb21be89dcbd29b3a729c96853f26afb07dea9439e8a0
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,32 @@ All notable changes to this project will be documented here.
|
|
4
4
|
|
5
5
|
Rascal follows semantic versioning. This has little consequence pre 1.0, so expect breaking changes.
|
6
6
|
|
7
|
+
## 0.3.2 (2020-10-23)
|
8
|
+
|
9
|
+
- Fix broken `rascal shell` command.
|
10
|
+
|
11
|
+
|
12
|
+
## 0.3.1 (2020-10-01)
|
13
|
+
|
14
|
+
- Pass env variables to service containers.
|
15
|
+
|
16
|
+
|
17
|
+
## 0.3.0 (2020-09-23)
|
18
|
+
|
19
|
+
- Mount /repo into all service volumes
|
20
|
+
|
21
|
+
|
22
|
+
## 0.2.1 (2019-04-10)
|
23
|
+
|
24
|
+
- Prefix names with directory name, to avoid conflicts between projects.
|
25
|
+
|
26
|
+
|
27
|
+
## 0.2.0 (2019-04-10)
|
28
|
+
|
29
|
+
- Add `--all` flag for `rascal clean`.
|
30
|
+
- Add `rascal update` command.
|
31
|
+
- Allow rascal specific per job config.
|
32
|
+
|
7
33
|
|
8
34
|
## 0.1.0 (2019-03-21)
|
9
35
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -30,14 +30,18 @@ You need to add some extra information to your `.gitlab-ci.yml`. A working versi
|
|
30
30
|
|
31
31
|
```
|
32
32
|
# settings here override job settings
|
33
|
-
.rascal:
|
34
|
-
repo_dir: /repo
|
35
|
-
variables:
|
33
|
+
.rascal: # add rascal specific config here
|
34
|
+
repo_dir: /repo # /repo is the default
|
35
|
+
variables: # extra env variables
|
36
36
|
BUNDLE_PATH: /cache/bundle
|
37
|
-
volumes:
|
38
|
-
cache: /cache
|
37
|
+
volumes: # mount these volumes
|
38
|
+
cache: /cache # we will always mount a /builds volume
|
39
39
|
before_shell:
|
40
|
-
- bundle check
|
40
|
+
- bundle check # run this when starting a shell
|
41
|
+
jobs:
|
42
|
+
rspec: # override settings for a specific job
|
43
|
+
variables:
|
44
|
+
BUNDLE_GEMFILE = /repo/Gemfile
|
41
45
|
|
42
46
|
.environment: &environment
|
43
47
|
image: registry.makandra.de/makandra/ci-images/test-env:2.5
|
@@ -53,7 +57,6 @@ You need to add some extra information to your `.gitlab-ci.yml`. A working versi
|
|
53
57
|
variables:
|
54
58
|
BUNDLE_PATH: ./bundle/vendor
|
55
59
|
DATABASE_URL: postgresql://pg_user@pg-db/test-db
|
56
|
-
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: "true"
|
57
60
|
REDIS_URL: redis://redis
|
58
61
|
PROMPT: CI env
|
59
62
|
cache:
|
@@ -73,6 +76,27 @@ rspec:
|
|
73
76
|
Then, in your project root, run `rascal shell rspec`.
|
74
77
|
|
75
78
|
|
79
|
+
### Commands
|
80
|
+
|
81
|
+
#### rascal shell <job>
|
82
|
+
|
83
|
+
Start a docker container (plus required services) and open an interactive shell.
|
84
|
+
|
85
|
+
Currently requires a "bash" to exist.
|
86
|
+
|
87
|
+
|
88
|
+
#### rascal clean <job> | --all [--volumes]
|
89
|
+
|
90
|
+
Stop and remove all created containers, services, networks for either the given or all jobs.
|
91
|
+
|
92
|
+
If `--volumes` is given, also remove all cached volumes.
|
93
|
+
|
94
|
+
|
95
|
+
#### rascal update <job> | --all
|
96
|
+
|
97
|
+
Update all images for the given job, or all jobs.
|
98
|
+
|
99
|
+
|
76
100
|
## License
|
77
101
|
|
78
102
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/rascal/cli.rb
CHANGED
@@ -2,9 +2,10 @@ require 'thor'
|
|
2
2
|
|
3
3
|
module Rascal
|
4
4
|
module CLI
|
5
|
-
autoload :Base,
|
6
|
-
autoload :Clean,
|
7
|
-
autoload :Main,
|
8
|
-
autoload :Shell,
|
5
|
+
autoload :Base, 'rascal/cli/base'
|
6
|
+
autoload :Clean, 'rascal/cli/clean'
|
7
|
+
autoload :Main, 'rascal/cli/main'
|
8
|
+
autoload :Shell, 'rascal/cli/shell'
|
9
|
+
autoload :Update, 'rascal/cli/update'
|
9
10
|
end
|
10
11
|
end
|
data/lib/rascal/cli/base.rb
CHANGED
@@ -19,19 +19,38 @@ module Rascal
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def find_environment(environment_name)
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
definition = environment_definition
|
23
|
+
if (environment = definition.environment(environment_name))
|
24
|
+
return environment
|
25
|
+
else
|
26
|
+
available_environments = definition.available_environment_names.join(', ')
|
27
|
+
if environment_name
|
28
|
+
fail_with_error("Unknown environment #{environment_name}. Available: #{available_environments}.")
|
25
29
|
else
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
fail_with_error("Missing environment. Available: #{available_environments}.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each_environment(name, &block)
|
36
|
+
return enum_for(:each_environment) unless block_given?
|
37
|
+
|
38
|
+
if name == :all
|
39
|
+
definition = environment_definition
|
40
|
+
definition.available_environment_names.each do |environment_name|
|
41
|
+
yield definition.environment(environment_name)
|
32
42
|
end
|
43
|
+
else
|
44
|
+
yield find_environment(name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def environment_definition
|
49
|
+
if (definition = EnvironmentsDefinition.detect(config_location))
|
50
|
+
definition
|
33
51
|
else
|
34
52
|
fail_with_error("Could not find an environment definition in current working directory.")
|
53
|
+
nil
|
35
54
|
end
|
36
55
|
end
|
37
56
|
end
|
data/lib/rascal/cli/clean.rb
CHANGED
@@ -4,12 +4,19 @@ module Rascal
|
|
4
4
|
module CLI
|
5
5
|
class Clean < Base
|
6
6
|
def initialize(thor, options, environment_name)
|
7
|
-
@environment_name =
|
7
|
+
@environment_name = if options[:all]
|
8
|
+
fail_with_error('Cannot give --all and an environment name!') if environment_name
|
9
|
+
:all
|
10
|
+
else
|
11
|
+
environment_name
|
12
|
+
end
|
8
13
|
super(thor, options)
|
9
14
|
end
|
10
15
|
|
11
16
|
def run
|
12
|
-
|
17
|
+
each_environment(@environment_name) do |environment|
|
18
|
+
environment.clean(clean_volumes: @options[:volumes])
|
19
|
+
end
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
data/lib/rascal/cli/main.rb
CHANGED
@@ -31,15 +31,37 @@ module Rascal
|
|
31
31
|
map "shell" => "_shell"
|
32
32
|
desc 'shell ENVIRONMENT', 'Start a docker shell for the given environment'
|
33
33
|
def _shell(environment_name = nil)
|
34
|
-
|
34
|
+
handle_error do
|
35
|
+
Shell.new(self, options, environment_name).run
|
36
|
+
end
|
35
37
|
end
|
36
38
|
|
37
39
|
desc 'clean ENVIRONMENT', 'Stop and remove docker containers for the given environment'
|
40
|
+
method_option :volumes, type: :boolean, default: false, desc: 'Remove (cache) volumes'
|
41
|
+
method_option :all, type: :boolean, default: false, desc: 'Clean all environments'
|
38
42
|
def clean(environment_name = nil)
|
39
|
-
|
43
|
+
handle_error do
|
44
|
+
Clean.new(self, options, environment_name).run
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'update ENVIRONMENT', 'Update all docker images'
|
49
|
+
method_option :all, type: :boolean, default: false, desc: 'update all available environments'
|
50
|
+
def update(environment_name = nil)
|
51
|
+
handle_error do
|
52
|
+
Update.new(self, options, environment_name).run
|
53
|
+
end
|
40
54
|
end
|
41
55
|
|
42
|
-
class_option :config_file, aliases: ['-c'], default: '.', required: true,
|
56
|
+
class_option :config_file, aliases: ['-c'], default: '.', required: true, desc: 'path to configuration file or directory containing it'
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def handle_error
|
61
|
+
yield
|
62
|
+
rescue Rascal::Error => e
|
63
|
+
raise Thor::Error, e.message
|
64
|
+
end
|
43
65
|
end
|
44
66
|
end
|
45
67
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rascal'
|
2
|
+
|
3
|
+
module Rascal
|
4
|
+
module CLI
|
5
|
+
class Update < Base
|
6
|
+
def initialize(thor, options, environment_name)
|
7
|
+
@environment_name = if options[:all]
|
8
|
+
fail_with_error('Cannot give --all and an environment name!') if environment_name
|
9
|
+
:all
|
10
|
+
else
|
11
|
+
environment_name
|
12
|
+
end
|
13
|
+
super(thor, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
images = []
|
18
|
+
each_environment(@environment_name) do |environment|
|
19
|
+
images += environment.update(skip: images)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -3,6 +3,8 @@ module Rascal
|
|
3
3
|
class Container
|
4
4
|
include IOHelper
|
5
5
|
|
6
|
+
attr_reader :image
|
7
|
+
|
6
8
|
def initialize(name, image)
|
7
9
|
@name = name
|
8
10
|
@prefixed_name = "#{NAME_PREFIX}#{name}"
|
@@ -26,7 +28,7 @@ module Rascal
|
|
26
28
|
'container',
|
27
29
|
'inspect',
|
28
30
|
id,
|
29
|
-
output: :json
|
31
|
+
output: :json,
|
30
32
|
).first
|
31
33
|
!!container_info.dig('State', 'Running')
|
32
34
|
else
|
@@ -38,9 +40,9 @@ module Rascal
|
|
38
40
|
!!id
|
39
41
|
end
|
40
42
|
|
41
|
-
def start(network: nil, network_alias: nil)
|
43
|
+
def start(network: nil, network_alias: nil, volumes: [], env: {})
|
42
44
|
say "Starting container for #{@name}"
|
43
|
-
create(network: network, network_alias: network_alias) unless exists?
|
45
|
+
create(network: network, network_alias: network_alias, volumes: volumes, env: env) unless exists?
|
44
46
|
Docker.interface.run(
|
45
47
|
'container',
|
46
48
|
'start',
|
@@ -48,34 +50,65 @@ module Rascal
|
|
48
50
|
)
|
49
51
|
end
|
50
52
|
|
51
|
-
def create(network: nil, network_alias: nil)
|
53
|
+
def create(network: nil, network_alias: nil, volumes: [], env: {})
|
52
54
|
@id = Docker.interface.run(
|
53
55
|
'container',
|
54
56
|
'create',
|
55
57
|
'--name', @prefixed_name,
|
58
|
+
*(volumes.flat_map { |v| ['-v', v.to_param] }),
|
59
|
+
*env_args(env),
|
56
60
|
*(['--network', network.id] if network),
|
57
61
|
*(['--network-alias', network_alias] if network_alias),
|
58
62
|
@image,
|
59
|
-
output: :id
|
63
|
+
output: :id,
|
60
64
|
)
|
61
65
|
end
|
62
66
|
|
63
67
|
def run_and_attach(*command, env: {}, network: nil, volumes: [], working_dir: nil, allow_failure: false)
|
64
|
-
Docker.interface.run_and_attach(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
Docker.interface.run_and_attach(
|
69
|
+
'container',
|
70
|
+
'run',
|
71
|
+
'--rm',
|
72
|
+
'-a', 'STDOUT',
|
73
|
+
'-a', 'STDERR',
|
74
|
+
'-a', 'STDIN',
|
75
|
+
'--interactive',
|
76
|
+
'--tty',
|
77
|
+
*(['-w', working_dir] if working_dir),
|
78
|
+
*(volumes.flat_map { |v| ['-v', v.to_param] }),
|
79
|
+
*env_args(env),
|
80
|
+
*(['--network', network.id] if network),
|
81
|
+
@image,
|
82
|
+
*command,
|
83
|
+
redirect_io: {
|
84
|
+
out: stdout,
|
85
|
+
err: stderr,
|
86
|
+
in: stdin,
|
87
|
+
},
|
88
|
+
allow_failure: allow_failure,
|
73
89
|
)
|
74
90
|
end
|
75
91
|
|
76
92
|
def clean
|
77
|
-
|
78
|
-
|
93
|
+
if running?
|
94
|
+
say "Stopping container for #{@name}"
|
95
|
+
stop_container
|
96
|
+
end
|
97
|
+
if exists?
|
98
|
+
say "Removing container for #{@name}"
|
99
|
+
remove_container
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def update(skip: [])
|
104
|
+
return if skip.include?(@image)
|
105
|
+
say "Updating image #{@image}"
|
106
|
+
Docker.interface.run(
|
107
|
+
'pull',
|
108
|
+
@image,
|
109
|
+
stdout: stdout,
|
110
|
+
)
|
111
|
+
@image
|
79
112
|
end
|
80
113
|
|
81
114
|
private
|
@@ -87,7 +120,7 @@ module Rascal
|
|
87
120
|
'--all',
|
88
121
|
'--quiet',
|
89
122
|
'--filter', "name=^/#{@prefixed_name}$",
|
90
|
-
output: :id
|
123
|
+
output: :id,
|
91
124
|
)
|
92
125
|
end
|
93
126
|
|
@@ -116,6 +149,10 @@ module Rascal
|
|
116
149
|
id,
|
117
150
|
)
|
118
151
|
end
|
152
|
+
|
153
|
+
def env_args(env)
|
154
|
+
env.flat_map { |key, value| ['-e', "#{key}=#{value}"] }
|
155
|
+
end
|
119
156
|
end
|
120
157
|
end
|
121
158
|
end
|
@@ -6,7 +6,7 @@ module Rascal
|
|
6
6
|
class Interface
|
7
7
|
class Error < Rascal::Error; end
|
8
8
|
|
9
|
-
def run(*command, output: :ignore, stdout: nil,
|
9
|
+
def run(*command, output: :ignore, stdout: nil, allow_failure: false)
|
10
10
|
save_stdout = ''
|
11
11
|
save_stderr = ''
|
12
12
|
exit_status = nil
|
@@ -22,60 +22,11 @@ module Rascal
|
|
22
22
|
unless allow_failure || exit_status.success?
|
23
23
|
raise Error, "docker command '#{command.join(' ')}' failed with error:\n#{save_stderr}"
|
24
24
|
end
|
25
|
-
|
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
|
25
|
+
parse_output(output, save_stdout, save_stderr, command: command.join(' '))
|
39
26
|
end
|
40
27
|
|
41
|
-
def run_and_attach(
|
42
|
-
|
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
|
-
)
|
28
|
+
def run_and_attach(*command, stdout: nil, stderr: nil, stdin: nil, env: {}, network: nil, volumes: [], working_dir: nil, redirect_io: {}, allow_failure: false)
|
29
|
+
exit_status = spawn(env, 'docker', *stringify_command(command), redirect_io)
|
79
30
|
unless allow_failure || exit_status.success?
|
80
31
|
raise Error, "docker container run failed"
|
81
32
|
end
|
@@ -106,6 +57,23 @@ module Rascal
|
|
106
57
|
rescue IOError
|
107
58
|
end
|
108
59
|
end
|
60
|
+
|
61
|
+
def parse_output(output, stdout, stderr, command:)
|
62
|
+
case output
|
63
|
+
when :json
|
64
|
+
begin
|
65
|
+
JSON.parse(stdout)
|
66
|
+
rescue JSON::ParserError
|
67
|
+
raise Error, "could not parse output of docker command '#{command}':\n#{stdout}"
|
68
|
+
end
|
69
|
+
when :id
|
70
|
+
stdout[/[0-9a-f]+/]
|
71
|
+
when :ignore
|
72
|
+
nil
|
73
|
+
else
|
74
|
+
raise ArgumentError, 'unknown option for :output'
|
75
|
+
end
|
76
|
+
end
|
109
77
|
end
|
110
78
|
end
|
111
79
|
end
|
data/lib/rascal/docker/volume.rb
CHANGED
@@ -2,6 +2,7 @@ module Rascal
|
|
2
2
|
module Docker
|
3
3
|
module Volume
|
4
4
|
class Base
|
5
|
+
include IOHelper
|
5
6
|
end
|
6
7
|
|
7
8
|
class Named < Base
|
@@ -15,10 +16,23 @@ module Rascal
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def clean
|
19
|
+
if exists?
|
20
|
+
say "Removing volume #{@prefixed_name}"
|
21
|
+
Docker.interface.run(
|
22
|
+
'volume',
|
23
|
+
'rm',
|
24
|
+
@prefixed_name,
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def exists?
|
18
30
|
Docker.interface.run(
|
19
31
|
'volume',
|
20
|
-
'
|
21
|
-
|
32
|
+
'ls',
|
33
|
+
'--quiet',
|
34
|
+
'--filter', "name=^#{@prefixed_name}$",
|
35
|
+
output: :id,
|
22
36
|
)
|
23
37
|
end
|
24
38
|
end
|
data/lib/rascal/environment.rb
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
module Rascal
|
2
2
|
class Environment
|
3
|
-
attr_reader :name
|
3
|
+
attr_reader :name, :network, :container, :env_variables, :services, :volumes, :working_dir, :before_shell
|
4
4
|
|
5
|
-
def initialize(
|
5
|
+
def initialize(full_name, name:, image:, env_variables: {}, services: [], volumes: [], before_shell: [], after_shell: [], working_dir: nil)
|
6
6
|
@name = name
|
7
|
-
@network = Docker::Network.new(
|
8
|
-
@container = Docker::Container.new(
|
7
|
+
@network = Docker::Network.new(full_name)
|
8
|
+
@container = Docker::Container.new(full_name, image)
|
9
9
|
@env_variables = env_variables
|
10
10
|
@services = services
|
11
11
|
@volumes = volumes
|
12
12
|
@working_dir = working_dir
|
13
13
|
@before_shell = before_shell
|
14
|
+
@after_shell = after_shell
|
14
15
|
end
|
15
16
|
|
16
17
|
def run_shell
|
17
18
|
download_missing
|
18
19
|
start_services
|
19
|
-
command = [*@before_shell, 'bash'].join(';')
|
20
|
+
command = [*@before_shell, 'bash', *@after_shell].join(';')
|
20
21
|
@container.run_and_attach('bash', '-c', command,
|
21
22
|
env: @env_variables,
|
22
23
|
network: @network,
|
@@ -26,10 +27,17 @@ module Rascal
|
|
26
27
|
)
|
27
28
|
end
|
28
29
|
|
29
|
-
def clean
|
30
|
+
def clean(clean_volumes: false)
|
30
31
|
@services.each(&:clean)
|
31
32
|
@network.clean
|
32
|
-
@volumes.each(&:clean)
|
33
|
+
@volumes.each(&:clean) if clean_volumes
|
34
|
+
end
|
35
|
+
|
36
|
+
def update(skip: [])
|
37
|
+
[
|
38
|
+
*@services.collect { |s| s.update(skip: skip) },
|
39
|
+
*@container.update(skip: skip),
|
40
|
+
]
|
33
41
|
end
|
34
42
|
|
35
43
|
private
|
@@ -35,7 +35,9 @@ module Rascal
|
|
35
35
|
def initialize(config_path)
|
36
36
|
@info = parse_definition(config_path.read)
|
37
37
|
@repo_dir = config_path.parent
|
38
|
+
@base_name = @repo_dir.basename
|
38
39
|
@rascal_config = @info.fetch('.rascal', {})
|
40
|
+
@rascal_environment_config = @rascal_config.delete('jobs') || {}
|
39
41
|
end
|
40
42
|
|
41
43
|
def environment(name)
|
@@ -51,21 +53,34 @@ module Rascal
|
|
51
53
|
private
|
52
54
|
|
53
55
|
def parse_definition(yaml)
|
54
|
-
|
56
|
+
if Psych::VERSION >= '3.1'
|
57
|
+
YAML.safe_load(yaml, aliases: true)
|
58
|
+
else
|
59
|
+
YAML.safe_load(yaml, [], [], true)
|
60
|
+
end
|
55
61
|
end
|
56
62
|
|
57
63
|
def environments
|
58
64
|
@environments ||= begin
|
59
65
|
@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
|
-
|
66
|
+
config = Config.new(deep_merge(environment_config, @rascal_config, @rascal_environment_config[key] || {}), key)
|
67
|
+
docker_repo_dir = config.get('repo_dir', '/repo')
|
68
|
+
unless key.start_with?('.') || config.get('hide', false)
|
69
|
+
name = config.get('name', key)
|
70
|
+
full_name = "#{@base_name}-#{name}"
|
71
|
+
shared_volumes = [build_repo_volume(docker_repo_dir), build_builds_volume(full_name)]
|
72
|
+
env_variables = (config.get('variables', {}))
|
73
|
+
Environment.new(full_name,
|
74
|
+
name: name,
|
64
75
|
image: config.get('image'),
|
65
|
-
|
66
|
-
|
67
|
-
|
76
|
+
volumes: [
|
77
|
+
*shared_volumes,
|
78
|
+
*build_volumes(full_name, config.get('volumes', {}))
|
79
|
+
],
|
80
|
+
env_variables: env_variables,
|
81
|
+
services: build_services(full_name, config.get('services', []), volumes: shared_volumes, env_variables: env_variables),
|
68
82
|
before_shell: config.get('before_shell', []),
|
83
|
+
after_shell: config.get('after_shell', []),
|
69
84
|
working_dir: docker_repo_dir,
|
70
85
|
)
|
71
86
|
end
|
@@ -73,9 +88,9 @@ module Rascal
|
|
73
88
|
end
|
74
89
|
end
|
75
90
|
|
76
|
-
def deep_merge(hash1, hash2)
|
91
|
+
def deep_merge(hash1, hash2, *other)
|
92
|
+
result = {}
|
77
93
|
if hash1.is_a?(Hash) && hash2.is_a?(Hash)
|
78
|
-
result = {}
|
79
94
|
hash1.each do |key1, value1|
|
80
95
|
if hash2.has_key?(key1)
|
81
96
|
result[key1] = deep_merge(value1, hash2[key1])
|
@@ -86,18 +101,24 @@ module Rascal
|
|
86
101
|
hash2.each do |key2, value2|
|
87
102
|
result[key2] ||= value2
|
88
103
|
end
|
89
|
-
result
|
90
104
|
else
|
91
|
-
hash2
|
105
|
+
result = hash2
|
106
|
+
end
|
107
|
+
if other.any?
|
108
|
+
deep_merge(result, *other)
|
109
|
+
else
|
110
|
+
result
|
92
111
|
end
|
93
112
|
end
|
94
113
|
|
95
|
-
def build_services(name, services)
|
114
|
+
def build_services(name, services, volumes: [], env_variables: {})
|
96
115
|
services.collect do |service_config|
|
97
116
|
service_alias = service_config['alias']
|
98
117
|
Service.new("#{name}_#{service_alias}",
|
99
118
|
alias_name: service_config['alias'],
|
100
119
|
image: service_config['name'],
|
120
|
+
volumes: volumes,
|
121
|
+
env_variables: env_variables,
|
101
122
|
)
|
102
123
|
end
|
103
124
|
end
|
@@ -106,6 +127,10 @@ module Rascal
|
|
106
127
|
Docker::Volume::Bind.new(@repo_dir, docker_repo_dir)
|
107
128
|
end
|
108
129
|
|
130
|
+
def build_builds_volume(name)
|
131
|
+
Docker::Volume::Named.new("#{name}-builds", '/builds')
|
132
|
+
end
|
133
|
+
|
109
134
|
def build_volumes(name, volume_config)
|
110
135
|
volume_config.collect do |volume_name, docker_path|
|
111
136
|
Docker::Volume::Named.new("#{name}-#{volume_name}", docker_path)
|
data/lib/rascal/service.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
module Rascal
|
2
2
|
class Service
|
3
|
-
attr_reader :name
|
3
|
+
attr_reader :name, :container, :alias, :env_variables
|
4
4
|
|
5
|
-
def initialize(name, image:, alias_name:)
|
5
|
+
def initialize(name, env_variables: {}, image:, alias_name:, volumes: [])
|
6
6
|
@name = name
|
7
7
|
@container = Docker::Container.new(name, image)
|
8
8
|
@alias = alias_name
|
9
|
+
@volumes = volumes
|
10
|
+
@env_variables = env_variables
|
9
11
|
end
|
10
12
|
|
11
13
|
def download_missing
|
@@ -14,12 +16,16 @@ module Rascal
|
|
14
16
|
|
15
17
|
def start_if_stopped(network: nil)
|
16
18
|
unless @container.running?
|
17
|
-
@container.start(network: network, network_alias: @alias)
|
19
|
+
@container.start(network: network, network_alias: @alias, volumes: @volumes, env: @env_variables)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
23
|
def clean
|
22
24
|
@container.clean
|
23
25
|
end
|
26
|
+
|
27
|
+
def update(**args)
|
28
|
+
@container.update(**args)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
end
|
data/lib/rascal/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rascal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Kraze
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/rascal/cli/clean.rb
|
57
57
|
- lib/rascal/cli/main.rb
|
58
58
|
- lib/rascal/cli/shell.rb
|
59
|
+
- lib/rascal/cli/update.rb
|
59
60
|
- lib/rascal/docker.rb
|
60
61
|
- lib/rascal/docker/container.rb
|
61
62
|
- lib/rascal/docker/interface.rb
|
@@ -90,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
91
|
- !ruby/object:Gem::Version
|
91
92
|
version: '0'
|
92
93
|
requirements: []
|
93
|
-
rubygems_version: 3.
|
94
|
+
rubygems_version: 3.1.4
|
94
95
|
signing_key:
|
95
96
|
specification_version: 4
|
96
97
|
summary: Spin up CI environments locally.
|