devbox_launcher 0.3.2 → 0.3.3

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: 25588592982c23d00fa42fbd84265d18c9f113ee46bcdb64e9c458115a0b65dc
4
- data.tar.gz: 00560c6344d2f39489f4f6acdb9afc442a90b3d2dc98b98d5ca47fd0085980e2
3
+ metadata.gz: 7ed54fc2716507cd48b6e65acb08d38f1399c7d2a626a898ea0bdbeb70317f9d
4
+ data.tar.gz: 13b0d4ba4c3b9ae2515040bddff831b746de65c1296d57be20df82bc7f99f722
5
5
  SHA512:
6
- metadata.gz: db2e46803d38b20624480c3dd8dfd9eadcd7fe315841ff6e57fd8b4a70e3752784d38917f8b7d00d41cca8feaadb3bcf44817e3daa7c7885aebdac007ff5fb54
7
- data.tar.gz: 7f1a8c4f5eefd21c6cf32eb0d041b36ac8f495670e9440dfe0079445205c8b02d53db524a182f3c3d7764fe475613552a3e55e064ee52b354103e28045921b1a
6
+ metadata.gz: 0c9720ff3bc730ad33b4d225a24796eca616615534d23f5d01c57be8c8291f0b37a0fc2d13092b2f84799c66ea513d6cc3b00bef3fd3e3559fe8d6ea107596e2
7
+ data.tar.gz: a040326a3fe949841970a3d5fd26535dc2cbf06bec09bd30098f63744ff87ac7fd1184b3a79d4848571681b7cad7c91c6ebe89753781f6126c8b5372619790de
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.3.3]
8
+ ### Fixed
9
+ - Support launching multiple boxes at the same time
10
+
7
11
  ## [0.3.2]
8
12
  ### Fixed
9
13
  - Fix: add missing file
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- devbox_launcher (0.3.2)
4
+ devbox_launcher (0.3.3)
5
5
  activesupport (~> 6.0)
6
6
  bcrypt_pbkdf (~> 1.0)
7
7
  ed25519 (~> 1.2)
@@ -17,3 +17,5 @@ end
17
17
  require "devbox_launcher/cli"
18
18
  require "devbox_launcher/watchman"
19
19
  require "devbox_launcher/models/description"
20
+ require "devbox_launcher/models/mutagen"
21
+ require "devbox_launcher/models/box"
@@ -0,0 +1,205 @@
1
+ module DevboxLauncher
2
+ class Box
3
+
4
+ WAIT_BOOT_RESCUED_EXCEPTIONS = [
5
+ Net::SSH::ConnectionTimeout,
6
+ Net::SSH::Disconnect,
7
+ Errno::ECONNRESET,
8
+ Errno::ETIMEDOUT,
9
+ ]
10
+ WAIT_BOOT_IN_SECONDS = 10.freeze
11
+ DEFAULT_IDENTIFY_FILE_PATH = "~/.ssh/google_compute_engine".freeze
12
+ SSH_CONFIG_PATH = File.expand_path("~/.ssh/config").freeze
13
+ CONFIG_PATH = File.expand_path("~/.devbox_launcher.yml").freeze
14
+ CONFIG = YAML.load_file(CONFIG_PATH).freeze
15
+
16
+ attr_reader :account
17
+
18
+ def initialize(account)
19
+ @account = account
20
+ end
21
+
22
+ def start
23
+ start_stdout, start_stderr, start_status =
24
+ Open3.capture3(start_cmd)
25
+
26
+ set_ssh_config!(hostname, {
27
+ username: username,
28
+ ip: description.ip,
29
+ })
30
+
31
+ wait_boot
32
+
33
+ reset_mutagen_session(
34
+ mutagen_config: config[:mutagen],
35
+ hostname: hostname,
36
+ )
37
+ end
38
+
39
+ def start_cmd
40
+ args = {
41
+ project: config[:project],
42
+ account: account,
43
+ }.map do |(key, val)|
44
+ ["--#{key}", val].join("=")
45
+ end.join(" ")
46
+
47
+ [
48
+ "gcloud",
49
+ "compute",
50
+ "instances",
51
+ "start",
52
+ name,
53
+ args
54
+ ].join(" ")
55
+ end
56
+
57
+ def wait_boot(tries: 1)
58
+ Net::SSH.start(hostname, username, timeout: WAIT_BOOT_IN_SECONDS) do |ssh|
59
+ puts "[#{ssh.exec!('date').chomp}] Machine booted"
60
+ end
61
+ rescue *WAIT_BOOT_RESCUED_EXCEPTIONS
62
+ puts "Not booted. Waiting #{WAIT_BOOT_IN_SECONDS} seconds before trying again..."
63
+
64
+ sleep WAIT_BOOT_IN_SECONDS
65
+
66
+ description = describe(name)
67
+ if !description.running?
68
+ puts "Detected that the machine is not running " \
69
+ "(status is #{description.status}). Booting it..."
70
+ start_box name, username
71
+ end
72
+
73
+ wait_boot name, username, tries: tries+1
74
+ end
75
+
76
+ def description(reload: false)
77
+ return @description if !reload && @description
78
+
79
+ puts "Fetching box's description..."
80
+
81
+ describe_command = %Q(gcloud compute instances describe #{name})
82
+ describe_stdout, describe_stderr, describe_status =
83
+ Open3.capture3(describe_command)
84
+
85
+ if !describe_status.success?
86
+ msg = "Problem fetching the description of #{name}. "
87
+ msg += "Please ensure you can call `#{describe_command}`.\n"
88
+ msg += "Error:\n"
89
+ msg += describe_stderr
90
+ fail msg
91
+ end
92
+
93
+ @description = Description.new(describe_stdout)
94
+ end
95
+
96
+ def set_ssh_config!(hostname, username:, ip:)
97
+ FileUtils.touch(SSH_CONFIG_PATH)
98
+ config = ConfigFile.new
99
+ args = {
100
+ "HostName" => ip,
101
+ "User" => username,
102
+ "IdentityFile" => DEFAULT_IDENTIFY_FILE_PATH,
103
+ }
104
+ args.each do |key, value|
105
+ config.set(hostname, key, value)
106
+ end
107
+ config.save
108
+ end
109
+
110
+ def reset_mutagen_session
111
+ mutagen_config = config[:mutagen]
112
+ return if mutagen_config.nil?
113
+
114
+ alpha_dir = mutagen_config[:alpha]
115
+ beta_dir = mutagen_config[:beta]
116
+
117
+ return if alpha_dir.nil? || beta_dir.nil?
118
+
119
+ terminate_mutagen_session
120
+
121
+ create_mutagen_session(
122
+ alpha_dir: alpha_dir,
123
+ beta_dir: beta_dir,
124
+ hostname: hostname,
125
+ username: username,
126
+ )
127
+
128
+ if OS.linux?
129
+ watch_alpha(alpha_dir: alpha_dir, hostname: hostname)
130
+ end
131
+ end
132
+
133
+ def terminate_mutagen_session(username)
134
+ puts "Terminating mutagen session..."
135
+ terminate_mutagen_command =
136
+ %Q(mutagen terminate --label-selector=#{username})
137
+ terminate_mutagen_stdout,
138
+ terminate_mutagen_stderr,
139
+ terminate_mutagen_status =
140
+ Open3.capture3(terminate_mutagen_command)
141
+
142
+ if not terminate_mutagen_status.success?
143
+ # mutagen prints to stdout and stderr
144
+ msg = "Failed to terminate mutagen sessions: " \
145
+ "#{terminate_mutagen_stdout} -" \
146
+ "#{terminate_mutagen_stderr}"
147
+ fail msg
148
+ end
149
+ end
150
+
151
+ def create_mutagen_session(alpha_dir:, beta_dir:, hostname:, username:)
152
+ puts "Create mutagen session syncing local #{alpha_dir} " \
153
+ "with #{hostname} #{beta_dir}"
154
+
155
+ create_mutagen_command = [
156
+ "mutagen sync create",
157
+ alpha_dir,
158
+ "#{hostname}:#{beta_dir}",
159
+ "--label=#{username}",
160
+ ]
161
+ create_mutagen_command << "--watch-mode-alpha=no-watch" if OS.linux?
162
+
163
+ create_mutagen_stdout,
164
+ create_mutagen_stderr,
165
+ create_mutagen_status =
166
+ Open3.capture3(create_mutagen_command.join(" "))
167
+
168
+ if not create_mutagen_status.success?
169
+ # mutagen prints to stdout and stderr
170
+ msg = "Failed to create mutagen sessions: " \
171
+ "#{create_mutagen_stdout} -" \
172
+ "#{create_mutagen_stderr}"
173
+ fail msg
174
+ end
175
+ end
176
+
177
+ def watch_alpha(alpha_dir:, hostname:)
178
+ watchman = Watchman.new(dir: alpha_dir)
179
+ watchman.trigger("mutagen sync flush --label-selector=#{hostname}")
180
+ end
181
+
182
+ def name
183
+ @name ||= config[:box]
184
+ end
185
+
186
+ def hostname
187
+ [name, username, "devbox"].join("-")
188
+ end
189
+
190
+ def username
191
+ @username ||= account.gsub(/\W/, "_")
192
+ end
193
+
194
+ def config
195
+ return @config if @config
196
+
197
+ if not CONFIG.has_key?(account)
198
+ fail "No config in #{CONFIG_PATH} found for #{account}"
199
+ end
200
+
201
+ @config = CONFIG[account].with_indifferent_access
202
+ end
203
+
204
+ end
205
+ end
@@ -6,186 +6,12 @@ module DevboxLauncher
6
6
  SSH_CONFIG_PATH = File.expand_path("~/.ssh/config").freeze
7
7
  CONFIG_PATH = File.expand_path("~/.devbox_launcher.yml").freeze
8
8
  CONFIG = YAML.load_file(CONFIG_PATH).freeze
9
- LABEL = "devbox".freeze
10
9
 
11
10
  desc "start configured box for account", "Start a devbox by account"
12
11
  option :mosh, type: :boolean, desc: "Mosh in"
13
12
 
14
13
  def start(account)
15
- if not CONFIG.has_key?(account)
16
- fail "No config in #{CONFIG_PATH} found for #{account}"
17
- end
18
-
19
- config = CONFIG[account].with_indifferent_access
20
- name = config[:box]
21
-
22
- username = account.gsub(/\W/, "_")
23
-
24
- puts "Starting #{name}..."
25
-
26
- set_account_command = %Q(gcloud config set account #{account})
27
- set_account_stdout, set_account_stderr, set_account_status =
28
- Open3.capture3(set_account_command)
29
-
30
- set_project_command = %Q(gcloud config set project #{config[:project]})
31
- set_project_stdout, set_project_stderr, set_project_status =
32
- Open3.capture3(set_project_command)
33
-
34
- start_box name, username
35
-
36
- wait_boot(name, username)
37
-
38
- hostname = hostname_for(name)
39
-
40
- reset_mutagen_session(
41
- mutagen_config: config[:mutagen],
42
- hostname: hostname,
43
- )
44
-
45
- if options[:mosh]
46
- mosh_command = %Q(mosh #{hostname})
47
- system(mosh_command)
48
- end
49
- end
50
-
51
- no_commands do
52
- def wait_boot(name, username, tries: 1)
53
- hostname = hostname_for(name)
54
-
55
- Net::SSH.start(hostname, username, timeout: WAIT_BOOT_IN_SECONDS) do |ssh|
56
- puts "[#{ssh.exec!('date').chomp}] Machine booted"
57
- end
58
- rescue Net::SSH::ConnectionTimeout, Net::SSH::Disconnect, Errno::ECONNRESET
59
- puts "Not booted. Waiting #{WAIT_BOOT_IN_SECONDS} seconds before trying again..."
60
-
61
- sleep WAIT_BOOT_IN_SECONDS
62
-
63
- description = describe(name)
64
- if !description.running?
65
- puts "Detected that the machine is not running " \
66
- "(status is #{description.status}). Booting it..."
67
- start_box name, username
68
- end
69
-
70
- wait_boot name, username, tries: tries+1
71
- end
72
-
73
- def set_ssh_config!(hostname, username:, ip:)
74
- FileUtils.touch(SSH_CONFIG_PATH)
75
- config = ConfigFile.new
76
- args = {
77
- "HostName" => ip,
78
- "User" => username,
79
- "IdentityFile" => DEFAULT_IDENTIFY_FILE_PATH,
80
- }
81
- args.each do |key, value|
82
- config.set(hostname, key, value)
83
- end
84
- config.save
85
- end
86
-
87
- def reset_mutagen_session(mutagen_config:, hostname:)
88
- return if mutagen_config.nil?
89
- alpha_dir = mutagen_config[:alpha]
90
- beta_dir = mutagen_config[:beta]
91
-
92
- return if alpha_dir.nil? || beta_dir.nil?
93
-
94
- terminate_mutagen_session
95
- create_mutagen_session(
96
- alpha_dir: alpha_dir,
97
- beta_dir: beta_dir,
98
- hostname: hostname,
99
- )
100
-
101
- if OS.linux?
102
- watch_alpha(alpha_dir: alpha_dir)
103
- end
104
- end
105
-
106
- def terminate_mutagen_session
107
- puts "Terminating mutagen session..."
108
- terminate_mutagen_command =
109
- %Q(mutagen terminate --label-selector=#{LABEL})
110
- terminate_mutagen_stdout,
111
- terminate_mutagen_stderr,
112
- terminate_mutagen_status =
113
- Open3.capture3(terminate_mutagen_command)
114
-
115
- if not terminate_mutagen_status.success?
116
- # mutagen prints to stdout and stderr
117
- msg = "Failed to terminate mutagen sessions: " \
118
- "#{terminate_mutagen_stdout} -" \
119
- "#{terminate_mutagen_stderr}"
120
- fail msg
121
- end
122
- end
123
-
124
- def create_mutagen_session(alpha_dir:, beta_dir:, hostname:)
125
- puts "Create mutagen session syncing local #{alpha_dir} " \
126
- "with #{hostname} #{beta_dir}"
127
-
128
- create_mutagen_command = [
129
- "mutagen sync create",
130
- alpha_dir,
131
- "#{hostname}:#{beta_dir}",
132
- "--label=#{LABEL}",
133
- ]
134
- create_mutagen_command << "--watch-mode-alpha=no-watch" if OS.linux?
135
-
136
- create_mutagen_stdout,
137
- create_mutagen_stderr,
138
- create_mutagen_status =
139
- Open3.capture3(create_mutagen_command.join(" "))
140
-
141
- if not create_mutagen_status.success?
142
- # mutagen prints to stdout and stderr
143
- msg = "Failed to create mutagen sessions: " \
144
- "#{create_mutagen_stdout} -" \
145
- "#{create_mutagen_stderr}"
146
- fail msg
147
- end
148
- end
149
-
150
- def watch_alpha(alpha_dir:)
151
- watchman = Watchman.new(dir: alpha_dir)
152
- watchman.trigger("mutagen sync flush --label-selector=#{LABEL}")
153
- end
154
-
155
- def start_box(name, username)
156
- start_command = %Q(gcloud compute instances start #{name})
157
- start_stdout, start_stderr, start_status = Open3.capture3(start_command)
158
-
159
- desc = describe(name)
160
- ip = desc.ip
161
-
162
- set_ssh_config!(hostname_for(name), {
163
- username: username,
164
- ip: ip,
165
- })
166
- end
167
-
168
- def describe(name)
169
- puts "Fetching box's description..."
170
-
171
- describe_command = %Q(gcloud compute instances describe #{name})
172
- describe_stdout, describe_stderr, describe_status =
173
- Open3.capture3(describe_command)
174
-
175
- if !describe_status.success?
176
- msg = "Problem fetching the description of #{name}. "
177
- msg += "Please ensure you can call `#{describe_command}`.\n"
178
- msg += "Error:\n"
179
- msg += describe_stderr
180
- fail msg
181
- end
182
-
183
- Description.new(describe_stdout)
184
- end
185
-
186
- def hostname_for(name)
187
- [name, "devbox"].join("-")
188
- end
14
+ Box.new(account, options).start
189
15
  end
190
16
 
191
17
  end
@@ -0,0 +1,222 @@
1
+ module DevboxLauncher
2
+ class Box
3
+
4
+ WAIT_BOOT_RESCUED_EXCEPTIONS = [
5
+ Net::SSH::ConnectionTimeout,
6
+ Net::SSH::Disconnect,
7
+ Errno::ECONNRESET,
8
+ Errno::ETIMEDOUT,
9
+ ]
10
+ WAIT_BOOT_IN_SECONDS = 10.freeze
11
+ MAX_BOOT_RETRIES = 10
12
+ DEFAULT_IDENTIFY_FILE_PATH = "~/.ssh/google_compute_engine".freeze
13
+ SSH_CONFIG_PATH = File.expand_path("~/.ssh/config").freeze
14
+ CONFIG_PATH = File.expand_path("~/.devbox_launcher.yml").freeze
15
+ CONFIG = YAML.load_file(CONFIG_PATH).freeze
16
+
17
+ attr_reader :account, :options
18
+
19
+ def initialize(account, options)
20
+ @account = account
21
+ @options = options
22
+ end
23
+
24
+ def start
25
+ start_stdout, start_stderr, start_status =
26
+ Open3.capture3(start_cmd)
27
+
28
+ set_ssh_config!
29
+
30
+ wait_boot
31
+
32
+ reset_mutagen_session
33
+
34
+ connect_mosh
35
+ end
36
+
37
+ def start_cmd
38
+ args = {
39
+ project: config[:project],
40
+ account: account,
41
+ }.map do |(key, val)|
42
+ ["--#{key}", val].join("=")
43
+ end.join(" ")
44
+
45
+ [
46
+ "gcloud",
47
+ "compute",
48
+ "instances",
49
+ "start",
50
+ name,
51
+ args
52
+ ].join(" ")
53
+ end
54
+
55
+ def connect_mosh
56
+ return if options[:mosh].nil?
57
+
58
+ mosh_cmd = %Q(mosh #{hostname})
59
+ system(mosh_cmd)
60
+ end
61
+
62
+ def wait_boot(tries: 1)
63
+ Net::SSH.start(hostname, username, timeout: WAIT_BOOT_IN_SECONDS) do |ssh|
64
+ puts "[#{ssh.exec!('date').chomp}] Machine booted"
65
+ end
66
+ rescue *WAIT_BOOT_RESCUED_EXCEPTIONS
67
+ puts "Not booted. Waiting #{WAIT_BOOT_IN_SECONDS} seconds before trying again..."
68
+
69
+ sleep WAIT_BOOT_IN_SECONDS
70
+
71
+ if !description(reload: true).running?
72
+ puts "Detected that the machine is not running " \
73
+ "(status is #{description.status}). Booting it..."
74
+ start
75
+ end
76
+
77
+ fail if tries >= MAX_BOOT_RETRIES
78
+
79
+ wait_boot tries: tries+1
80
+ end
81
+
82
+ def description(reload: false)
83
+ return @description if !reload && @description
84
+
85
+ puts "Fetching box's description..."
86
+
87
+ describe_stdout, describe_stderr, describe_status =
88
+ Open3.capture3(describe_cmd)
89
+
90
+ if !describe_status.success?
91
+ msg = "Problem fetching the description of #{name}. "
92
+ msg += "Please ensure you can call `#{describe_cmd}`.\n"
93
+ msg += "Error:\n"
94
+ msg += describe_stderr
95
+ fail msg
96
+ end
97
+
98
+ @description = Description.new(describe_stdout)
99
+ end
100
+
101
+ def describe_cmd
102
+ args = {
103
+ project: config[:project],
104
+ account: account,
105
+ }.map do |(key, val)|
106
+ ["--#{key}", val].join("=")
107
+ end.join(" ")
108
+
109
+ [
110
+ "gcloud",
111
+ "compute",
112
+ "instances",
113
+ "describe",
114
+ name,
115
+ args
116
+ ].join(" ")
117
+ end
118
+
119
+ def set_ssh_config!
120
+ FileUtils.touch(SSH_CONFIG_PATH)
121
+ config = ConfigFile.new
122
+ args = {
123
+ "HostName" => description.ip,
124
+ "User" => username,
125
+ "IdentityFile" => DEFAULT_IDENTIFY_FILE_PATH,
126
+ }
127
+ args.each do |key, value|
128
+ config.set(hostname, key, value)
129
+ end
130
+ config.save
131
+ end
132
+
133
+ def reset_mutagen_session
134
+ return if !mutagen_config.configured?
135
+
136
+ terminate_mutagen_session
137
+ create_mutagen_session
138
+ watch_alpha if OS.linux?
139
+ end
140
+
141
+ def terminate_mutagen_session
142
+ puts "Terminating mutagen session..."
143
+ terminate_mutagen_cmd =
144
+ %Q(mutagen terminate --label-selector=#{label})
145
+ terminate_mutagen_stdout,
146
+ terminate_mutagen_stderr,
147
+ terminate_mutagen_status =
148
+ Open3.capture3(terminate_mutagen_cmd)
149
+
150
+ if not terminate_mutagen_status.success?
151
+ # mutagen prints to stdout and stderr
152
+ msg = "Failed to terminate mutagen sessions: " \
153
+ "#{terminate_mutagen_stdout} -" \
154
+ "#{terminate_mutagen_stderr}"
155
+ fail msg
156
+ end
157
+ end
158
+
159
+ def label
160
+ "#{username}=#{name}"
161
+ end
162
+
163
+ def create_mutagen_session
164
+ puts "Create mutagen session syncing local " \
165
+ "#{mutagen_config.alpha_dir} with " \
166
+ "#{hostname} #{mutagen_config.beta_dir}"
167
+
168
+ create_mutagen_cmd = [
169
+ "mutagen sync create",
170
+ mutagen_config.alpha_dir,
171
+ "#{hostname}:#{mutagen_config.beta_dir}",
172
+ "--label=#{label}",
173
+ ]
174
+ create_mutagen_cmd << "--watch-mode-alpha=no-watch" if OS.linux?
175
+
176
+ create_mutagen_stdout,
177
+ create_mutagen_stderr,
178
+ create_mutagen_status =
179
+ Open3.capture3(create_mutagen_cmd.join(" "))
180
+
181
+ if not create_mutagen_status.success?
182
+ # mutagen prints to stdout and stderr
183
+ msg = "Failed to create mutagen sessions: " \
184
+ "#{create_mutagen_stdout} -" \
185
+ "#{create_mutagen_stderr}"
186
+ fail msg
187
+ end
188
+ end
189
+
190
+ def watch_alpha
191
+ watchman = Watchman.new(dir: mutagen_config.alpha_dir)
192
+ watchman.trigger("mutagen sync flush --label-selector=#{label}")
193
+ end
194
+
195
+ def name
196
+ @name ||= config[:box]
197
+ end
198
+
199
+ def hostname
200
+ [name, username, "devbox"].join("-")
201
+ end
202
+
203
+ def username
204
+ @username ||= account.gsub(/\W/, "_")
205
+ end
206
+
207
+ def config
208
+ return @config if @config
209
+
210
+ if not CONFIG.has_key?(account)
211
+ fail "No config in #{CONFIG_PATH} found for #{account}"
212
+ end
213
+
214
+ @config = CONFIG[account].with_indifferent_access
215
+ end
216
+
217
+ def mutagen_config
218
+ @mutagen_config ||= Mutagen.new(config[:mutagen])
219
+ end
220
+
221
+ end
222
+ end
@@ -0,0 +1,23 @@
1
+ module DevboxLauncher
2
+ class Mutagen
3
+
4
+ attr_reader :config
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ end
9
+
10
+ def configured?
11
+ [config, alpha_dir, beta_dir].none?(&:nil?)
12
+ end
13
+
14
+ def alpha_dir
15
+ config[:alpha]
16
+ end
17
+
18
+ def beta_dir
19
+ config[:beta]
20
+ end
21
+
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module DevboxLauncher
2
- VERSION = "0.3.2"
2
+ VERSION = "0.3.3"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devbox_launcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramon Tayag
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-25 00:00:00.000000000 Z
11
+ date: 2020-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -189,8 +189,11 @@ files:
189
189
  - bin/setup
190
190
  - devbox_launcher.gemspec
191
191
  - lib/devbox_launcher.rb
192
+ - lib/devbox_launcher/box.rb
192
193
  - lib/devbox_launcher/cli.rb
194
+ - lib/devbox_launcher/models/box.rb
193
195
  - lib/devbox_launcher/models/description.rb
196
+ - lib/devbox_launcher/models/mutagen.rb
194
197
  - lib/devbox_launcher/version.rb
195
198
  - lib/devbox_launcher/watchman.rb
196
199
  homepage: https://github.com/bloom-solutions/devbox_launcher