yoker 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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +8 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +223 -0
  5. data/Rakefile +12 -0
  6. data/exe/yoker +177 -0
  7. data/exe/yoker (Copy) +87 -0
  8. data/lib/yoker/cli/base.rb +106 -0
  9. data/lib/yoker/cli/init.rb +193 -0
  10. data/lib/yoker/cli/update.rb +457 -0
  11. data/lib/yoker/configuration.rb +290 -0
  12. data/lib/yoker/detectors/database_detector.rb +35 -0
  13. data/lib/yoker/detectors/rails_detector.rb +48 -0
  14. data/lib/yoker/detectors/version_manager_detector.rb +91 -0
  15. data/lib/yoker/errors.rb +149 -0
  16. data/lib/yoker/generators/base_generator.rb +116 -0
  17. data/lib/yoker/generators/container/docker.rb +255 -0
  18. data/lib/yoker/generators/container/docker_compose.rb +255 -0
  19. data/lib/yoker/generators/container/none.rb +314 -0
  20. data/lib/yoker/generators/database/mysql.rb +147 -0
  21. data/lib/yoker/generators/database/postgresql.rb +64 -0
  22. data/lib/yoker/generators/database/sqlite.rb +123 -0
  23. data/lib/yoker/generators/version_manager/mise.rb +140 -0
  24. data/lib/yoker/generators/version_manager/rbenv.rb +165 -0
  25. data/lib/yoker/generators/version_manager/rvm.rb +246 -0
  26. data/lib/yoker/templates/bin/setup.rb.erb +232 -0
  27. data/lib/yoker/templates/config/database_mysql.yml.erb +47 -0
  28. data/lib/yoker/templates/config/database_postgresql.yml.erb +34 -0
  29. data/lib/yoker/templates/config/database_sqlite.yml.erb +40 -0
  30. data/lib/yoker/templates/docker/Dockerfile.erb +124 -0
  31. data/lib/yoker/templates/docker/docker-compose.yml.erb +117 -0
  32. data/lib/yoker/templates/docker/entrypoint.sh.erb +94 -0
  33. data/lib/yoker/templates/docker/init_mysql.sql.erb +44 -0
  34. data/lib/yoker/templates/docker/init_postgresql.sql.erb +203 -0
  35. data/lib/yoker/templates/version_managers/gemrc.erb +61 -0
  36. data/lib/yoker/templates/version_managers/mise.toml.erb +72 -0
  37. data/lib/yoker/templates/version_managers/rbenv_setup.sh.erb +93 -0
  38. data/lib/yoker/templates/version_managers/rvm_setup.sh.erb +99 -0
  39. data/lib/yoker/templates/version_managers/rvmrc.erb +70 -0
  40. data/lib/yoker/version.rb +5 -0
  41. data/lib/yoker.rb +32 -0
  42. data/sig/yoker.rbs +4 -0
  43. metadata +215 -0
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoker
4
+ module Generators
5
+ module Container
6
+ class None
7
+ attr_reader :config, :shell
8
+
9
+ def initialize(config, shell)
10
+ @config = config
11
+ @shell = shell
12
+ end
13
+
14
+ def generate
15
+ generate_native_setup_instructions
16
+ generate_database_setup_scripts if needs_database_setup?
17
+ generate_service_setup_scripts if needs_service_setup?
18
+ create_development_procfile if needs_procfile?
19
+ end
20
+
21
+ private
22
+
23
+ def generate_native_setup_instructions
24
+ shell.template(
25
+ "native/SETUP_INSTRUCTIONS.md.erb",
26
+ "SETUP_INSTRUCTIONS.md",
27
+ {
28
+ app_name: config[:app_name],
29
+ database: config[:database],
30
+ version_manager: config[:version_manager],
31
+ ruby_version: config[:ruby_version],
32
+ additional_services: config[:additional_services] || [],
33
+ installation_commands: installation_commands,
34
+ service_commands: service_commands
35
+ }
36
+ )
37
+ end
38
+
39
+ def generate_database_setup_scripts
40
+ case config[:database]
41
+ when "postgresql"
42
+ generate_postgresql_setup
43
+ when "mysql"
44
+ generate_mysql_setup
45
+ end
46
+ end
47
+
48
+ def generate_postgresql_setup
49
+ shell.template(
50
+ "native/postgresql-setup.sh.erb",
51
+ "bin/postgresql-setup",
52
+ {
53
+ app_name: config[:app_name],
54
+ version_manager: config[:version_manager]
55
+ }
56
+ )
57
+
58
+ File.chmod(0o755, "bin/postgresql-setup") if File.exist?("bin/postgresql-setup")
59
+ end
60
+
61
+ def generate_mysql_setup
62
+ shell.template(
63
+ "native/mysql-setup.sh.erb",
64
+ "bin/mysql-setup",
65
+ {
66
+ app_name: config[:app_name],
67
+ version_manager: config[:version_manager]
68
+ }
69
+ )
70
+
71
+ File.chmod(0o755, "bin/mysql-setup") if File.exist?("bin/mysql-setup")
72
+ end
73
+
74
+ def generate_service_setup_scripts
75
+ generate_redis_setup if config[:additional_services]&.include?("redis")
76
+
77
+ return unless config[:additional_services]&.include?("sidekiq")
78
+
79
+ generate_sidekiq_setup
80
+ end
81
+
82
+ def generate_redis_setup
83
+ shell.template(
84
+ "native/redis-setup.sh.erb",
85
+ "bin/redis-setup",
86
+ {
87
+ version_manager: config[:version_manager]
88
+ }
89
+ )
90
+
91
+ File.chmod(0o755, "bin/redis-setup") if File.exist?("bin/redis-setup")
92
+ end
93
+
94
+ def generate_sidekiq_setup
95
+ shell.template(
96
+ "native/sidekiq.yml.erb",
97
+ "config/sidekiq.yml",
98
+ {
99
+ app_name: config[:app_name]
100
+ }
101
+ )
102
+ end
103
+
104
+ def create_development_procfile
105
+ services = ["web: bundle exec rails server -b 0.0.0.0 -p 3000"]
106
+
107
+ services << "sidekiq: bundle exec sidekiq" if config[:additional_services]&.include?("sidekiq")
108
+
109
+ shell.create_file("Procfile.dev", services.join("\n"))
110
+ end
111
+
112
+ def installation_commands
113
+ {
114
+ "homebrew_packages" => homebrew_packages,
115
+ "apt_packages" => apt_packages,
116
+ "database_commands" => database_installation_commands,
117
+ "service_commands" => service_installation_commands
118
+ }
119
+ end
120
+
121
+ def homebrew_packages
122
+ packages = []
123
+
124
+ case config[:database]
125
+ when "postgresql"
126
+ packages << "postgresql@16"
127
+ when "mysql"
128
+ packages << "mysql@8.0"
129
+ end
130
+
131
+ packages << "redis" if config[:additional_services]&.include?("redis")
132
+
133
+ packages
134
+ end
135
+
136
+ def apt_packages
137
+ packages = []
138
+
139
+ case config[:database]
140
+ when "postgresql"
141
+ packages << "postgresql" << "postgresql-contrib" << "libpq-dev"
142
+ when "mysql"
143
+ packages << "mysql-server" << "libmysqlclient-dev"
144
+ end
145
+
146
+ packages << "redis-server" if config[:additional_services]&.include?("redis")
147
+
148
+ packages
149
+ end
150
+
151
+ def database_installation_commands
152
+ commands = {}
153
+
154
+ case config[:database]
155
+ when "postgresql"
156
+ commands["macos"] = [
157
+ "brew install postgresql@16",
158
+ "brew services start postgresql@16",
159
+ "createdb #{config[:app_name]}_development",
160
+ "createdb #{config[:app_name]}_test"
161
+ ]
162
+ commands["ubuntu"] = [
163
+ "sudo apt update",
164
+ "sudo apt install -y postgresql postgresql-contrib libpq-dev",
165
+ "sudo systemctl start postgresql",
166
+ "sudo systemctl enable postgresql",
167
+ "sudo -u postgres createuser -s $USER",
168
+ "createdb #{config[:app_name]}_development",
169
+ "createdb #{config[:app_name]}_test"
170
+ ]
171
+ when "mysql"
172
+ commands["macos"] = [
173
+ "brew install mysql@8.0",
174
+ "brew services start mysql@8.0",
175
+ "mysql -u root -e \"CREATE DATABASE #{config[:app_name]}_development;\"",
176
+ "mysql -u root -e \"CREATE DATABASE #{config[:app_name]}_test;\""
177
+ ]
178
+ commands["ubuntu"] = [
179
+ "sudo apt update",
180
+ "sudo apt install -y mysql-server libmysqlclient-dev",
181
+ "sudo systemctl start mysql",
182
+ "sudo systemctl enable mysql",
183
+ "sudo mysql -e \"CREATE DATABASE #{config[:app_name]}_development;\"",
184
+ "sudo mysql -e \"CREATE DATABASE #{config[:app_name]}_test;\""
185
+ ]
186
+ end
187
+
188
+ commands
189
+ end
190
+
191
+ def service_installation_commands
192
+ commands = {}
193
+
194
+ if config[:additional_services]&.include?("redis")
195
+ commands["redis"] = {
196
+ "macos" => [
197
+ "brew install redis",
198
+ "brew services start redis"
199
+ ],
200
+ "ubuntu" => [
201
+ "sudo apt install -y redis-server",
202
+ "sudo systemctl start redis-server",
203
+ "sudo systemctl enable redis-server"
204
+ ]
205
+ }
206
+ end
207
+
208
+ commands
209
+ end
210
+
211
+ def service_commands
212
+ commands = {}
213
+
214
+ case config[:database]
215
+ when "postgresql"
216
+ commands["postgresql"] = {
217
+ "start" => service_start_command("postgresql"),
218
+ "stop" => service_stop_command("postgresql"),
219
+ "status" => service_status_command("postgresql")
220
+ }
221
+ when "mysql"
222
+ commands["mysql"] = {
223
+ "start" => service_start_command("mysql"),
224
+ "stop" => service_stop_command("mysql"),
225
+ "status" => service_status_command("mysql")
226
+ }
227
+ end
228
+
229
+ if config[:additional_services]&.include?("redis")
230
+ commands["redis"] = {
231
+ "start" => service_start_command("redis"),
232
+ "stop" => service_stop_command("redis"),
233
+ "status" => service_status_command("redis")
234
+ }
235
+ end
236
+
237
+ commands
238
+ end
239
+
240
+ def service_start_command(service)
241
+ case service
242
+ when "postgresql"
243
+ {
244
+ "macos" => "brew services start postgresql@16",
245
+ "ubuntu" => "sudo systemctl start postgresql"
246
+ }
247
+ when "mysql"
248
+ {
249
+ "macos" => "brew services start mysql@8.0",
250
+ "ubuntu" => "sudo systemctl start mysql"
251
+ }
252
+ when "redis"
253
+ {
254
+ "macos" => "brew services start redis",
255
+ "ubuntu" => "sudo systemctl start redis-server"
256
+ }
257
+ end
258
+ end
259
+
260
+ def service_stop_command(service)
261
+ case service
262
+ when "postgresql"
263
+ {
264
+ "macos" => "brew services stop postgresql@16",
265
+ "ubuntu" => "sudo systemctl stop postgresql"
266
+ }
267
+ when "mysql"
268
+ {
269
+ "macos" => "brew services stop mysql@8.0",
270
+ "ubuntu" => "sudo systemctl stop mysql"
271
+ }
272
+ when "redis"
273
+ {
274
+ "macos" => "brew services stop redis",
275
+ "ubuntu" => "sudo systemctl stop redis-server"
276
+ }
277
+ end
278
+ end
279
+
280
+ def service_status_command(service)
281
+ case service
282
+ when "postgresql"
283
+ {
284
+ "macos" => "brew services list | grep postgresql",
285
+ "ubuntu" => "sudo systemctl status postgresql"
286
+ }
287
+ when "mysql"
288
+ {
289
+ "macos" => "brew services list | grep mysql",
290
+ "ubuntu" => "sudo systemctl status mysql"
291
+ }
292
+ when "redis"
293
+ {
294
+ "macos" => "brew services list | grep redis",
295
+ "ubuntu" => "sudo systemctl status redis-server"
296
+ }
297
+ end
298
+ end
299
+
300
+ def needs_database_setup?
301
+ config[:database] != "sqlite3"
302
+ end
303
+
304
+ def needs_service_setup?
305
+ config[:additional_services]&.any?
306
+ end
307
+
308
+ def needs_procfile?
309
+ config[:additional_services]&.include?("sidekiq") || config[:database] != "sqlite3"
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoker
4
+ module Generators
5
+ module Database
6
+ class Mysql
7
+ attr_reader :config, :shell
8
+
9
+ def initialize(config, shell)
10
+ @config = config
11
+ @shell = shell
12
+ end
13
+
14
+ def generate
15
+ generate_database_yml
16
+ generate_init_sql if config[:container] != "none"
17
+ generate_mysql_config if needs_custom_config?
18
+ end
19
+
20
+ private
21
+
22
+ def generate_database_yml
23
+ shell.template(
24
+ "config/database_mysql.yml.erb",
25
+ "config/database.yml",
26
+ {
27
+ app_name: config[:app_name],
28
+ username: database_username,
29
+ password: database_password,
30
+ host: database_host,
31
+ port: database_port,
32
+ socket: database_socket,
33
+ encoding: database_encoding
34
+ }
35
+ )
36
+ end
37
+
38
+ def generate_init_sql
39
+ shell.template(
40
+ "docker/init_mysql.sql.erb",
41
+ "docker/init.sql",
42
+ {
43
+ app_name: config[:app_name],
44
+ username: database_username,
45
+ password: database_password,
46
+ root_password: root_password
47
+ }
48
+ )
49
+ end
50
+
51
+ def generate_mysql_config
52
+ shell.template(
53
+ "config/mysql.cnf.erb",
54
+ "config/mysql.cnf",
55
+ mysql_config_options
56
+ )
57
+ end
58
+
59
+ def database_username
60
+ case config[:container]
61
+ when "none"
62
+ ENV["USER"] || "root"
63
+ else
64
+ "rails"
65
+ end
66
+ end
67
+
68
+ def database_password
69
+ case config[:container]
70
+ when "none"
71
+ # For native setup, often no password or user-specific
72
+ ""
73
+ else
74
+ "password"
75
+ end
76
+ end
77
+
78
+ def root_password
79
+ config[:container] == "none" ? "" : "password"
80
+ end
81
+
82
+ def database_host
83
+ case config[:container]
84
+ when "none"
85
+ "localhost"
86
+ when "docker-compose"
87
+ "db"
88
+ else
89
+ "localhost"
90
+ end
91
+ end
92
+
93
+ def database_port
94
+ 3306
95
+ end
96
+
97
+ def database_socket
98
+ # MySQL socket path varies by system
99
+ case config[:container]
100
+ when "none"
101
+ detect_mysql_socket
102
+ else
103
+ nil # Not needed for containerized setup
104
+ end
105
+ end
106
+
107
+ def database_encoding
108
+ "utf8mb4"
109
+ end
110
+
111
+ def detect_mysql_socket
112
+ possible_sockets = [
113
+ "/tmp/mysql.sock", # macOS default
114
+ "/var/run/mysqld/mysqld.sock", # Ubuntu default
115
+ "/var/lib/mysql/mysql.sock", # CentOS/RHEL
116
+ "/opt/homebrew/var/mysql/mysql.sock", # Homebrew on Apple Silicon
117
+ "/usr/local/var/mysql/mysql.sock" # Homebrew on Intel
118
+ ]
119
+
120
+ possible_sockets.find { |socket| File.exist?(socket) }
121
+ end
122
+
123
+ def mysql_config_options
124
+ {
125
+ max_connections: 200,
126
+ innodb_buffer_pool_size: "256M",
127
+ innodb_log_file_size: "64M",
128
+ query_cache_size: "32M",
129
+ tmp_table_size: "64M",
130
+ max_heap_table_size: "64M",
131
+ slow_query_log: development_mode?,
132
+ general_log: development_mode? && config[:verbose]
133
+ }
134
+ end
135
+
136
+ def development_mode?
137
+ true # Always true for development setup
138
+ end
139
+
140
+ def needs_custom_config?
141
+ # Generate custom MySQL config for performance tuning in development
142
+ config[:container] != "none" || config[:additional_services]&.any?
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoker
4
+ module Generators
5
+ module Database
6
+ class Postgresql
7
+ attr_reader :config, :shell
8
+
9
+ def initialize(config, shell)
10
+ @config = config
11
+ @shell = shell
12
+ end
13
+
14
+ def generate
15
+ generate_database_yml
16
+ generate_init_sql if config[:container] != "none"
17
+ end
18
+
19
+ private
20
+
21
+ def generate_database_yml
22
+ shell.template(
23
+ "config/database_postgresql.yml.erb",
24
+ "config/database.yml",
25
+ {
26
+ app_name: config[:app_name],
27
+ username: database_username,
28
+ password: database_password,
29
+ host: database_host,
30
+ port: database_port
31
+ }
32
+ )
33
+ end
34
+
35
+ def generate_init_sql
36
+ shell.template(
37
+ "docker/init_postgresql.sql.erb",
38
+ "docker/init.sql",
39
+ {
40
+ app_name: config[:app_name],
41
+ username: database_username
42
+ }
43
+ )
44
+ end
45
+
46
+ def database_username
47
+ config[:container] == "none" ? ENV["USER"] || "postgres" : "postgres"
48
+ end
49
+
50
+ def database_password
51
+ config[:container] == "none" ? "" : "password"
52
+ end
53
+
54
+ def database_host
55
+ config[:container] == "none" ? "localhost" : "db"
56
+ end
57
+
58
+ def database_port
59
+ 5432
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoker
4
+ module Generators
5
+ module Database
6
+ class Sqlite
7
+ attr_reader :config, :shell
8
+
9
+ def initialize(config, shell)
10
+ @config = config
11
+ @shell = shell
12
+ end
13
+
14
+ def generate
15
+ generate_database_yml
16
+ generate_sqlite_optimizations if needs_optimizations?
17
+ create_database_directory
18
+ generate_backup_script if config[:backup_existing]
19
+ end
20
+
21
+ private
22
+
23
+ def generate_database_yml
24
+ shell.template(
25
+ "config/database_sqlite.yml.erb",
26
+ "config/database.yml",
27
+ {
28
+ app_name: config[:app_name],
29
+ database_path: database_path,
30
+ timeout: database_timeout,
31
+ pool_size: database_pool_size,
32
+ pragmas: sqlite_pragmas
33
+ }
34
+ )
35
+ end
36
+
37
+ def generate_sqlite_optimizations
38
+ shell.template(
39
+ "config/sqlite_optimizations.rb.erb",
40
+ "config/initializers/sqlite_optimizations.rb",
41
+ {
42
+ pragmas: sqlite_performance_pragmas,
43
+ development_mode: true
44
+ }
45
+ )
46
+ end
47
+
48
+ def create_database_directory
49
+ db_dir = File.dirname(database_path_full)
50
+ Dir.mkdir(db_dir) unless Dir.exist?(db_dir)
51
+
52
+ # Create .gitkeep for empty db directory
53
+ gitkeep_path = File.join(db_dir, ".gitkeep")
54
+ File.write(gitkeep_path, "") unless File.exist?(gitkeep_path)
55
+ end
56
+
57
+ def generate_backup_script
58
+ shell.template(
59
+ "scripts/sqlite_backup.sh.erb",
60
+ "bin/sqlite-backup",
61
+ {
62
+ app_name: config[:app_name],
63
+ database_path: database_path_full,
64
+ backup_directory: "db/backups"
65
+ }
66
+ )
67
+
68
+ # Make executable
69
+ File.chmod(0o755, "bin/sqlite-backup") if File.exist?("bin/sqlite-backup")
70
+ end
71
+
72
+ def database_path
73
+ "db/#{config[:app_name]}_development.sqlite3"
74
+ end
75
+
76
+ def database_path_full
77
+ File.join(Dir.pwd, database_path)
78
+ end
79
+
80
+ def database_timeout
81
+ 5000 # 5 seconds
82
+ end
83
+
84
+ def database_pool_size
85
+ # SQLite doesn't benefit from large connection pools
86
+ ENV.fetch("RAILS_MAX_THREADS", 5).to_i
87
+ end
88
+
89
+ def sqlite_pragmas
90
+ {
91
+ # Basic pragmas for development
92
+ "journal_mode" => "WAL",
93
+ "synchronous" => "NORMAL",
94
+ "cache_size" => "-64000", # 64MB cache
95
+ "temp_store" => "memory",
96
+ "mmap_size" => "268435456" # 256MB mmap
97
+ }
98
+ end
99
+
100
+ def sqlite_performance_pragmas
101
+ {
102
+ # Performance-focused pragmas for development
103
+ "journal_mode" => "WAL",
104
+ "synchronous" => "NORMAL",
105
+ "cache_size" => "-64000",
106
+ "temp_store" => "memory",
107
+ "mmap_size" => "268435456",
108
+ "page_size" => "4096",
109
+ "auto_vacuum" => "INCREMENTAL",
110
+ "wal_autocheckpoint" => "1000",
111
+ "optimize" => nil # Run PRAGMA optimize on connection
112
+ }
113
+ end
114
+
115
+ def needs_optimizations?
116
+ # Always generate optimizations for development
117
+ # SQLite can benefit significantly from proper PRAGMA settings
118
+ true
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end