chagall 0.0.1.beta1
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 +54 -0
- data/bin/chagall +12 -0
- data/lib/chagall/base.rb +29 -0
- data/lib/chagall/cli.rb +130 -0
- data/lib/chagall/compose/main.rb +45 -0
- data/lib/chagall/deploy/main.rb +261 -0
- data/lib/chagall/install/main.rb +321 -0
- data/lib/chagall/install/templates/template.Dockerfile +37 -0
- data/lib/chagall/install/templates/template.compose.yaml +121 -0
- data/lib/chagall/settings.rb +232 -0
- data/lib/chagall/setup/main.rb +121 -0
- data/lib/chagall/ssh.rb +55 -0
- data/lib/chagall/version.rb +3 -0
- data/lib/chagall.rb +21 -0
- metadata +129 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "io/console"
|
5
|
+
|
6
|
+
module Chagall
|
7
|
+
module Setup
|
8
|
+
# Handles server provisioning and Docker environment setup for deployment
|
9
|
+
class Main < Base
|
10
|
+
class DockerSetupError < StandardError; end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super()
|
14
|
+
setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
install_docker unless docker_installed?
|
19
|
+
setup_non_root_docker_deamon_access if unable_to_access_docker_deamon?
|
20
|
+
create_project_folder unless project_folder_exists?
|
21
|
+
create_env_files
|
22
|
+
|
23
|
+
logger.info "Docker environment setup complete"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def create_env_files
|
29
|
+
logger.debug "Create env files described at services..."
|
30
|
+
|
31
|
+
Settings[:compose_files].each do |file|
|
32
|
+
yaml = YAML.load_file(file, aliases: true)
|
33
|
+
yaml["services"].each do |service|
|
34
|
+
service[1]["env_file"]&.each do |env_file|
|
35
|
+
ssh.execute("touch #{env_file}", directory: Settings.instance.project_folder_path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def unable_to_access_docker_deamon?
|
42
|
+
return true
|
43
|
+
docker_result = ssh.command("docker ps")
|
44
|
+
docker_output = `#{docker_result} 2>&1`.strip
|
45
|
+
logger.debug "Docker output: #{docker_output}"
|
46
|
+
docker_output.downcase.include?("permission denied") ||
|
47
|
+
docker_output.downcase.include?("connect: permission denied")
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup_non_root_docker_deamon_access
|
51
|
+
logger.info "Add user to docker group..."
|
52
|
+
|
53
|
+
username = `#{ssh.command("whoami")} 2>&1`.strip
|
54
|
+
return true if username == "root"
|
55
|
+
|
56
|
+
groups = `#{ssh.command("groups")} 2>&1`.strip
|
57
|
+
return if groups.include?("docker")
|
58
|
+
|
59
|
+
logger.debug "Adding #{username} user to docker group"
|
60
|
+
ssh.execute("sudo usermod -aG docker #{username}", tty: true)
|
61
|
+
ssh.execute("groups #{username} | grep -q docker")
|
62
|
+
|
63
|
+
logger.debug "Successfully added user to docker group"
|
64
|
+
true
|
65
|
+
rescue StandardError => e
|
66
|
+
logger.error "Error setting up Docker daemon access: #{e.message}"
|
67
|
+
logger.debug e.backtrace.join("\n")
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def docker_installed?
|
72
|
+
logger.debug "Checking Docker installation..."
|
73
|
+
|
74
|
+
docker_output = `#{ssh.command("docker --version")} 2>&1`.strip
|
75
|
+
logger.debug "Docker version output: '#{docker_output}'"
|
76
|
+
|
77
|
+
compose_output = `#{ssh.command("docker compose version")} 2>&1`.strip
|
78
|
+
logger.debug "Docker Compose output: '#{compose_output}'"
|
79
|
+
|
80
|
+
return true if docker_output.include?("Docker version") &&
|
81
|
+
compose_output.include?("Docker Compose version")
|
82
|
+
|
83
|
+
logger.warn "Docker check failed:"
|
84
|
+
logger.warn "Docker output: #{docker_output}"
|
85
|
+
logger.warn "Docker Compose output: #{compose_output}"
|
86
|
+
false
|
87
|
+
rescue StandardError => e
|
88
|
+
logger.error "Error checking Docker installation: #{e.message}"
|
89
|
+
logger.debug e.backtrace.join("\n")
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
def project_folder_exists?
|
94
|
+
ssh.execute("test -d #{Settings.instance.project_folder_path}")
|
95
|
+
rescue StandardError => e
|
96
|
+
logger.error "Error checking project folder: #{e.message}"
|
97
|
+
false
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_project_folder
|
101
|
+
logger.debug "Creating project folder #{Settings.instance.project_folder_path}"
|
102
|
+
ssh.execute("mkdir -p #{Settings.instance.project_folder_path}")
|
103
|
+
end
|
104
|
+
|
105
|
+
def install_docker
|
106
|
+
logger.debug "Installing Docker..."
|
107
|
+
|
108
|
+
command = '(curl -fsSL https://get.docker.com || wget -O - https://get.docker.com || echo "exit 1") | sh'
|
109
|
+
# Clear any existing sudo session
|
110
|
+
ssh.execute("sudo -k")
|
111
|
+
|
112
|
+
logger.debug "Executing: #{command}"
|
113
|
+
result = ssh.execute(command, tty: true)
|
114
|
+
raise DockerSetupError, "Failed to execute: #{command}" unless result
|
115
|
+
rescue StandardError => e
|
116
|
+
logger.error "Docker installation failed: #{e.message}"
|
117
|
+
raise DockerSetupError, "Failed to install Docker: #{e.message}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/chagall/ssh.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require "English"
|
3
|
+
module Chagall
|
4
|
+
class SSH
|
5
|
+
attr_reader :server, :ssh_args
|
6
|
+
|
7
|
+
DEFAULT_SSH_ARGS = "-o StrictHostKeyChecking=no -o ServerAliveInterval=60".freeze
|
8
|
+
|
9
|
+
def initialize(server: Settings.instance.options[:server], ssh_args: DEFAULT_SSH_ARGS)
|
10
|
+
@server = server
|
11
|
+
@ssh_args = ssh_args
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(command, directory: nil, tty: false)
|
15
|
+
cmd = build_command(command, directory, tty)
|
16
|
+
logger.debug "SSH: #{cmd}"
|
17
|
+
system(cmd)
|
18
|
+
$CHILD_STATUS.success?
|
19
|
+
end
|
20
|
+
|
21
|
+
def command(command, directory: nil, tty: false)
|
22
|
+
build_command(command, directory, tty)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def build_command(command, directory, tty)
|
28
|
+
ssh_cmd = [ "ssh" ]
|
29
|
+
ssh_cmd << "-t" if tty
|
30
|
+
ssh_cmd << ssh_args
|
31
|
+
ssh_cmd << server
|
32
|
+
|
33
|
+
cmd = if directory
|
34
|
+
"cd #{directory} && #{command}"
|
35
|
+
else
|
36
|
+
command
|
37
|
+
end
|
38
|
+
|
39
|
+
"#{ssh_cmd.join(' ')} '#{cmd}'"
|
40
|
+
end
|
41
|
+
|
42
|
+
def logger
|
43
|
+
@logger ||= Logger.new($stdout).tap do |l|
|
44
|
+
l.formatter = proc do |severity, _, _, msg|
|
45
|
+
if severity == "DEBUG"
|
46
|
+
"[#{severity}] #{msg}\n"
|
47
|
+
else
|
48
|
+
"#{msg}\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
l.level = ENV.fetch("LOG_LEVEL") { "debug" }.downcase == "debug" ? Logger::DEBUG : Logger::INFO
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/chagall.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chagall
|
4
|
+
BIN_PATH = File.expand_path("../bin/chagall", __dir__)
|
5
|
+
|
6
|
+
class SettingsError < StandardError; end
|
7
|
+
class Error < StandardError; end
|
8
|
+
end
|
9
|
+
|
10
|
+
require "zeitwerk"
|
11
|
+
require "yaml"
|
12
|
+
require "pathname"
|
13
|
+
require "thor"
|
14
|
+
|
15
|
+
loader = Zeitwerk::Loader.for_gem
|
16
|
+
loader.setup
|
17
|
+
# Eager load necessary namespaces
|
18
|
+
loader.eager_load_namespace(Chagall::Deploy)
|
19
|
+
loader.eager_load_namespace(Chagall::Compose)
|
20
|
+
|
21
|
+
require_relative "chagall/cli"
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chagall
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.beta1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roman Klevtsov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-04-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: clamp
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: zeitwerk
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: debug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.12'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.12'
|
83
|
+
description:
|
84
|
+
email: frontandstart@gmail.com
|
85
|
+
executables:
|
86
|
+
- chagall
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- MIT-LICENSE
|
91
|
+
- Readme.md
|
92
|
+
- bin/chagall
|
93
|
+
- lib/chagall.rb
|
94
|
+
- lib/chagall/base.rb
|
95
|
+
- lib/chagall/cli.rb
|
96
|
+
- lib/chagall/compose/main.rb
|
97
|
+
- lib/chagall/deploy/main.rb
|
98
|
+
- lib/chagall/install/main.rb
|
99
|
+
- lib/chagall/install/templates/template.Dockerfile
|
100
|
+
- lib/chagall/install/templates/template.compose.yaml
|
101
|
+
- lib/chagall/settings.rb
|
102
|
+
- lib/chagall/setup/main.rb
|
103
|
+
- lib/chagall/ssh.rb
|
104
|
+
- lib/chagall/version.rb
|
105
|
+
homepage: https://github.com/frontandstart/chagall
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubygems_version: 3.5.22
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Chagall is a deployment tool for Rails applications, optimized for development
|
128
|
+
and production single-server docker/podman compose setups.
|
129
|
+
test_files: []
|