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.
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Chagall
2
+ VERSION = "0.0.1.beta1"
3
+ 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: []