kdeploy 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.
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kdeploy
4
+ # ERB template management class
5
+ class Template
6
+ attr_reader :path, :content, :variables
7
+
8
+ def initialize(template_path, variables = {})
9
+ @path = template_path
10
+ @variables = variables
11
+ @content = load_template
12
+ end
13
+
14
+ # Render template with variables
15
+ # @param additional_vars [Hash] Additional variables to merge
16
+ # @return [String] Rendered content
17
+ def render(additional_vars = {})
18
+ all_vars = @variables.merge(additional_vars)
19
+
20
+ # Create binding with variables
21
+ template_binding = create_binding(all_vars)
22
+
23
+ # Render ERB template
24
+ erb = ERB.new(@content, trim_mode: '-')
25
+ erb.result(template_binding)
26
+ rescue StandardError => e
27
+ raise TemplateError, "Failed to render template #{@path}: #{e.message}"
28
+ end
29
+
30
+ # Render and save to file
31
+ # @param output_path [String] Output file path
32
+ # @param additional_vars [Hash] Additional variables
33
+ def render_to_file(output_path, additional_vars = {})
34
+ rendered_content = render(additional_vars)
35
+
36
+ # Ensure output directory exists
37
+ FileUtils.mkdir_p(File.dirname(output_path))
38
+
39
+ # Write rendered content
40
+ File.write(output_path, rendered_content)
41
+
42
+ KdeployLogger.info("Template rendered to: #{output_path}")
43
+ output_path
44
+ end
45
+
46
+ # Check if template file exists
47
+ # @return [Boolean] True if template exists
48
+ def exist?
49
+ File.exist?(@path)
50
+ end
51
+
52
+ # Get template modification time
53
+ # @return [Time] Template file mtime
54
+ def mtime
55
+ File.mtime(@path) if exist?
56
+ end
57
+
58
+ private
59
+
60
+ # Load template content from file
61
+ # @return [String] Template content
62
+ def load_template
63
+ raise TemplateError, "Template file not found: #{@path}" unless File.exist?(@path)
64
+
65
+ File.read(@path)
66
+ rescue StandardError => e
67
+ raise TemplateError, "Failed to load template #{@path}: #{e.message}"
68
+ end
69
+
70
+ # Create binding with variables
71
+ # @param vars [Hash] Variables hash
72
+ # @return [Binding] Binding object with variables
73
+ def create_binding(vars)
74
+ # Create a clean binding
75
+ template_binding = binding
76
+
77
+ # Define variables in the binding
78
+ vars.each do |key, value|
79
+ template_binding.local_variable_set(key.to_sym, value)
80
+ end
81
+
82
+ # Define helper methods
83
+ template_binding.local_variable_set(:hostname, vars[:hostname] || vars['hostname'])
84
+ template_binding.local_variable_set(:user, vars[:user] || vars['user'])
85
+ template_binding.local_variable_set(:port, vars[:port] || vars['port'])
86
+
87
+ template_binding
88
+ end
89
+ end
90
+
91
+ # Template manager for handling multiple templates
92
+ class TemplateManager
93
+ attr_reader :template_dir, :global_variables
94
+
95
+ def initialize(template_dir = 'templates', global_variables = {})
96
+ @template_dir = template_dir
97
+ @global_variables = global_variables
98
+ @templates = {}
99
+ end
100
+
101
+ # Load template by name
102
+ # @param template_name [String] Template name (without .erb extension)
103
+ # @param variables [Hash] Template variables
104
+ # @return [Template] Template object
105
+ def load_template(template_name, variables = {})
106
+ template_path = resolve_template_path(template_name)
107
+ all_variables = @global_variables.merge(variables)
108
+
109
+ @templates[template_name] = Template.new(template_path, all_variables)
110
+ end
111
+
112
+ # Render template by name
113
+ # @param template_name [String] Template name
114
+ # @param variables [Hash] Additional variables
115
+ # @return [String] Rendered content
116
+ def render(template_name, variables = {})
117
+ template = @templates[template_name] || load_template(template_name, variables)
118
+ template.render(variables)
119
+ end
120
+
121
+ # Render template to file
122
+ # @param template_name [String] Template name
123
+ # @param output_path [String] Output file path
124
+ # @param variables [Hash] Additional variables
125
+ # @return [String] Output file path
126
+ def render_to_file(template_name, output_path, variables = {})
127
+ template = @templates[template_name] || load_template(template_name, variables)
128
+ template.render_to_file(output_path, variables)
129
+ end
130
+
131
+ # List available templates
132
+ # @return [Array<String>] Template names
133
+ def list_templates
134
+ return [] unless Dir.exist?(@template_dir)
135
+
136
+ Dir.glob("#{@template_dir}/**/*.erb").map do |path|
137
+ File.basename(path, '.erb')
138
+ end
139
+ end
140
+
141
+ # Set global variables
142
+ # @param variables [Hash] Global variables
143
+ def global_variables=(variables)
144
+ @global_variables.merge!(variables)
145
+ end
146
+
147
+ private
148
+
149
+ # Resolve template file path
150
+ # @param template_name [String] Template name
151
+ # @return [String] Full template path
152
+ def resolve_template_path(template_name)
153
+ # Add .erb extension if not present
154
+ template_name += '.erb' unless template_name.end_with?('.erb')
155
+
156
+ # Try template directory first
157
+ template_path = File.join(@template_dir, template_name)
158
+ return template_path if File.exist?(template_path)
159
+
160
+ # Try relative to current directory
161
+ return template_name if File.exist?(template_name)
162
+
163
+ # Try absolute path
164
+ return template_name if File.absolute_path?(template_name) && File.exist?(template_name)
165
+
166
+ # Default to template directory path (will be checked by Template class)
167
+ template_path
168
+ end
169
+ end
170
+
171
+ # Template-related errors
172
+ class TemplateError < StandardError; end
173
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kdeploy
4
+ # Current version of Kdeploy
5
+ VERSION = '0.1.0'
6
+ end
data/lib/kdeploy.rb ADDED
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # External dependencies
4
+ require 'concurrent-ruby'
5
+ require 'net/ssh'
6
+ require 'net/scp'
7
+ require 'yaml'
8
+ require 'logger'
9
+ require 'colorize'
10
+ require 'thor'
11
+ require 'tty-prompt'
12
+ require 'erb'
13
+ require 'fileutils'
14
+ require 'json'
15
+
16
+ # Internal dependencies - ordered by dependency chain
17
+ module Kdeploy
18
+ # Load all components
19
+ autoload :VERSION, 'kdeploy/version'
20
+ autoload :Configuration, 'kdeploy/configuration'
21
+ autoload :Logger, 'kdeploy/logger'
22
+ autoload :Host, 'kdeploy/host'
23
+ autoload :Inventory, 'kdeploy/inventory'
24
+ autoload :Template, 'kdeploy/template'
25
+ autoload :SSHConnection, 'kdeploy/ssh_connection'
26
+ autoload :Command, 'kdeploy/command'
27
+ autoload :Task, 'kdeploy/task'
28
+ autoload :Pipeline, 'kdeploy/pipeline'
29
+ autoload :DSL, 'kdeploy/dsl'
30
+ autoload :Runner, 'kdeploy/runner'
31
+ autoload :Statistics, 'kdeploy/statistics'
32
+ autoload :Banner, 'kdeploy/banner'
33
+ autoload :CLI, 'kdeploy/cli'
34
+
35
+ # Base error class for Kdeploy
36
+ class Error < StandardError; end
37
+
38
+ # Error raised when connection fails
39
+ class ConnectionError < Error; end
40
+
41
+ # Error raised when command execution fails
42
+ class CommandError < Error; end
43
+
44
+ # Error raised when configuration is invalid
45
+ class ConfigurationError < Error; end
46
+
47
+ class << self
48
+ attr_accessor :configuration
49
+ attr_reader :statistics
50
+
51
+ # Initialize statistics
52
+ # @return [Statistics] Statistics instance
53
+ def statistics
54
+ @statistics ||= Statistics.new
55
+ end
56
+
57
+ # Configure kdeploy
58
+ # @yield [Configuration] Configuration instance
59
+ # @return [Configuration] Configuration instance
60
+ def configure
61
+ self.configuration ||= Configuration.new
62
+ yield(configuration) if block_given?
63
+ configuration
64
+ end
65
+
66
+ # Load and execute deployment script
67
+ # @param script_file [String] Path to deployment script
68
+ # @return [Pipeline] Loaded pipeline
69
+ # @raise [ConfigurationError] If script file not found
70
+ def load_script(script_file)
71
+ validate_script_file(script_file)
72
+ script_dir = File.dirname(File.expand_path(script_file))
73
+ create_pipeline(script_file, script_dir)
74
+ end
75
+
76
+ # Execute deployment pipeline
77
+ # @param pipeline [Pipeline] Pipeline to execute
78
+ # @return [Hash] Execution results
79
+ def execute(pipeline)
80
+ runner = Runner.new(pipeline)
81
+ runner.execute
82
+ end
83
+
84
+ # Convenient method to load and execute script
85
+ # @param script_file [String] Path to deployment script
86
+ # @return [Hash] Execution results
87
+ def run(script_file)
88
+ pipeline = load_script(script_file)
89
+ execute(pipeline)
90
+ end
91
+
92
+ private
93
+
94
+ def validate_script_file(script_file)
95
+ return if File.exist?(script_file)
96
+
97
+ raise ConfigurationError, "Script file not found: #{script_file}"
98
+ end
99
+
100
+ def create_pipeline(script_file, script_dir)
101
+ dsl = DSL.new(script_dir)
102
+ dsl.instance_eval(File.read(script_file), script_file)
103
+ dsl.pipeline
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Common tasks for kdeploy projects
4
+ # This file demonstrates modular script organization
5
+ #
6
+ # To use this file, include it in your main deployment script:
7
+ # include 'scripts/common_tasks.rb' if File.exist?('scripts/common_tasks.rb')
8
+
9
+ # ===================================================================
10
+ # HOST DEFINITIONS (Required for remote tasks)
11
+ # ===================================================================
12
+
13
+ # Define default hosts - update these with your actual hosts
14
+ host 'localhost', user: ENV.fetch('USER', 'deploy'), port: 22, roles: %i[web app db all]
15
+
16
+ # Uncomment and update these for real deployments:
17
+ # host 'web1.example.com', user: 'deploy', port: 22, roles: [:web, :app]
18
+ # host 'web2.example.com', user: 'deploy', port: 22, roles: [:web, :app]
19
+ # host 'db1.example.com', user: 'deploy', port: 22, roles: [:db]
20
+
21
+ # ===================================================================
22
+ # COMMON UTILITY TASKS
23
+ # ===================================================================
24
+
25
+ # Shared pre-deployment checks (LOCAL + REMOTE)
26
+ task 'pre_deploy_checks', on: :all do
27
+ local 'echo "🔍 Running pre-deployment checks..."'
28
+ local 'echo "Current user: $(whoami)"'
29
+ local 'echo "Current directory: $(pwd)"'
30
+ local 'echo "Git status:" && git status --porcelain || echo "Not a git repository"'
31
+ local 'echo "✅ Pre-deployment checks completed"'
32
+
33
+ # Add a dummy remote command to satisfy validation
34
+ run 'echo "Pre-deployment check completed on {{hostname}}"'
35
+ end
36
+
37
+ # Common environment setup
38
+ task 'setup_environment', on: :all do
39
+ run 'echo "Setting up environment on {{hostname}}..."'
40
+
41
+ # Set timezone
42
+ run 'sudo timedatectl set-timezone UTC', ignore_errors: true
43
+
44
+ # Update system packages
45
+ run 'sudo apt-get update -qq', ignore_errors: true
46
+
47
+ # Install common utilities
48
+ run 'sudo apt-get install -y -qq htop curl wget vim git unzip', ignore_errors: true
49
+
50
+ run 'echo "✅ Environment setup completed on {{hostname}}"'
51
+ end
52
+
53
+ # Common log rotation setup
54
+ task 'setup_log_rotation', on: :all do
55
+ run 'echo "Setting up log rotation on {{hostname}}..."'
56
+
57
+ # Application log rotation
58
+ run <<~LOGROTATE
59
+ sudo tee /etc/logrotate.d/{{application}} > /dev/null << 'EOF'
60
+ {{deploy_to}}/shared/logs/*.log {
61
+ daily
62
+ missingok
63
+ rotate 52
64
+ compress
65
+ delaycompress
66
+ notifempty
67
+ create 644 {{user}} {{user}}
68
+ postrotate
69
+ sudo systemctl reload {{application}} || true
70
+ endscript
71
+ }
72
+ EOF
73
+ LOGROTATE
74
+
75
+ run 'echo "✅ Log rotation setup completed on {{hostname}}"'
76
+ end
77
+
78
+ # Common system monitoring setup
79
+ task 'setup_monitoring', on: :all do
80
+ run 'echo "Setting up basic monitoring on {{hostname}}..."'
81
+
82
+ # Install system monitoring tools
83
+ run 'sudo apt-get install -y htop iotop iftop nethogs', ignore_errors: true
84
+
85
+ # Setup basic disk space monitoring
86
+ run <<~MONITORING
87
+ sudo tee /usr/local/bin/disk-alert.sh > /dev/null << 'EOF'
88
+ #!/bin/bash
89
+ THRESHOLD=90
90
+ USAGE=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
91
+ if [ $USAGE -gt $THRESHOLD ]; then
92
+ echo "Warning: Disk usage is $USAGE% on $(hostname)"
93
+ logger "Disk usage alert: $USAGE% on $(hostname)"
94
+ fi
95
+ EOF
96
+ MONITORING
97
+
98
+ run 'sudo chmod +x /usr/local/bin/disk-alert.sh'
99
+
100
+ # Add to crontab
101
+ run 'echo "0 */6 * * * /usr/local/bin/disk-alert.sh" | sudo crontab -', ignore_errors: true
102
+
103
+ run 'echo "✅ Monitoring setup completed on {{hostname}}"'
104
+ end
105
+
106
+ # Common security hardening
107
+ task 'security_hardening', on: :all do
108
+ run 'echo "Applying security hardening on {{hostname}}..."'
109
+
110
+ # Disable root login
111
+ run 'sudo sed -i "s/PermitRootLogin yes/PermitRootLogin no/" /etc/ssh/sshd_config', ignore_errors: true
112
+
113
+ # Configure firewall basics
114
+ run 'sudo ufw --force reset', ignore_errors: true
115
+ run 'sudo ufw default deny incoming', ignore_errors: true
116
+ run 'sudo ufw default allow outgoing', ignore_errors: true
117
+ run 'sudo ufw allow ssh', ignore_errors: true
118
+ run 'sudo ufw allow 80', ignore_errors: true
119
+ run 'sudo ufw allow 443', ignore_errors: true
120
+ run 'sudo ufw --force enable', ignore_errors: true
121
+
122
+ # Install fail2ban
123
+ run 'sudo apt-get install -y fail2ban', ignore_errors: true
124
+ run 'sudo systemctl enable fail2ban', ignore_errors: true
125
+ run 'sudo systemctl start fail2ban', ignore_errors: true
126
+
127
+ run 'echo "✅ Security hardening completed on {{hostname}}"'
128
+ end
129
+
130
+ # Common performance tuning
131
+ task 'performance_tuning', on: :all do
132
+ run 'echo "Applying performance tuning on {{hostname}}..."'
133
+
134
+ # System limits
135
+ run <<~LIMITS
136
+ sudo tee -a /etc/security/limits.conf > /dev/null << 'EOF'
137
+ * soft nofile 65536
138
+ * hard nofile 65536
139
+ * soft nproc 32768
140
+ * hard nproc 32768
141
+ EOF
142
+ LIMITS
143
+
144
+ # Kernel parameters
145
+ run <<~SYSCTL
146
+ sudo tee -a /etc/sysctl.conf > /dev/null << 'EOF'
147
+ # Network performance
148
+ net.core.rmem_max = 16777216
149
+ net.core.wmem_max = 16777216
150
+ net.ipv4.tcp_rmem = 4096 65536 16777216
151
+ net.ipv4.tcp_wmem = 4096 65536 16777216
152
+ EOF
153
+ SYSCTL
154
+
155
+ run 'sudo sysctl -p', ignore_errors: true
156
+
157
+ run 'echo "✅ Performance tuning completed on {{hostname}}"'
158
+ end
159
+
160
+ # ===================================================================
161
+ # COMMON UTILITY FUNCTIONS
162
+ # ===================================================================
163
+
164
+ # Health check wrapper (LOCAL + REMOTE)
165
+ task 'health_check_all', on: :all do
166
+ local 'echo "🏥 Running comprehensive health checks..."'
167
+
168
+ # You can call other scripts from here
169
+ local 'echo "1. System health check"'
170
+ local 'echo "2. Application health check"'
171
+ local 'echo "3. Service status check"'
172
+ local 'echo "✅ Health checks completed"'
173
+
174
+ # Add a dummy remote command to satisfy validation
175
+ run 'echo "Health check completed on {{hostname}}"'
176
+ end
177
+
178
+ # ===================================================================
179
+ # EMERGENCY PROCEDURES
180
+ # ===================================================================
181
+
182
+ # Emergency stop all services
183
+ task 'emergency_stop', on: :all do
184
+ run 'echo "🚨 Emergency stop initiated on {{hostname}}"'
185
+ run 'sudo systemctl stop nginx || echo "nginx not found"', ignore_errors: true
186
+ run 'sudo systemctl stop apache2 || echo "apache2 not found"', ignore_errors: true
187
+ run 'echo "🛑 Services stopped on {{hostname}}"'
188
+ end
189
+
190
+ # Emergency start all services
191
+ task 'emergency_start', on: :all do
192
+ run 'echo "🚨 Emergency start initiated on {{hostname}}"'
193
+ run 'sudo systemctl start nginx || echo "nginx not found"', ignore_errors: true
194
+ run 'sudo systemctl start apache2 || echo "apache2 not found"', ignore_errors: true
195
+ run 'echo "🚀 Services started on {{hostname}}"'
196
+ end
197
+
198
+ # ===================================================================
199
+ # USAGE EXAMPLES
200
+ # ===================================================================
201
+ #
202
+ # This file can be included in your main deploy.rb script:
203
+ #
204
+ # include 'scripts/common_tasks.rb' if File.exist?('scripts/common_tasks.rb')
205
+ #
206
+ # Then you can call these tasks from your deployment workflow:
207
+ #
208
+ # task 'full_setup' do
209
+ # run_task 'pre_deploy_checks'
210
+ # run_task 'setup_environment'
211
+ # run_task 'security_hardening'
212
+ # run_task 'performance_tuning'
213
+ # end
214
+ #
215
+ # Or run individual tasks:
216
+ # kdeploy deploy scripts/common_tasks.rb --task setup_environment
217
+ # kdeploy deploy scripts/common_tasks.rb --task security_hardening
218
+ # kdeploy deploy scripts/common_tasks.rb --task emergency_stop
data/scripts/deploy.rb ADDED
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Simple kdeploy test deployment script - only using local commands
4
+
5
+ # Set global variables
6
+ set 'application', 'myapp'
7
+ set 'hostname', 'localhost'
8
+ set 'version', '1.0.0'
9
+
10
+ # All commands are executed locally, no SSH required
11
+ local 'echo "=== Starting kdeploy deployment process ==="'
12
+ local 'echo "Application: {{application}}"'
13
+ local 'echo "Version: {{version}}"'
14
+ local 'echo "Target: {{hostname}}"'
15
+ local 'echo "Current user: $(whoami)"'
16
+ local 'echo "Current date: $(date)"'
17
+
18
+ local 'echo "=== Preparation Phase ==="'
19
+ local 'echo "Preparing deployment environment..."'
20
+ local 'mkdir -p /tmp/kdeploy-test'
21
+ local 'echo "Environment prepared!"'
22
+
23
+ local 'echo "=== Build Phase ==="'
24
+ local 'echo "Building application..."'
25
+ local 'sleep 1'
26
+ local 'echo "Build completed successfully!"'
27
+
28
+ local 'echo "=== Testing Phase ==="'
29
+ local 'echo "Running tests..."'
30
+ local 'echo "Test 1: Basic functionality... PASSED"'
31
+ local 'echo "Test 2: Integration tests... PASSED"'
32
+ local 'echo "All tests passed!"'
33
+
34
+ local 'echo "=== Deployment Phase ==="'
35
+ local 'echo "Deploying to {{hostname}}..."'
36
+ local 'echo "{{application}} {{version}}" > /tmp/kdeploy-test/version.txt'
37
+ local 'echo "Application deployed to /tmp/kdeploy-test/"'
38
+
39
+ local 'echo "=== Verification Phase ==="'
40
+ local 'echo "Verifying deployment..."'
41
+ local 'cat /tmp/kdeploy-test/version.txt'
42
+ local 'echo "Deployment verification completed!"'
43
+
44
+ local 'echo "=== Cleanup Phase ==="'
45
+ local 'echo "Cleaning up temporary files..."'
46
+ local 'rm -rf /tmp/kdeploy-test'
47
+ local 'echo "Cleanup completed!"'
48
+
49
+ local 'echo "=== Deployment Complete ==="'
50
+ local 'echo "{{application}} {{version}} successfully deployed!"'