deployed 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/MIT-LICENSE +20 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/app/assets/config/deployed_manifest.js +1 -0
- data/app/assets/javascripts/deployed/application.js +104 -0
- data/app/assets/stylesheets/deployed/deployed.css +1635 -0
- data/app/assets/stylesheets/deployed/src/input.css +19 -0
- data/app/controllers/deployed/application_controller.rb +18 -0
- data/app/controllers/deployed/config_controller.rb +10 -0
- data/app/controllers/deployed/git_controller.rb +11 -0
- data/app/controllers/deployed/run_controller.rb +178 -0
- data/app/controllers/deployed/setup_controller.rb +20 -0
- data/app/controllers/deployed/welcome_controller.rb +10 -0
- data/app/helpers/deployed/application_helper.rb +71 -0
- data/app/helpers/deployed/log_output_helper.rb +56 -0
- data/app/models/deployed/config.rb +39 -0
- data/app/models/deployed/current_execution.rb +8 -0
- data/app/views/deployed/git/uncommitted_check.html.erb +5 -0
- data/app/views/deployed/setup/new.html.erb +47 -0
- data/app/views/deployed/welcome/index.html.erb +128 -0
- data/app/views/layouts/deployed/_nav_menu.html.erb +54 -0
- data/app/views/layouts/deployed/application.html.erb +90 -0
- data/config/routes.rb +9 -0
- data/lib/deployed/engine.rb +5 -0
- data/lib/deployed/version.rb +3 -0
- data/lib/deployed.rb +18 -0
- data/lib/tasks/kamal_rails_tasks.rake +4 -0
- metadata +116 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
@tailwind base;
|
2
|
+
@tailwind components;
|
3
|
+
@tailwind utilities;
|
4
|
+
|
5
|
+
[x-cloak] { display: none !important; }
|
6
|
+
|
7
|
+
/* Style for scrollable-div to fill the available vertical space */
|
8
|
+
#scrollable-div {
|
9
|
+
flex: 1; /* Grow to fill available vertical space */
|
10
|
+
}
|
11
|
+
|
12
|
+
#resize-handle {
|
13
|
+
cursor: ns-resize; /* Change cursor to indicate vertical resizing */
|
14
|
+
}
|
15
|
+
|
16
|
+
/* Style for deploy-output-container */
|
17
|
+
#deploy-output-container {
|
18
|
+
min-height: 150px;
|
19
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deployed
|
4
|
+
class ApplicationController < ActionController::Base
|
5
|
+
layout 'deployed/application'
|
6
|
+
|
7
|
+
helper Deployed::Engine.helpers
|
8
|
+
|
9
|
+
before_action :initialize_deployed
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# A bunch of housekeeping stuff to make things run
|
14
|
+
def initialize_deployed
|
15
|
+
Deployed.setup!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deployed
|
4
|
+
# Provides a git status check to see if we are deploying uncommitted changes
|
5
|
+
class GitController < ApplicationController
|
6
|
+
def uncommitted_check
|
7
|
+
@git_status = `git status --porcelain`
|
8
|
+
respond_to(&:html)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deployed
|
4
|
+
# Provides a centralized way to run all `kamal [command]` executions and streams to the browser
|
5
|
+
class RunController < ApplicationController
|
6
|
+
class ConcurrentProcessRunning < StandardError; end
|
7
|
+
|
8
|
+
include ActionController::Live
|
9
|
+
|
10
|
+
before_action :set_headers
|
11
|
+
|
12
|
+
# Endpoint to execute the kamal command
|
13
|
+
def execute
|
14
|
+
if process_running?
|
15
|
+
raise(ConcurrentProcessRunning)
|
16
|
+
elsif stored_pid
|
17
|
+
release_process
|
18
|
+
end
|
19
|
+
|
20
|
+
sse.write(
|
21
|
+
"<div class='text-slate-400'>> <span class='text-slate-300 font-semibold'>kamal #{command}</span></div>",
|
22
|
+
event: 'message'
|
23
|
+
)
|
24
|
+
|
25
|
+
read_io, write_io = IO.pipe
|
26
|
+
|
27
|
+
# Fork a child process
|
28
|
+
Deployed::CurrentExecution.child_pid = fork do
|
29
|
+
# Redirect the standard output to the write end of the pipe
|
30
|
+
$stdout.reopen(write_io)
|
31
|
+
|
32
|
+
# Execute the command
|
33
|
+
exec("kamal #{command}; echo \"[Kamal Rails] End transmission\"")
|
34
|
+
end
|
35
|
+
|
36
|
+
lock_process
|
37
|
+
|
38
|
+
sse.write(
|
39
|
+
"<div class='text-slate-400' data-child-pid=\"#{Deployed::CurrentExecution.child_pid}\"></div>",
|
40
|
+
event: 'message'
|
41
|
+
)
|
42
|
+
|
43
|
+
write_io.close
|
44
|
+
|
45
|
+
# Use a separate thread to read and stream the output
|
46
|
+
output_thread = Thread.new do
|
47
|
+
read_io.each_line do |line|
|
48
|
+
output = line.strip
|
49
|
+
output = output.gsub('49.13.91.176', '[redacted]')
|
50
|
+
text_color_class = 'text-green-400'
|
51
|
+
|
52
|
+
# Hackish way of dealing with error messages at the moment
|
53
|
+
if output.include?('[31m')
|
54
|
+
text_color_class = 'text-red-500'
|
55
|
+
output.gsub!('[31m', '')
|
56
|
+
output.gsub!('[0m', '')
|
57
|
+
end
|
58
|
+
|
59
|
+
sse.write("<div class='#{text_color_class}'>#{output}</div>", event: 'message')
|
60
|
+
end
|
61
|
+
|
62
|
+
# Ensure the response stream and the thread are closed properly
|
63
|
+
sse.close
|
64
|
+
response.stream.close
|
65
|
+
end
|
66
|
+
|
67
|
+
# Ensure that the thread is joined when the execution is complete
|
68
|
+
Process.wait
|
69
|
+
output_thread.join
|
70
|
+
rescue ConcurrentProcessRunning
|
71
|
+
sse.write(
|
72
|
+
"<div class='text-red-500'>Existing process running with PID: #{stored_pid}</div>",
|
73
|
+
event: 'message'
|
74
|
+
)
|
75
|
+
logger.info 'Existing process running'
|
76
|
+
rescue ActionController::Live::ClientDisconnected
|
77
|
+
logger.info 'Client Disconnected'
|
78
|
+
rescue IOError
|
79
|
+
logger.info 'IOError'
|
80
|
+
ensure
|
81
|
+
sse.close
|
82
|
+
response.stream.close
|
83
|
+
release_process
|
84
|
+
end
|
85
|
+
|
86
|
+
# Endpoint to cancel currently running process
|
87
|
+
def cancel
|
88
|
+
if process_running?
|
89
|
+
# If a process is running, get the PID and attempt to kill it
|
90
|
+
begin
|
91
|
+
Process.kill('TERM', stored_pid)
|
92
|
+
sse.write(
|
93
|
+
"<div class='text-yellow-400'>Cancelled the process with PID: #{stored_pid}</div>",
|
94
|
+
event: 'message'
|
95
|
+
)
|
96
|
+
release_process
|
97
|
+
rescue Errno::ESRCH
|
98
|
+
sse.write(
|
99
|
+
"<div class='text-red-500'>Process with PID #{stored_pid} is not running.</div>",
|
100
|
+
event: 'message'
|
101
|
+
)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
sse.write(
|
105
|
+
"<div class='text-slate-400'>No process is currently running, nothing to cancel.</div>",
|
106
|
+
event: 'message'
|
107
|
+
)
|
108
|
+
end
|
109
|
+
rescue ActionController::Live::ClientDisconnected
|
110
|
+
logger.info 'Client Disconnected'
|
111
|
+
rescue IOError
|
112
|
+
logger.info 'IOError'
|
113
|
+
ensure
|
114
|
+
sse.write(
|
115
|
+
'[Kamal Rails] End transmission',
|
116
|
+
event: 'message'
|
117
|
+
)
|
118
|
+
sse.close
|
119
|
+
response.stream.close
|
120
|
+
release_process
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def set_headers
|
126
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
127
|
+
response.headers['Last-Modified'] = Time.now.httpdate
|
128
|
+
end
|
129
|
+
|
130
|
+
def sse
|
131
|
+
@sse ||= SSE.new(response.stream, event: 'Stream Started')
|
132
|
+
end
|
133
|
+
|
134
|
+
def command
|
135
|
+
params[:command]
|
136
|
+
end
|
137
|
+
|
138
|
+
def lock_file_path
|
139
|
+
Rails.root.join(Deployed::DIRECTORY, 'process.lock')
|
140
|
+
end
|
141
|
+
|
142
|
+
def lock_process
|
143
|
+
File.open(lock_file_path, 'a') do |file|
|
144
|
+
file.puts(Deployed::CurrentExecution.child_pid)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def release_process
|
149
|
+
return unless File.exist?(lock_file_path)
|
150
|
+
|
151
|
+
File.delete(lock_file_path)
|
152
|
+
end
|
153
|
+
|
154
|
+
def stored_pid
|
155
|
+
return false unless File.exist?(lock_file_path)
|
156
|
+
|
157
|
+
value = File.read(lock_file_path).to_i
|
158
|
+
|
159
|
+
if value.is_a?(Integer)
|
160
|
+
value
|
161
|
+
else
|
162
|
+
false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def process_running?
|
167
|
+
return false unless stored_pid
|
168
|
+
|
169
|
+
begin
|
170
|
+
# Send signal 0 to the process to check if it exists
|
171
|
+
Process.kill(0, stored_pid)
|
172
|
+
true
|
173
|
+
rescue Errno::ESRCH
|
174
|
+
false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deployed
|
4
|
+
# Provides a way to setup ./config/deploy.yml
|
5
|
+
class SetupController < ApplicationController
|
6
|
+
def new
|
7
|
+
respond_to do |format|
|
8
|
+
format.html
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
`kamal init`
|
14
|
+
|
15
|
+
respond_to do |format|
|
16
|
+
format.html { redirect_to root_path }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deployed
|
4
|
+
module ApplicationHelper
|
5
|
+
def rocket_icon
|
6
|
+
output = <<~ICON
|
7
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
8
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.631 8.41m5.96 5.96a14.926 14.926 0 01-5.841 2.58m-.119-8.54a6 6 0 00-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 00-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 01-2.448-2.448 14.9 14.9 0 01.06-.312m-2.24 2.39a4.493 4.493 0 00-1.757 4.306 4.493 4.493 0 004.306-1.758M16.5 9a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z" />
|
9
|
+
</svg>
|
10
|
+
ICON
|
11
|
+
|
12
|
+
output.html_safe
|
13
|
+
end
|
14
|
+
|
15
|
+
def config_icon
|
16
|
+
output = <<~ICON
|
17
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
18
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
|
19
|
+
</svg>
|
20
|
+
ICON
|
21
|
+
|
22
|
+
output.html_safe
|
23
|
+
end
|
24
|
+
|
25
|
+
def tools_icon
|
26
|
+
output = <<~ICON
|
27
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
28
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75a4.5 4.5 0 01-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 11-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 016.336-4.486l-3.276 3.276a3.004 3.004 0 002.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852z" />
|
29
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4.867 19.125h.008v.008h-.008v-.008z" />
|
30
|
+
</svg>
|
31
|
+
ICON
|
32
|
+
|
33
|
+
output.html_safe
|
34
|
+
end
|
35
|
+
|
36
|
+
def right_arrow
|
37
|
+
output = <<~ICON
|
38
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
39
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 8.25L21 12m0 0l-3.75 3.75M21 12H3" />
|
40
|
+
</svg>
|
41
|
+
ICON
|
42
|
+
|
43
|
+
output.html_safe
|
44
|
+
end
|
45
|
+
|
46
|
+
def kamal_exec_button(label:, command:)
|
47
|
+
button_tag(
|
48
|
+
label,
|
49
|
+
type: 'button',
|
50
|
+
onclick: "execKamal('#{command}')",
|
51
|
+
class: 'rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50',
|
52
|
+
'x-bind:disabled' => '$store.process.running',
|
53
|
+
'x-bind:class' => "{'opacity-50': $store.process.running}",
|
54
|
+
'x-data' => ''
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def kamal_abort_button
|
59
|
+
button_tag(
|
60
|
+
'Abort',
|
61
|
+
type: 'button',
|
62
|
+
onclick: "abortKamal()",
|
63
|
+
class: 'rounded-md bg-red-600 px-3 py-1.5 text-sm text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600',
|
64
|
+
'x-bind:disabled' => '$store.process.abortInProgress',
|
65
|
+
'x-bind:class' => "{'opacity-50': $store.process.abortInProgress}",
|
66
|
+
'x-text' => "$store.process.abortInProgress ? 'Aborting' : 'Abort'",
|
67
|
+
'x-data' => ''
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deployed
|
4
|
+
module LogOutputHelper
|
5
|
+
def deploy_output_intro
|
6
|
+
output = <<~HTML
|
7
|
+
<div>Ready... Set... Deploy!</div>
|
8
|
+
HTML
|
9
|
+
|
10
|
+
output.html_safe
|
11
|
+
end
|
12
|
+
|
13
|
+
def deploy_output_kamal_version
|
14
|
+
output = <<~HTML
|
15
|
+
<div>
|
16
|
+
Using <span class="text-slate-300">kamal #{::Kamal::VERSION}</span>
|
17
|
+
</div>
|
18
|
+
HTML
|
19
|
+
|
20
|
+
output.html_safe
|
21
|
+
end
|
22
|
+
|
23
|
+
def deploy_output_missing_config
|
24
|
+
return unless Deployed::Config.requires_init
|
25
|
+
|
26
|
+
output = <<~HTML
|
27
|
+
<div class="text-red-500">WARNING: ./config/deploy.yml file not detected</div>
|
28
|
+
HTML
|
29
|
+
|
30
|
+
output.html_safe
|
31
|
+
end
|
32
|
+
|
33
|
+
def deploy_output_spinner
|
34
|
+
output = <<~HTML
|
35
|
+
<div id="spinner" class="hidden mt-2 pb-4">
|
36
|
+
<svg class="animate-spin -ml-1 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
37
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
38
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
39
|
+
</svg>
|
40
|
+
</div>
|
41
|
+
HTML
|
42
|
+
|
43
|
+
output.html_safe
|
44
|
+
end
|
45
|
+
|
46
|
+
def deploy_output_resize_handler
|
47
|
+
output = <<~ICON
|
48
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
49
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
|
50
|
+
</svg>
|
51
|
+
ICON
|
52
|
+
|
53
|
+
output.html_safe
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Deployed
|
6
|
+
# Provides a model to track the current values in ./config/deploy.yml
|
7
|
+
class Config < ActiveSupport::CurrentAttributes
|
8
|
+
attribute :service, :image, :servers, :ssh, :volumes, :registry, :env, :traefik, :requires_init, :env_values
|
9
|
+
|
10
|
+
def self.init!(yaml_file = './config/deploy.yml', dot_env_file = '.env')
|
11
|
+
self.requires_init = File.exist?(yaml_file) ? false : true
|
12
|
+
|
13
|
+
unless requires_init
|
14
|
+
yaml_data = YAML.load_file(yaml_file)
|
15
|
+
self.service = yaml_data['service']
|
16
|
+
self.image = yaml_data['image']
|
17
|
+
self.servers = yaml_data['servers']
|
18
|
+
self.ssh = yaml_data['ssh']
|
19
|
+
self.volumes = yaml_data['volumes']
|
20
|
+
self.registry = yaml_data['registry']
|
21
|
+
self.env = yaml_data['env'] || {}
|
22
|
+
self.traefik = yaml_data['traefik']
|
23
|
+
end
|
24
|
+
|
25
|
+
if File.exist?(dot_env_file)
|
26
|
+
self.env_values = {}
|
27
|
+
|
28
|
+
File.open(dot_env_file, 'r') do |file|
|
29
|
+
file.each_line do |line|
|
30
|
+
key, value = line.strip.split('=')
|
31
|
+
self.env_values[key] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
requires_init
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<%= turbo_frame_tag 'deployed-init', target: '_top' do %>
|
2
|
+
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true" x-data x-show="!$store.ready">
|
3
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
4
|
+
x-data
|
5
|
+
x-show="!$store.ready"
|
6
|
+
x-transition:enter="ease-out duration-300"
|
7
|
+
x-transition:enter-start="opacity-0"
|
8
|
+
x-transition:enter-end="opacity-100"
|
9
|
+
x-transition:leave="ease-in duration-200"
|
10
|
+
x-transition:leave-start="opacity-100"
|
11
|
+
x-transition:leave-end="opacity-0"></div>
|
12
|
+
|
13
|
+
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
14
|
+
<div class="flex min-h-full items-center justify-center p-4 text-center">
|
15
|
+
<div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all my-8 w-full max-w-lg p-6"
|
16
|
+
x-data
|
17
|
+
x-show="!$store.ready"
|
18
|
+
x-transition:enter="ease-out duration-300"
|
19
|
+
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
20
|
+
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
21
|
+
x-transition:leave="ease-in duration-200"
|
22
|
+
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
23
|
+
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
|
24
|
+
<div>
|
25
|
+
<div class="mt-5">
|
26
|
+
<div class="flex justify-center">
|
27
|
+
<h3 class="font-semibold mb-5 flex items-center text-xl">
|
28
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-2 text-red-500">
|
29
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
|
30
|
+
</svg>
|
31
|
+
Kamal Initialization Required
|
32
|
+
</h3>
|
33
|
+
</div>
|
34
|
+
<div class="text-gray-500 space-y-4">
|
35
|
+
<p><span class="font-mono bg-slate-700 px-2 py-0.5 rounded-md text-slate-300">./config/deploy.yml</span> not detected.</p>
|
36
|
+
<p>We will run <code class="font-mono bg-slate-700 px-2 py-0.5 rounded-md text-slate-300">kamal init</code> for you.</p>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<div class="mt-5">
|
41
|
+
<%= button_to 'Setup Kamal', setup_path, method: :post, class: 'inline-flex w-full justify-center rounded-full bg-sky-600 py-3 font-semibold text-white shadow-sm hover:bg-sky-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600 sm:col-start-2' %>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
<% end %>
|
@@ -0,0 +1,128 @@
|
|
1
|
+
<div class="space-y-10 divide-y divide-gray-900/10">
|
2
|
+
<div class="grid grid-cols-1 gap-x-8 gap-y-4 md:grid-cols-3">
|
3
|
+
<div class="px-0">
|
4
|
+
<h2 class="text-base font-semibold leading-7 text-gray-900 flex items-center">
|
5
|
+
<span class="mr-2"><%= rocket_icon %></span>
|
6
|
+
Deployment
|
7
|
+
</h2>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-xl md:col-span-2">
|
11
|
+
<div class="px-4 py-6 sm:p-8 flex flex-col space-y-4">
|
12
|
+
<div class="divide-y divide-gray-100">
|
13
|
+
<div class="!pt-0 py-4">
|
14
|
+
<%= kamal_exec_button(label: 'Deploy', command: 'deploy') %>
|
15
|
+
<!-- Asynchronously fetch git status -->
|
16
|
+
<%= turbo_frame_tag 'git-status', src: git_uncommitted_check_url %>
|
17
|
+
</div>
|
18
|
+
<div class="py-4">
|
19
|
+
<%= kamal_exec_button(label: 'App Details', command: 'app details') %>
|
20
|
+
<%= kamal_exec_button(label: 'App Containers', command: 'app containers') %>
|
21
|
+
</div>
|
22
|
+
<div class="!pb-0 py-4">
|
23
|
+
<%= kamal_exec_button(label: 'App Boot', command: 'app boot') %>
|
24
|
+
<%= kamal_exec_button(label: 'App Start', command: 'app start') %>
|
25
|
+
<%= kamal_exec_button(label: 'App Stop', command: 'app stop') %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div class="grid grid-cols-1 gap-x-8 gap-y-4 pt-10 md:grid-cols-3">
|
33
|
+
<div class="px-0">
|
34
|
+
<h2 class="text-base font-semibold leading-7 text-gray-900 flex items-center">
|
35
|
+
<span class="mr-2"><%= config_icon %></span>
|
36
|
+
Configuration
|
37
|
+
</h2>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-xl md:col-span-2">
|
41
|
+
<div class="px-4 py-6 sm:p-8 flex flex-col space-y-4">
|
42
|
+
<% if Deployed::Config.requires_init %>
|
43
|
+
<p class="text-slate-700">Your <span class="font-mono bg-slate-700 px-2 py-0.5 rounded-md text-slate-300">./config/deploy.yml</span> file does not exist. You will need to create it.</p>
|
44
|
+
<% else %>
|
45
|
+
<dl class="divide-y divide-gray-100">
|
46
|
+
<div class="!pt-0 py-3">
|
47
|
+
<dt class="text-sm font-medium text-gray-900">App</dt>
|
48
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
49
|
+
<span class="font-mono"><%= Deployed::Config.service %></span>
|
50
|
+
</dd>
|
51
|
+
</div>
|
52
|
+
<div class="py-3">
|
53
|
+
<dt class="text-sm font-medium text-gray-900">Image</dt>
|
54
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
55
|
+
<span class="font-mono"><%= Deployed::Config.image %></span>
|
56
|
+
</dd>
|
57
|
+
</div>
|
58
|
+
<div class="py-3">
|
59
|
+
<dt class="text-sm font-medium text-gray-900">Environment variables</dt>
|
60
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
|
61
|
+
<% if !Deployed::Config.env.dig('secret').present? && !Deployed::Config.env.dig('clear').present? %>
|
62
|
+
<p>You have not specified any environment variables in your configuration file. This is likely an error. Please check your <span class="font-mono font-medium">./config/deploy.yml</span> file.</p>
|
63
|
+
<% end %>
|
64
|
+
|
65
|
+
<% if Deployed::Config.env.dig('secret').present? %>
|
66
|
+
<div class="italic mt-4">Secret</div>
|
67
|
+
<ul>
|
68
|
+
<% Deployed::Config.env.dig('secret').each do |secret_env| %>
|
69
|
+
<li class="flex space-x-1">
|
70
|
+
<span class="font-mono"><%= secret_env %></span>
|
71
|
+
<span><%= right_arrow %></span>
|
72
|
+
<% if Deployed::Config.env_values[secret_env].present? %>
|
73
|
+
<span class="font-mono text-slate-400">
|
74
|
+
<%= redacted_string = '*' * (Deployed::Config.env_values[secret_env].length - 6) + Deployed::Config.env_values[secret_env][-6..-1] %>
|
75
|
+
</span>
|
76
|
+
<% else %>
|
77
|
+
<span class="text-slate-400">Not found</span>
|
78
|
+
<% end %>
|
79
|
+
</li>
|
80
|
+
<% end %>
|
81
|
+
</ul>
|
82
|
+
<% end %>
|
83
|
+
|
84
|
+
<% if Deployed::Config.env['clear'].present? %>
|
85
|
+
<div class="italic mt-4">Secret</div>
|
86
|
+
<ul>
|
87
|
+
<% Deployed::Config.env['clear'].each do |secret_env| %>
|
88
|
+
<li class="flex space-x-1">
|
89
|
+
<span class="font-mono"><%= secret_env %></span>
|
90
|
+
<span><%= right_arrow %></span>
|
91
|
+
<% if Deployed::Config.env_values[secret_env].present? %>
|
92
|
+
<span class="font-mono text-slate-400">
|
93
|
+
<%= Deployed::Config.env_values[secret_env] %>
|
94
|
+
</span>
|
95
|
+
<% else %>
|
96
|
+
<span class="text-slate-400">Not found</span>
|
97
|
+
<% end %>
|
98
|
+
</li>
|
99
|
+
<% end %>
|
100
|
+
</ul>
|
101
|
+
<% end %>
|
102
|
+
</dd>
|
103
|
+
</div>
|
104
|
+
</dl>
|
105
|
+
<% end %>
|
106
|
+
</div>
|
107
|
+
</div>
|
108
|
+
</div>
|
109
|
+
|
110
|
+
<div class="grid grid-cols-1 gap-x-8 gap-y-4 pt-10 md:grid-cols-3">
|
111
|
+
<div class="px-0">
|
112
|
+
<h2 class="text-base font-semibold leading-7 text-gray-900 flex items-center">
|
113
|
+
<span class="mr-2"><%= tools_icon %></span>
|
114
|
+
Utilities
|
115
|
+
</h2>
|
116
|
+
</div>
|
117
|
+
|
118
|
+
<div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-xl md:col-span-2">
|
119
|
+
<div class="px-4 py-6 sm:p-8 flex flex-col space-y-4">
|
120
|
+
<div><%= kamal_exec_button(label: 'Perform Healthcheck', command: 'healthcheck --verbose') %></div>
|
121
|
+
<div>
|
122
|
+
<%= kamal_exec_button(label: 'Check Lock Status', command: 'lock status') %>
|
123
|
+
<%= kamal_exec_button(label: 'Release Lock', command: 'lock release') %>
|
124
|
+
</div>
|
125
|
+
</div>
|
126
|
+
</div>
|
127
|
+
</div>
|
128
|
+
</div>
|