fly.io-rails 0.1.0-x64-mingw32 → 0.1.2-x64-mingw32

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: 31dbb298f72984f01bd39eb4859577e3442b4f4021b0e099bf0be2e3d7c16134
4
- data.tar.gz: 374cee9b246abb711a1f6954d9a77f04aeada9f47fb6a6441cebd3105a627ced
3
+ metadata.gz: cec3ca4f27cbb576d74009dcdea78a60113a091c85b833efb1f83388bfd37475
4
+ data.tar.gz: 702966a87970bca9d3192dbc8cd76cb94bb5fdf0cfa25f331ea290a02f08f097
5
5
  SHA512:
6
- metadata.gz: 9d6a32e791648274508e64ca20bb3234a42189bff8a9c6031549dcb12b6391b47685c7606c708a97327bb71a058238c76029788d753d263e294ef204a4167b18
7
- data.tar.gz: 68481956ca5448163257265683cc72363f19a83f5eee2fe4e51c3a00baf6b38988336ac439cc87ecd7b4f6773021193248f0850c64b8652bb559edcd9181e649
6
+ metadata.gz: 17a5d4feb8fdfe76af46edb6b6f416c5387aab1d01a6a2e8dcf10d9c37925eafd0a17043f67b5faf3a6d056f58bacb147494964142957e3848199caf0bcec792
7
+ data.tar.gz: bf8f63c0bb7d6d890a3dbb1f1b8f5b4fb2190277ef606e6b2bca2016c58c649c1c474ba0c6c48be0dc99e2bbda4a6fbfc9e3cd35248a49617aff0d4f4a00d628
Binary file
@@ -1,17 +1,21 @@
1
+ require 'open3'
1
2
  require 'thor'
2
3
  require 'active_support'
3
4
  require 'active_support/core_ext/string/inflections'
4
5
  require 'fly.io-rails/machines'
5
6
  require 'fly.io-rails/utils'
7
+ require 'fly.io-rails/dsl'
8
+ require 'fly.io-rails/scanner'
6
9
 
7
10
  module Fly
8
11
  class Actions < Thor::Group
9
12
  include Thor::Actions
10
13
  include Thor::Base
11
14
  include Thor::Shell
15
+ include Fly::Scanner
12
16
  attr_accessor :options
13
17
 
14
- def initialize(app = nil)
18
+ def initialize(app = nil, regions = nil)
15
19
  self.app = app if app
16
20
 
17
21
  @ruby_version = RUBY_VERSION
@@ -20,10 +24,25 @@ module Fly
20
24
  @yarn = File.exist? 'yarn.lock'
21
25
  @node_version = @node ? `node --version`.chomp.sub(/^v/, '') : '16.17.0'
22
26
  @org = Fly::Machines.org
23
- @regions = []
24
27
 
25
28
  @options = {}
26
29
  @destination_stack = [Dir.pwd]
30
+
31
+ if !regions or regions.empty?
32
+ @regions = JSON.parse(`flyctl regions list --json --app #{app}`)['Regions'].
33
+ map {|region| region['Code']} rescue []
34
+ else
35
+ @regions = regions
36
+ end
37
+
38
+ @region = @regions.first || 'iad'
39
+
40
+ @config = Fly::DSL::Config.new
41
+ if File.exist? 'config/fly.rb'
42
+ @config.instance_eval IO.read('config/fly.rb')
43
+ end
44
+
45
+ scan_rails_app
27
46
  end
28
47
 
29
48
  def app
@@ -43,6 +62,11 @@ module Fly
43
62
  template 'fly.toml.erb', 'fly.toml'
44
63
  end
45
64
 
65
+ def generate_fly_config
66
+ app
67
+ template 'fly.rb.erb', 'config/fly.rb'
68
+ end
69
+
46
70
  def generate_dockerfile
47
71
  app
48
72
  template 'Dockerfile.erb', 'Dockerfile'
@@ -63,11 +87,28 @@ module Fly
63
87
  template 'fly.rake.erb', 'lib/tasks/fly.rake'
64
88
  end
65
89
 
66
- def generate_all
67
- generate_dockerfile
68
- generate_dockerignore
69
- generate_terraform
70
- generate_raketask
90
+ def generate_key
91
+ credentials = nil
92
+ if File.exist? 'config/credentials/production.key'
93
+ credentials = 'config/credentials/production.key'
94
+ elsif File.exist? 'config/master.key'
95
+ credentials = 'config/master.key'
96
+ end
97
+
98
+ if credentials
99
+ say_status :run, "flyctl secrets set --stage RAILS_MASTER_KEY from #{credentials}"
100
+ system "flyctl secrets set --stage RAILS_MASTER_KEY=#{IO.read(credentials).chomp}"
101
+ puts
102
+ end
103
+
104
+ ENV['FLY_API_TOKEN'] = `flyctl auth token`
105
+ end
106
+
107
+ def generate_patches
108
+ if @redis_cable and not File.exist? 'config/initializers/action_cable.rb'
109
+ app
110
+ template 'patches/action_cable.rb', 'config/initializers/action_cable.rb'
111
+ end
71
112
  end
72
113
 
73
114
  def generate_ipv4
@@ -85,7 +126,7 @@ module Fly
85
126
  def create_volume(app, region, size)
86
127
  volume = "#{app.gsub('-', '_')}_volume"
87
128
  volumes = JSON.parse(`flyctl volumes list --json`).
88
- map {|volume| volume[' Name']}
129
+ map {|volume| volume['Name']}
89
130
 
90
131
  unless volumes.include? volume
91
132
  cmd = "flyctl volumes create #{volume} --app #{app} --region #{region} --size #{size}"
@@ -97,71 +138,96 @@ module Fly
97
138
  end
98
139
 
99
140
  def create_postgres(app, org, region, vm_size, volume_size, cluster_size)
100
- cmd = "fly postgres create --name #{app}-db --org #{org} --region #{region} --vm-size #{vm_size} --volume-size #{volume_size} --initial-cluster-size #{cluster_size}"
141
+ cmd = "flyctl postgres create --name #{app}-db --org #{org} --region #{region} --vm-size #{vm_size} --volume-size #{volume_size} --initial-cluster-size #{cluster_size}"
101
142
  say_status :run, cmd
102
143
  output = FlyIoRails::Utils.tee(cmd)
103
144
  output[%r{postgres://\S+}]
104
145
  end
105
146
 
147
+ def create_redis(app, org, region, eviction)
148
+ # see if redis is already defined
149
+ name = `flyctl redis list`.lines[1..-2].map(&:split).
150
+ find {|tokens| tokens[1] == org}&.first
151
+
152
+ if name
153
+ secret = `flyctl redis status #{name}`[%r{redis://\S+}]
154
+ return secret if secret
155
+ end
156
+
157
+ # create a new redis
158
+ cmd = "flyctl redis create --org #{org} --name #{app}-redis --region #{region} --no-replicas #{eviction} --plan #{@config.redis.plan}"
159
+ say_status :run, cmd
160
+ output = FlyIoRails::Utils.tee(cmd)
161
+ output[%r{redis://\S+}]
162
+ end
163
+
106
164
  def release(app, config)
107
- start = Fly::Machines.create_start_machine(app, config: config)
165
+ start = Fly::Machines.create_and_start_machine(app, config: config)
108
166
  machine = start[:id]
109
167
 
110
168
  if !machine
111
- STDERR.puts 'Error starting release machine'
112
- PP.pp start, STDERR
113
- exit 1
169
+ STDERR.puts 'Error starting release machine'
170
+ PP.pp start, STDERR
171
+ exit 1
114
172
  end
115
173
 
174
+ status = Fly::Machines.wait_for_machine app, machine,
175
+ timeout: 60, state: 'started'
176
+
116
177
  # wait for release to copmlete
117
178
  status = nil
118
179
  5.times do
119
- status = Fly::Machines.wait_for_machine app, machine,
120
- timeout: 60, status: 'stopped'
121
- return machine if status[:ok]
180
+ status = Fly::Machines.wait_for_machine app, machine,
181
+ timeout: 60, state: 'stopped'
182
+ return machine if status[:ok]
183
+ end
184
+
185
+ # wait for release to copmlete
186
+ event = nil
187
+ 90.times do
188
+ sleep 1
189
+ status = Fly::Machines.get_a_machine app, machine
190
+ event = status[:events]&.first
191
+ return machine if event && event[:type] == 'exit'
122
192
  end
123
193
 
124
- STDERR.puts status.to_json
194
+ STDERR.puts event.to_json
125
195
  exit 1
126
196
  end
127
197
 
128
198
  def deploy(app, image)
129
- regions = JSON.parse(`flyctl regions list --json`)['Regions'].
130
- map {|region| region['Code']} rescue []
131
- region = regions.first || 'iad'
132
199
 
133
- secrets = JSON.parse(`fly secrets list --json`).
200
+ secrets = JSON.parse(`flyctl secrets list --json`).
134
201
  map {|secret| secret["Name"]}
135
202
 
136
203
  config = {
137
- region: region,
204
+ region: @region,
138
205
  app: app,
139
206
  name: "#{app}-machine",
140
207
  image: image,
141
208
  guest: {
142
- cpus: 1,
143
- cpu_kind: "shared",
144
- memory_mb: 256,
209
+ cpus: @config.machine.cpus,
210
+ cpu_kind: @config.machine.cpu_kind,
211
+ memory_mb: @config.machine.memory_mb
145
212
  },
146
213
  services: [
147
- {
148
- ports: [
149
- {port: 443, handlers: ["tls", "http"]},
150
- {port: 80, handlers: ["http"]}
151
- ],
152
- protocol: "tcp",
153
- internal_port: 8080
154
- }
214
+ {
215
+ ports: [
216
+ {port: 443, handlers: ["tls", "http"]},
217
+ {port: 80, handlers: ["http"]}
218
+ ],
219
+ protocol: "tcp",
220
+ internal_port: 8080
221
+ }
155
222
  ]
156
223
  }
157
224
 
158
- database = YAML.load_file('config/database.yml').
159
- dig('production', 'adapter') rescue nil
160
- cable = YAML.load_file('config/cable.yml').
161
- dig('production', 'adapter') rescue nil
225
+ unless secrets.include? 'RAILS_MASTER_KEY'
226
+ generate_key
227
+ end
162
228
 
163
- if database == 'sqlite3'
164
- volume = create_volume(app, region, 3)
229
+ if @sqlite3
230
+ volume = create_volume(app, @region, @config.sqlite3.size)
165
231
 
166
232
  config[:mounts] = [
167
233
  { volume: volume, path: '/mnt/volume' }
@@ -170,12 +236,30 @@ module Fly
170
236
  config[:env] = {
171
237
  "DATABASE_URL" => "sqlite3:///mnt/volume/production.sqlite3"
172
238
  }
173
- elsif database == 'postgresql' and not secrets.include? 'DATABASE_URL'
174
- secret = create_postgres(app, @org, region, 'shared-cpu-1x', 1, 1)
239
+ elsif @postgresql and not secrets.include? 'DATABASE_URL'
240
+ secret = create_postgres(app, @org, @region,
241
+ @config.postgres.vm_size,
242
+ @config.postgres.volume_size,
243
+ @config.postgres.initial_cluster_size)
244
+
245
+ if secret
246
+ cmd = "flyctl secrets set --stage DATABASE_URL=#{secret}"
247
+ say_status :run, cmd
248
+ system cmd
249
+ end
250
+ end
175
251
 
176
- cmd = "fly secrets set DATABASE_URL=#{secret}"
177
- say_status :run, cmd
178
- system cmd
252
+ if @redis and not secrets.include? 'REDIS_URL'
253
+ # Set eviction policy to true if a cache provider, else false.
254
+ eviction = @redis_cache ? '--enable-eviction' : '--disable-eviction'
255
+
256
+ secret = create_redis(app, @org, @region, eviction)
257
+
258
+ if secret
259
+ cmd = "flyctl secrets set --stage REDIS_URL=#{secret}"
260
+ say_status :run, cmd
261
+ system cmd
262
+ end
179
263
  end
180
264
 
181
265
  # build config for release machine, overriding server command
@@ -192,21 +276,30 @@ module Fly
192
276
  # start proxy, if necessary
193
277
  endpoint = Fly::Machines::fly_api_hostname!
194
278
 
279
+ # stop previous instances - list will fail on first run
280
+ stdout, stderr, status = Open3.capture3('fly machines list --json')
281
+ unless stdout.empty?
282
+ JSON.parse(stdout).each do |list|
283
+ next if list['id'] == machine
284
+ system "fly machines remove --force #{list['id']}"
285
+ end
286
+ end
287
+
195
288
  # start app
196
289
  say_status :fly, "start #{app}"
197
- start = Fly::Machines.create_start_machine(app, config: config)
290
+ start = Fly::Machines.create_and_start_machine(app, config: config)
198
291
  machine = start[:id]
199
292
 
200
293
  if !machine
201
- STDERR.puts 'Error starting application'
202
- PP.pp start, STDERR
203
- exit 1
294
+ STDERR.puts 'Error starting application'
295
+ PP.pp start, STDERR
296
+ exit 1
204
297
  end
205
298
 
206
299
  5.times do
207
- status = Fly::Machines.wait_for_machine app, machine,
300
+ status = Fly::Machines.wait_for_machine app, machine,
208
301
  timeout: 60, status: 'started'
209
- return if status[:ok]
302
+ return if status[:ok]
210
303
  end
211
304
 
212
305
  STDERR.puts 'Timeout waiting for application to start'
@@ -220,22 +313,22 @@ module Fly
220
313
 
221
314
  # find first machine in terraform config file
222
315
  machines = Fly::HCL.parse(IO.read('main.tf')).find {|block|
223
- block.keys.first == :resource and
224
- block.values.first.keys.first == 'fly_machine'}
316
+ block.keys.first == :resource and
317
+ block.values.first.keys.first == 'fly_machine'}
225
318
 
226
319
  # extract HCL configuration for the machine
227
320
  config = machines.values.first.values.first.values.first
228
321
 
229
322
  # delete HCL specific configuration items
230
323
  %i(services for_each region app name depends_on).each do |key|
231
- config.delete key
324
+ config.delete key
232
325
  end
233
326
 
234
327
  # move machine configuration into guest object
235
328
  config[:guest] = {
236
- cpus: config.delete(:cpus),
237
- memory_mb: config.delete(:memorymb),
238
- cpu_kind: config.delete(:cputype)
329
+ cpus: config.delete(:cpus),
330
+ memory_mb: config.delete(:memorymb),
331
+ cpu_kind: config.delete(:cputype)
239
332
  }
240
333
 
241
334
  # release machines should have no services or mounts
@@ -251,40 +344,40 @@ module Fly
251
344
 
252
345
  # start release machine
253
346
  STDERR.puts "--> #{config[:env]['SERVER_COMMAND']}"
254
- start = Fly::Machines.create_start_machine(app, config: config)
347
+ start = Fly::Machines.create_and_start_machine(app, config: config)
255
348
  machine = start[:id]
256
349
 
257
350
  if !machine
258
- STDERR.puts 'Error starting release machine'
259
- PP.pp start, STDERR
260
- exit 1
351
+ STDERR.puts 'Error starting release machine'
352
+ PP.pp start, STDERR
353
+ exit 1
261
354
  end
262
355
 
263
356
  # wait for release to copmlete
264
357
  event = nil
265
358
  90.times do
266
- sleep 1
267
- status = Fly::Machines.get_a_machine app, machine
268
- event = status[:events]&.first
269
- break if event && event[:type] == 'exit'
359
+ sleep 1
360
+ status = Fly::Machines.get_a_machine app, machine
361
+ event = status[:events]&.first
362
+ break if event && event[:type] == 'exit'
270
363
  end
271
364
 
272
365
  # extract exit code
273
366
  exit_code = event.dig(:request, :exit_event, :exit_code)
274
-
367
+
275
368
  if exit_code == 0
276
- # delete release machine
277
- Fly::Machines.delete_machine app, machine
369
+ # delete release machine
370
+ Fly::Machines.delete_machine app, machine
278
371
 
279
- # use terraform apply to deploy
280
- ENV['FLY_API_TOKEN'] = `flyctl auth token`.chomp
281
- ENV['FLY_HTTP_ENDPOINT'] = endpoint if endpoint
282
- system 'terraform apply -auto-approve'
372
+ # use terraform apply to deploy
373
+ ENV['FLY_API_TOKEN'] = `flyctl auth token`.chomp
374
+ ENV['FLY_HTTP_ENDPOINT'] = endpoint if endpoint
375
+ system 'terraform apply -auto-approve'
283
376
  else
284
- STDERR.puts 'Error performing release'
285
- STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
286
- STDERR.puts "run 'flyctl logs --instance #{machine}' for more information"
287
- exit 1
377
+ STDERR.puts 'Error performing release'
378
+ STDERR.puts (exit_code ? {exit_code: exit_code} : event).inspect
379
+ STDERR.puts "run 'flyctl logs --instance #{machine}' for more information"
380
+ exit 1
288
381
  end
289
382
  end
290
383
  end
@@ -0,0 +1,79 @@
1
+ module Fly
2
+ module DSL
3
+ class Base
4
+ def initialize
5
+ @value = {}
6
+ end
7
+
8
+ def self.option name, default=nil
9
+ @options ||= {}
10
+ @options[name] = default
11
+
12
+ define_method name do |*args|
13
+ if args.length == 1
14
+ @value[name] = args.first
15
+ elsif args.length > 1
16
+ raise ArgumentError.new("wrong number of arguments (given #{args.length}, expected 0..1)")
17
+ end
18
+
19
+ @value.include?(name) ? @value[name] : default
20
+ end
21
+ end
22
+
23
+ def self.options
24
+ @options ||= {}
25
+ end
26
+ end
27
+
28
+ #############################################################
29
+
30
+ class Machine < Base
31
+ option :cpus, 1
32
+ option :cpu_kind, 'shared'
33
+ option :memory_mb, 256
34
+ end
35
+
36
+ class Postgres < Base
37
+ option :vm_size, 'shared-cpu-1x'
38
+ option :volume_size, 1
39
+ option :initial_cluster_size, 1
40
+ end
41
+
42
+ class Redis < Base
43
+ option :plan, "Free"
44
+ end
45
+
46
+ class Sqlite3 < Base
47
+ option :size, 3
48
+ end
49
+
50
+ #############################################################
51
+
52
+ class Config
53
+ @@blocks = {}
54
+
55
+ def initialize
56
+ @config = {}
57
+ end
58
+
59
+ def self.block name, kind
60
+ @@blocks[name] = kind
61
+
62
+ define_method name do |&block|
63
+ @config[name] ||= kind.new
64
+ @config[name].instance_eval(&block) if block
65
+ @config[name]
66
+ end
67
+ end
68
+
69
+ def self.blocks
70
+ @@blocks
71
+ end
72
+
73
+ block :machine, Machine
74
+ block :postgres, Postgres
75
+ block :redis, Redis
76
+ block :sqlite3, Sqlite3
77
+ end
78
+ end
79
+ end
@@ -80,7 +80,7 @@ module Fly
80
80
  get "/v1/apps/#{app}"
81
81
  end
82
82
 
83
- # create_start_machine 'user-functions', name: 'quirky_machine', config: {
83
+ # create_and_start_machine 'user-functions', name: 'quirky_machine', config: {
84
84
  # image: 'flyio/fastify-functions',
85
85
  # env: {'APP_ENV' => 'production'},
86
86
  # services: [
@@ -94,7 +94,7 @@ module Fly
94
94
  # }
95
95
  # ]
96
96
  # }
97
- def self.create_start_machine app, options
97
+ def self.create_and_start_machine app, options
98
98
  post "/v1/apps/#{app}/machines", options
99
99
  end
100
100
 
@@ -0,0 +1,26 @@
1
+ module Fly
2
+ module Scanner
3
+ # scan for major features - things that if present will likely affect
4
+ # more than one artifact that is generated.
5
+ def scan_rails_app
6
+ database = YAML.load_file('config/database.yml').
7
+ dig('production', 'adapter') rescue nil
8
+
9
+ if database == 'sqlite3'
10
+ @sqlite3 = true
11
+ elsif database == 'postgresql'
12
+ @postgresql = true
13
+ end
14
+
15
+ if (YAML.load_file('config/cable.yml').dig('production', 'adapter') rescue false)
16
+ @redis_cable = true
17
+ end
18
+
19
+ if (IO.read('config/environments/production.rb') =~ /redis/i rescue false)
20
+ @redis_cache = true
21
+ end
22
+
23
+ @redis = @redis_cable || @redis_cache
24
+ end
25
+ end
26
+ end
@@ -4,7 +4,7 @@ module FlyIoRails
4
4
  module Utils
5
5
 
6
6
  def tee cmd
7
- say_status :run, cmd
7
+ say_status :run, cmd if defined? say_status
8
8
  FlyIoRails::Utils.tee cmd
9
9
  end
10
10
 
@@ -12,21 +12,40 @@ module FlyIoRails
12
12
  data = []
13
13
 
14
14
  begin
15
- PTY.spawn( cmd ) do |stdin, stdout, pid|
16
- begin
17
- # Do stuff with the output here. Just printing to show it works
18
- stdin.each do |line|
15
+ PTY.spawn( cmd ) do |stdin, stdout, pid|
16
+ begin
17
+ # Do stuff with the output here. Just printing to show it works
18
+ stdin.each do |line|
19
19
  print line
20
20
  data << line
21
21
  end
22
- rescue Errno::EIO
23
- end
24
- end
22
+ rescue Errno::EIO
23
+ end
24
+ end
25
25
  rescue PTY::ChildExited
26
26
  end
27
27
 
28
28
  data.join
29
29
  end
30
30
 
31
+ def create_app(name=nil, org='personal', regions=[])
32
+ cmd = if name
33
+ "flyctl apps create #{name.inspect} --org #{org.inspect} --machines"
34
+ else
35
+ "flyctl apps create --generate-name --org #{org.inspect} --machines"
36
+ end
37
+
38
+ output = tee cmd
39
+ exit 1 unless output =~ /^New app created: /
40
+
41
+ @app = output.split.last
42
+ template 'fly.toml.erb', 'fly.toml' if defined? template # rake tasks are on their own
43
+
44
+ unless regions.empty?
45
+ @regions = regions.flatten
46
+ end
47
+
48
+ @app
49
+ end
31
50
  end
32
51
  end
@@ -1,3 +1,3 @@
1
1
  module Fly_io
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.2'
3
3
  end
@@ -0,0 +1,29 @@
1
+ require 'fly.io-rails/actions'
2
+
3
+ module Fly::Generators
4
+ class AppGenerator < Rails::Generators::Base
5
+ include FlyIoRails::Utils
6
+
7
+ class_option :name, type: :string, required: false
8
+ class_option :org, type: :string, default: 'personal'
9
+ class_option :region, type: :array, repeatable: true, default: []
10
+
11
+ def generate_app
12
+ source_paths.push File.expand_path('../templates', __dir__)
13
+
14
+ create_app(options[:name], options[:org], options)
15
+
16
+ action = Fly::Actions.new(@app, options[:region])
17
+
18
+ action.generate_fly_config unless File.exist? 'config/fly.rb'
19
+ action.generate_dockerfile unless File.exist? 'Dockerfile'
20
+ action.generate_dockerignore unless File.exist? '.dockerignore'
21
+ action.generate_raketask unless File.exist? 'lib/tasks/fly.rake'
22
+ action.generate_patches
23
+ action.generate_ipv4
24
+ action.generate_ipv6
25
+ action.generate_key
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'fly.io-rails/actions'
2
+
3
+ module Fly::Generators
4
+ class TerraformGenerator < Rails::Generators::Base
5
+ include FlyIoRails::Utils
6
+
7
+ class_option :name, type: :string, required: false
8
+ class_option :org, type: :string, default: 'personal'
9
+ class_option :region, type: :array, repeatable: true, default: []
10
+
11
+ def terraform
12
+ source_paths.push File.expand_path('../templates', __dir__)
13
+
14
+ create_app(options[:name], options[:org], options[:region])
15
+
16
+ action = Fly::Actions.new(@app, options[:region])
17
+
18
+ action.generate_dockerfile
19
+ action.generate_dockerignore
20
+ action.generate_terraform
21
+ action.generate_raketask
22
+ action.generate_patches
23
+
24
+ action.generate_key
25
+
26
+ tee 'terraform init'
27
+ end
28
+ end
29
+ end
@@ -10,14 +10,22 @@ namespace :fly do
10
10
  # - changes to the filesystem made here are DISCARDED
11
11
  # - full access to secrets, databases
12
12
  # - failures here prevent deployment
13
+ <%- if @sqlite3 -%>
14
+ task :release
15
+ <%- else -%>
13
16
  task :release => 'db:migrate'
17
+ <%- end -%>
14
18
 
15
19
  # SERVER step:
16
20
  # - changes to the filesystem made here are deployed
17
21
  # - full access to secrets, databases
18
22
  # - failures here result in VM being stated, shutdown, and rolled back
19
23
  # to last successful deploy (if any).
24
+ <%- if @sqlite3 -%>
25
+ task :server => [:swapfile, 'db:migrate'] do
26
+ <%- else -%>
20
27
  task :server => :swapfile do
28
+ <%- end -%>
21
29
  sh 'bin/rails server'
22
30
  end
23
31
 
@@ -26,7 +34,11 @@ namespace :fly do
26
34
  # - performance critical applications should scale memory to the
27
35
  # point where swap is rarely used. 'fly scale help' for details.
28
36
  # - disable by removing dependency on the :server task, thus:
37
+ <%- if @sqlite3 -%>
38
+ # task :server => 'db:migrate' do
39
+ <%- else -%>
29
40
  # task :server do
41
+ <%- end -%>
30
42
  task :swapfile do
31
43
  sh 'fallocate -l 512M /swapfile'
32
44
  sh 'chmod 0600 /swapfile'
@@ -0,0 +1,25 @@
1
+ machine do
2
+ cpus 1
3
+ cpu_kind 'shared'
4
+ memory_mb 256
5
+ end
6
+ <% if @sqlite3 -%>
7
+
8
+ sqlite3 do
9
+ size 3
10
+ end
11
+ <% end -%>
12
+ <% if @postgresql -%>
13
+
14
+ postgres do
15
+ vm_size 'shared-cpu-1x'
16
+ volume_size 1
17
+ initial_cluster_size 1
18
+ end
19
+ <% end -%>
20
+ <% if @redis -%>
21
+
22
+ redis do
23
+ plan "Free"
24
+ end
25
+ <% end -%>
@@ -27,7 +27,9 @@ resource "fly_ip" "<%= @appName %>Ipv6" {
27
27
  type = "v6"
28
28
  }
29
29
 
30
+ <% unless @sqlite3 -%>
30
31
  /* Uncomment this if you want a volume
32
+ <% end -%>
31
33
  resource "fly_volume" "<%= @appName %>Volume" {
32
34
  for_each = toset(<%= @regions.inspect %>)
33
35
  region = each.value
@@ -36,7 +38,9 @@ resource "fly_volume" "<%= @appName %>Volume" {
36
38
  app = <%= @app.inspect %>
37
39
  size = 3
38
40
  }
41
+ <% unless @sqlite3 -%>
39
42
  */
43
+ <% end -%>
40
44
 
41
45
  # Start a fly machine
42
46
  resource "fly_machine" "<%= @appName %>Machine" {
@@ -71,7 +75,7 @@ resource "fly_machine" "<%= @appName %>Machine" {
71
75
  }
72
76
  ]
73
77
 
74
- /* Uncomment this if you want sqlite3 on a volume
78
+ <%- if @sqlite3 -%>
75
79
  env = {
76
80
  DATABASE_URL = "sqlite3:///mnt/db/production.sqlite3"
77
81
  }
@@ -83,6 +87,10 @@ resource "fly_machine" "<%= @appName %>Machine" {
83
87
  }
84
88
  ]
85
89
 
90
+ depends_on = [fly_volume.<%= @appName %>Volume]
91
+ <%- else -%>
92
+ /* Uncomment this if you want a volume
86
93
  depends_on = [fly_volume.<%= @appName %>Volume]
87
94
  */
95
+ <%- end -%>
88
96
  }
@@ -0,0 +1,20 @@
1
+ # Restart Action Cable server on Redis connection failures.
2
+ # See: https://github.com/rails/rails/pull/45478
3
+ require 'action_cable/subscription_adapter/redis'
4
+
5
+ module ActionCableRedisListenerPatch
6
+ private
7
+
8
+ def ensure_listener_running
9
+ @thread ||= Thread.new do
10
+ Thread.current.abort_on_exception = true
11
+ conn = @adapter.redis_connection_for_subscriptions
12
+ listen conn
13
+ rescue ::Redis::BaseConnectionError
14
+ @thread = @raw_client = nil
15
+ ::ActionCable.server.restart
16
+ end
17
+ end
18
+ end
19
+
20
+ ActionCable::SubscriptionAdapter::Redis::Listener.prepend(ActionCableRedisListenerPatch)
data/lib/tasks/fly.rake CHANGED
@@ -12,25 +12,20 @@ namespace :fly do
12
12
  if File.exist? 'fly.toml'
13
13
  app = TOML.load_file('fly.toml')['app']
14
14
  else
15
- output = FlyIoRails::Utils.tee(
16
- "flyctl apps create --generate-name --org personal --machines"
17
- )
18
-
19
- exit 1 unless output =~ /^New app created: /
20
-
21
- @app = app = output.split.last
15
+ app = create_app
22
16
  end
23
17
 
24
18
  # ensure fly.toml and Dockerfile are present
25
19
  action = Fly::Actions.new(app)
26
20
  action.generate_toml if @app
21
+ action.generate_fly_config unless File.exist? 'config/fly.rb'
27
22
  action.generate_dockerfile unless File.exist? 'Dockerfile'
28
23
  action.generate_dockerignore unless File.exist? '.dockerignore'
29
24
  action.generate_raketask unless File.exist? 'lib/tasks/fly.rake'
30
25
 
31
26
  # build and push an image
32
27
  out = FlyIoRails::Utils.tee 'fly deploy --build-only --push'
33
- image = out[/image:\s+(.*)/, 1].strip
28
+ image = out[/image:\s+(.*)/, 1]&.strip
34
29
 
35
30
  exit 1 unless image
36
31
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fly.io-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: x64-mingw32
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-23 00:00:00.000000000 Z
11
+ date: 2022-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fly-ruby
@@ -53,18 +53,23 @@ files:
53
53
  - exe/x64-mingw32/wintun.dll
54
54
  - lib/fly.io-rails.rb
55
55
  - lib/fly.io-rails/actions.rb
56
+ - lib/fly.io-rails/dsl.rb
56
57
  - lib/fly.io-rails/generators.rb
57
58
  - lib/fly.io-rails/hcl.rb
58
59
  - lib/fly.io-rails/machines.rb
59
60
  - lib/fly.io-rails/platforms.rb
61
+ - lib/fly.io-rails/scanner.rb
60
62
  - lib/fly.io-rails/utils.rb
61
63
  - lib/fly.io-rails/version.rb
64
+ - lib/generators/fly/app_generator.rb
65
+ - lib/generators/fly/terraform_generator.rb
62
66
  - lib/generators/templates/Dockerfile.erb
63
67
  - lib/generators/templates/dockerignore.erb
64
68
  - lib/generators/templates/fly.rake.erb
69
+ - lib/generators/templates/fly.rb.erb
65
70
  - lib/generators/templates/fly.toml.erb
66
71
  - lib/generators/templates/main.tf.erb
67
- - lib/generators/terraform_generator.rb
72
+ - lib/generators/templates/patches/action_cable.rb
68
73
  - lib/tasks/fly.rake
69
74
  homepage: https://github.com/rubys/fly-io.rails
70
75
  licenses:
@@ -1,52 +0,0 @@
1
- require 'open3'
2
- require 'fly.io-rails/actions'
3
-
4
- class Fly::Generators::TerraformGenerator < Rails::Generators::Base
5
- include FlyIoRails::Utils
6
-
7
- class_option :name, type: :string, required: false
8
- class_option :org, type: :string, default: 'personal'
9
- class_option :region, type: :array, repeatable: true, default: []
10
-
11
- def terraform
12
- source_paths.push File.expand_path('./templates', __dir__)
13
-
14
- cmd = if options[:name]
15
- "flyctl apps create #{options[:name].inspect} --org #{options[:org].inspect}"
16
- else
17
- "flyctl apps create --generate-name --org #{options[:org].inspect}"
18
- end
19
-
20
- output = tee cmd
21
- exit 1 unless output =~ /^New app created: /
22
-
23
- @app = output.split.last
24
- template 'fly.toml.erb', 'fly.toml'
25
-
26
- if options[:region].empty?
27
- @regions = JSON.parse(`flyctl regions list --json`)['Regions'].
28
- map {|region| region['Code']}
29
- else
30
- @regions = options[:regions].flatten
31
- end
32
-
33
- action = Fly::Actions.new(@app)
34
- action.generate_all
35
-
36
- credentials = nil
37
- if File.exist? 'config/credentials/production.key'
38
- credentials = 'config/credentials/production.key'
39
- elsif File.exist? 'config/master.key'
40
- credentials = 'config/master.key'
41
- end
42
-
43
- if credentials
44
- say_status :run, "flyctl secrets set RAILS_MASTER_KEY from #{credentials}"
45
- system "flyctl secrets set RAILS_MASTER_KEY=#{IO.read(credentials).chomp}"
46
- puts
47
- end
48
-
49
- ENV['FLY_API_TOKEN'] = `flyctl auth token`
50
- tee 'terraform init'
51
- end
52
- end