tayo 0.2.3 → 0.3.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 +4 -4
- data/CHANGELOG.md +21 -0
- data/README.md +38 -79
- data/lib/tayo/cli.rb +0 -6
- data/lib/tayo/commands/cf.rb +137 -30
- data/lib/tayo/commands/gh.rb +90 -55
- data/lib/tayo/commands/init.rb +15 -2
- data/lib/tayo/version.rb +1 -1
- metadata +2 -10
- data/lib/tayo/commands/proxy.rb +0 -97
- data/lib/tayo/proxy/cloudflare_client.rb +0 -323
- data/lib/tayo/proxy/docker_manager.rb +0 -150
- data/lib/tayo/proxy/network_config.rb +0 -147
- data/lib/tayo/proxy/traefik_config.rb +0 -303
- data/lib/tayo/proxy/welcome_service.rb +0 -337
- data/lib/templates/welcome/Dockerfile +0 -14
- data/lib/templates/welcome/index.html +0 -173
data/lib/tayo/commands/gh.rb
CHANGED
|
@@ -177,44 +177,48 @@ module Tayo
|
|
|
177
177
|
def create_github_repository
|
|
178
178
|
repo_name = File.basename(Dir.pwd)
|
|
179
179
|
username = `gh api user -q .login`.strip
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
# 조직 목록 가져오기
|
|
182
182
|
orgs_json = `gh api user/orgs -q '.[].login' 2>/dev/null`
|
|
183
183
|
orgs = orgs_json.strip.split("\n").reject(&:empty?)
|
|
184
|
-
|
|
184
|
+
|
|
185
185
|
owner = username
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
if orgs.any?
|
|
188
188
|
prompt = TTY::Prompt.new
|
|
189
189
|
choices = ["#{username} (개인 계정)"] + orgs.map { |org| "#{org} (조직)" }
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
selection = prompt.select("🏢 저장소를 생성할 위치를 선택하세요:", choices)
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
if selection != "#{username} (개인 계정)"
|
|
194
194
|
owner = selection.split(" ").first
|
|
195
195
|
end
|
|
196
196
|
end
|
|
197
|
-
|
|
197
|
+
|
|
198
|
+
@repo_name = repo_name
|
|
199
|
+
@username = owner
|
|
200
|
+
|
|
198
201
|
# 저장소 존재 여부 확인
|
|
199
202
|
repo_exists = system("gh repo view #{owner}/#{repo_name}", out: File::NULL, err: File::NULL)
|
|
200
|
-
|
|
203
|
+
|
|
201
204
|
if repo_exists
|
|
202
205
|
puts "ℹ️ GitHub 저장소가 이미 존재합니다: https://github.com/#{owner}/#{repo_name}".colorize(:yellow)
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
# remote 설정 확인 및 업데이트
|
|
207
|
+
setup_git_remote(owner, repo_name)
|
|
205
208
|
else
|
|
209
|
+
# 기존 origin remote 제거 (있다면)
|
|
210
|
+
system("git remote remove origin 2>/dev/null")
|
|
211
|
+
|
|
206
212
|
create_cmd = if owner == username
|
|
207
213
|
"gh repo create #{repo_name} --private --source=. --remote=origin --push"
|
|
208
214
|
else
|
|
209
215
|
"gh repo create #{owner}/#{repo_name} --private --source=. --remote=origin --push"
|
|
210
216
|
end
|
|
211
|
-
|
|
217
|
+
|
|
212
218
|
result = system(create_cmd)
|
|
213
|
-
|
|
219
|
+
|
|
214
220
|
if result
|
|
215
221
|
puts "✅ GitHub 저장소를 생성했습니다: https://github.com/#{owner}/#{repo_name}".colorize(:green)
|
|
216
|
-
@repo_name = repo_name
|
|
217
|
-
@username = owner
|
|
218
222
|
else
|
|
219
223
|
puts "❌ GitHub 저장소 생성에 실패했습니다.".colorize(:red)
|
|
220
224
|
exit 1
|
|
@@ -222,13 +226,38 @@ module Tayo
|
|
|
222
226
|
end
|
|
223
227
|
end
|
|
224
228
|
|
|
229
|
+
def setup_git_remote(owner, repo_name)
|
|
230
|
+
remote_url = "git@github.com:#{owner}/#{repo_name}.git"
|
|
231
|
+
current_remote = `git remote get-url origin 2>/dev/null`.strip
|
|
232
|
+
|
|
233
|
+
if current_remote.empty?
|
|
234
|
+
# origin이 없으면 추가
|
|
235
|
+
system("git remote add origin #{remote_url}")
|
|
236
|
+
puts " ✅ remote origin을 추가했습니다.".colorize(:green)
|
|
237
|
+
elsif current_remote != remote_url && !current_remote.include?("#{owner}/#{repo_name}")
|
|
238
|
+
# origin이 다른 저장소를 가리키면 업데이트
|
|
239
|
+
system("git remote set-url origin #{remote_url}")
|
|
240
|
+
puts " ✅ remote origin을 업데이트했습니다.".colorize(:green)
|
|
241
|
+
else
|
|
242
|
+
puts " ✅ remote origin이 올바르게 설정되어 있습니다.".colorize(:green)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# push 되지 않은 커밋이 있으면 push
|
|
246
|
+
unpushed = `git log origin/main..HEAD 2>/dev/null`.strip
|
|
247
|
+
if !unpushed.empty? || !system("git rev-parse origin/main", out: File::NULL, err: File::NULL)
|
|
248
|
+
puts " 📤 변경사항을 push합니다...".colorize(:yellow)
|
|
249
|
+
system("git push -u origin main")
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
225
253
|
def create_container_registry
|
|
226
254
|
# Docker 이미지 태그는 소문자여야 함
|
|
227
|
-
|
|
228
|
-
@
|
|
255
|
+
# Kamal은 registry.server + image를 조합하므로 image에는 username/repo만 지정
|
|
256
|
+
@image_name = "#{@username.downcase}/#{@repo_name.downcase}"
|
|
257
|
+
@registry_url = "ghcr.io/#{@image_name}" # 전체 URL (표시용)
|
|
229
258
|
|
|
230
259
|
puts "✅ 컨테이너 레지스트리가 설정되었습니다.".colorize(:green)
|
|
231
|
-
puts " URL: #{registry_url}".colorize(:gray)
|
|
260
|
+
puts " URL: #{@registry_url}".colorize(:gray)
|
|
232
261
|
puts " ℹ️ 컨테이너 레지스트리는 첫 이미지 푸시 시 자동으로 생성됩니다.".colorize(:gray)
|
|
233
262
|
|
|
234
263
|
# Docker로 GitHub Container Registry에 로그인
|
|
@@ -273,31 +302,44 @@ module Tayo
|
|
|
273
302
|
|
|
274
303
|
def update_kamal_config
|
|
275
304
|
content = File.read("config/deploy.yml")
|
|
276
|
-
|
|
277
|
-
# 이미지 설정 업데이트
|
|
278
|
-
#
|
|
279
|
-
content.gsub!(/^image:\s+.*$/, "image: #{@
|
|
280
|
-
|
|
305
|
+
|
|
306
|
+
# 이미지 설정 업데이트
|
|
307
|
+
# Kamal은 registry.server + image를 조합하므로 image에는 username/repo만 지정
|
|
308
|
+
content.gsub!(/^image:\s+.*$/, "image: #{@image_name}")
|
|
309
|
+
|
|
281
310
|
# registry 섹션 업데이트
|
|
282
311
|
if content.include?("registry:")
|
|
283
|
-
#
|
|
284
|
-
# server 라인이 주석처리되어 있는지 확인
|
|
312
|
+
# server 설정 (주석 처리된 경우 활성화)
|
|
285
313
|
if content.match?(/^\s*#\s*server:/)
|
|
286
314
|
content.gsub!(/^\s*#\s*server:\s*.*$/, " server: ghcr.io")
|
|
287
315
|
elsif content.match?(/^\s*server:/)
|
|
288
316
|
content.gsub!(/^\s*server:\s*.*$/, " server: ghcr.io")
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# username 설정 (주석 처리된 경우 활성화)
|
|
320
|
+
if content.match?(/^\s*#\s*username:/)
|
|
321
|
+
content.gsub!(/^\s*#\s*username:\s*.*$/, " username: #{@username.downcase}")
|
|
322
|
+
elsif content.match?(/^\s*username:/)
|
|
323
|
+
content.gsub!(/^\s*username:\s*.*$/, " username: #{@username.downcase}")
|
|
289
324
|
else
|
|
290
|
-
#
|
|
291
|
-
content.gsub!(/(
|
|
325
|
+
# username이 없으면 server 다음에 추가
|
|
326
|
+
content.gsub!(/(^\s*server:\s*ghcr\.io\s*$)/, "\\1\n username: #{@username.downcase}")
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# password 설정 (주석 처리된 경우 활성화)
|
|
330
|
+
# 형식: " # password:\n # - KAMAL_REGISTRY_PASSWORD"
|
|
331
|
+
if content.match?(/^(\s*)#\s*password:\s*\n\s*#\s+-\s*KAMAL_REGISTRY_PASSWORD/m)
|
|
332
|
+
content.gsub!(
|
|
333
|
+
/^(\s*)#\s*password:\s*\n\s*#\s+-\s*KAMAL_REGISTRY_PASSWORD/m,
|
|
334
|
+
"\\1password:\n\\1 - KAMAL_REGISTRY_PASSWORD"
|
|
335
|
+
)
|
|
292
336
|
end
|
|
293
|
-
# username도 소문자로 변환
|
|
294
|
-
content.gsub!(/^\s*username:\s+.*$/, " username: #{@username.downcase}")
|
|
295
337
|
else
|
|
296
338
|
# registry 섹션 추가
|
|
297
339
|
registry_config = "\n# Container registry configuration\nregistry:\n server: ghcr.io\n username: #{@username.downcase}\n password:\n - KAMAL_REGISTRY_PASSWORD\n"
|
|
298
340
|
content.gsub!(/^# Credentials for your image host\.\nregistry:.*?^$/m, registry_config)
|
|
299
341
|
end
|
|
300
|
-
|
|
342
|
+
|
|
301
343
|
File.write("config/deploy.yml", content)
|
|
302
344
|
|
|
303
345
|
# GitHub 토큰을 Kamal secrets 파일에 설정
|
|
@@ -312,35 +354,28 @@ module Tayo
|
|
|
312
354
|
def setup_kamal_secrets
|
|
313
355
|
# .kamal 디렉토리 생성
|
|
314
356
|
Dir.mkdir(".kamal") unless Dir.exist?(".kamal")
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if existing_content.include?("KAMAL_REGISTRY_PASSWORD")
|
|
328
|
-
# 기존 값 업데이트
|
|
329
|
-
updated_content = existing_content.gsub(/^KAMAL_REGISTRY_PASSWORD=.*$/, "KAMAL_REGISTRY_PASSWORD=#{token}")
|
|
330
|
-
else
|
|
331
|
-
# 새로 추가
|
|
332
|
-
updated_content = existing_content.empty? ? "KAMAL_REGISTRY_PASSWORD=#{token}\n" : "#{existing_content.chomp}\nKAMAL_REGISTRY_PASSWORD=#{token}\n"
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
File.write(secrets_file, updated_content)
|
|
336
|
-
puts "✅ GitHub 토큰이 .kamal/secrets에 설정되었습니다.".colorize(:green)
|
|
337
|
-
|
|
338
|
-
# .gitignore에 secrets 파일 추가
|
|
339
|
-
add_to_gitignore(".kamal/secrets")
|
|
357
|
+
|
|
358
|
+
secrets_file = ".kamal/secrets"
|
|
359
|
+
|
|
360
|
+
# 기존 secrets 파일 읽기 (있다면)
|
|
361
|
+
existing_content = File.exist?(secrets_file) ? File.read(secrets_file) : ""
|
|
362
|
+
|
|
363
|
+
# KAMAL_REGISTRY_PASSWORD가 실제로 설정되어 있는지 확인 (주석 제외)
|
|
364
|
+
password_line = 'KAMAL_REGISTRY_PASSWORD=$(gh auth token)'
|
|
365
|
+
|
|
366
|
+
if existing_content.match?(/^KAMAL_REGISTRY_PASSWORD=/)
|
|
367
|
+
# 기존 값 업데이트
|
|
368
|
+
updated_content = existing_content.gsub(/^KAMAL_REGISTRY_PASSWORD=.*$/, password_line)
|
|
340
369
|
else
|
|
341
|
-
|
|
342
|
-
|
|
370
|
+
# 새로 추가 (파일 끝에)
|
|
371
|
+
updated_content = "#{existing_content.chomp}\n#{password_line}\n"
|
|
343
372
|
end
|
|
373
|
+
|
|
374
|
+
File.write(secrets_file, updated_content)
|
|
375
|
+
puts "✅ KAMAL_REGISTRY_PASSWORD가 .kamal/secrets에 설정되었습니다.".colorize(:green)
|
|
376
|
+
|
|
377
|
+
# .gitignore에 secrets 파일 추가
|
|
378
|
+
add_to_gitignore(".kamal/secrets")
|
|
344
379
|
end
|
|
345
380
|
|
|
346
381
|
def add_to_gitignore(file_path)
|
data/lib/tayo/commands/init.rb
CHANGED
|
@@ -93,8 +93,21 @@ module Tayo
|
|
|
93
93
|
|
|
94
94
|
puts "🎨 Welcome 페이지를 생성합니다...".colorize(:yellow)
|
|
95
95
|
|
|
96
|
-
# Welcome 컨트롤러 생성
|
|
97
|
-
system("rails generate controller Welcome index --skip-routes --no-helper --no-assets")
|
|
96
|
+
# Welcome 컨트롤러 생성 시도
|
|
97
|
+
unless system("rails generate controller Welcome index --skip-routes --no-helper --no-assets")
|
|
98
|
+
puts " ⚠️ rails generate 실패. 수동으로 파일을 생성합니다.".colorize(:yellow)
|
|
99
|
+
# 디렉토리와 컨트롤러 파일 직접 생성
|
|
100
|
+
FileUtils.mkdir_p("app/controllers")
|
|
101
|
+
FileUtils.mkdir_p("app/views/welcome")
|
|
102
|
+
|
|
103
|
+
controller_content = <<~RUBY
|
|
104
|
+
class WelcomeController < ApplicationController
|
|
105
|
+
def index
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
RUBY
|
|
109
|
+
File.write("app/controllers/welcome_controller.rb", controller_content)
|
|
110
|
+
end
|
|
98
111
|
|
|
99
112
|
# 프로젝트 이름 가져오기
|
|
100
113
|
project_name = File.basename(Dir.pwd).gsub(/[-_]/, ' ').split.map(&:capitalize).join(' ')
|
data/lib/tayo/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tayo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 이원섭wonsup Lee/Alfonso
|
|
@@ -99,18 +99,10 @@ files:
|
|
|
99
99
|
- lib/tayo/commands/cf.rb
|
|
100
100
|
- lib/tayo/commands/gh.rb
|
|
101
101
|
- lib/tayo/commands/init.rb
|
|
102
|
-
- lib/tayo/commands/proxy.rb
|
|
103
102
|
- lib/tayo/commands/sqlite.rb
|
|
104
103
|
- lib/tayo/dockerfile_modifier.rb
|
|
105
|
-
- lib/tayo/proxy/cloudflare_client.rb
|
|
106
|
-
- lib/tayo/proxy/docker_manager.rb
|
|
107
|
-
- lib/tayo/proxy/network_config.rb
|
|
108
|
-
- lib/tayo/proxy/traefik_config.rb
|
|
109
|
-
- lib/tayo/proxy/welcome_service.rb
|
|
110
104
|
- lib/tayo/templates/solid-cable-sqlite-setup.md
|
|
111
105
|
- lib/tayo/version.rb
|
|
112
|
-
- lib/templates/welcome/Dockerfile
|
|
113
|
-
- lib/templates/welcome/index.html
|
|
114
106
|
- pkg/homebody-0.1.0.gem
|
|
115
107
|
- repomix-output.xml
|
|
116
108
|
- sig/tayo.rbs
|
|
@@ -134,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
126
|
- !ruby/object:Gem::Version
|
|
135
127
|
version: '0'
|
|
136
128
|
requirements: []
|
|
137
|
-
rubygems_version:
|
|
129
|
+
rubygems_version: 4.0.3
|
|
138
130
|
specification_version: 4
|
|
139
131
|
summary: Rails deployment tool for home servers
|
|
140
132
|
test_files: []
|
data/lib/tayo/commands/proxy.rb
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "colorize"
|
|
4
|
-
require "tty-prompt"
|
|
5
|
-
require_relative "../proxy/cloudflare_client"
|
|
6
|
-
require_relative "../proxy/docker_manager"
|
|
7
|
-
require_relative "../proxy/network_config"
|
|
8
|
-
require_relative "../proxy/traefik_config"
|
|
9
|
-
require_relative "../proxy/welcome_service"
|
|
10
|
-
|
|
11
|
-
module Tayo
|
|
12
|
-
module Commands
|
|
13
|
-
class Proxy
|
|
14
|
-
def execute
|
|
15
|
-
puts "🚀 Kamal Proxy와 Caddy 설정을 시작합니다...".colorize(:green)
|
|
16
|
-
puts ""
|
|
17
|
-
|
|
18
|
-
# 1. Cloudflare 설정
|
|
19
|
-
cloudflare = Tayo::Proxy::CloudflareClient.new
|
|
20
|
-
cloudflare.ensure_token
|
|
21
|
-
|
|
22
|
-
# 2. 네트워크 설정
|
|
23
|
-
network = Tayo::Proxy::NetworkConfig.new
|
|
24
|
-
network.detect_ips
|
|
25
|
-
network.configure_ports
|
|
26
|
-
|
|
27
|
-
# 3. Docker 확인
|
|
28
|
-
docker = Tayo::Proxy::DockerManager.new
|
|
29
|
-
docker.check_containers
|
|
30
|
-
|
|
31
|
-
# 4. 도메인 선택
|
|
32
|
-
selected_domains = cloudflare.select_domains
|
|
33
|
-
|
|
34
|
-
if selected_domains.empty?
|
|
35
|
-
puts "❌ 도메인이 선택되지 않았습니다. 종료합니다.".colorize(:red)
|
|
36
|
-
return
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# 5. DNS 설정
|
|
40
|
-
puts "\n📝 DNS 레코드를 설정합니다...".colorize(:yellow)
|
|
41
|
-
cloudflare.setup_dns_records(selected_domains, network.public_ip)
|
|
42
|
-
|
|
43
|
-
# 6. Welcome 서비스 확인
|
|
44
|
-
welcome = Tayo::Proxy::WelcomeService.new
|
|
45
|
-
welcome.ensure_running
|
|
46
|
-
|
|
47
|
-
# 7. Traefik 설정
|
|
48
|
-
traefik = Tayo::Proxy::TraefikConfig.new
|
|
49
|
-
traefik.setup(selected_domains)
|
|
50
|
-
|
|
51
|
-
# 8. 최종 안내
|
|
52
|
-
show_summary(selected_domains, network)
|
|
53
|
-
|
|
54
|
-
rescue => e
|
|
55
|
-
puts "❌ 오류가 발생했습니다: #{e.message}".colorize(:red)
|
|
56
|
-
puts e.backtrace.join("\n") if ENV["DEBUG"]
|
|
57
|
-
exit 1
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
def show_summary(domains, network)
|
|
63
|
-
puts "\n" + "="*60
|
|
64
|
-
puts "✅ Proxy 설정이 완료되었습니다!".colorize(:green)
|
|
65
|
-
puts "="*60
|
|
66
|
-
|
|
67
|
-
puts "\n📋 설정 요약:".colorize(:yellow)
|
|
68
|
-
puts "━"*40
|
|
69
|
-
puts "공인 IP: #{network.public_ip}".colorize(:white)
|
|
70
|
-
puts "내부 IP: #{network.internal_ip}".colorize(:white)
|
|
71
|
-
puts "Traefik: 80, 443 포트 사용 중".colorize(:white)
|
|
72
|
-
puts "대시보드: http://localhost:8080".colorize(:white)
|
|
73
|
-
puts "━"*40
|
|
74
|
-
|
|
75
|
-
puts "\n🌐 활성화된 도메인:".colorize(:yellow)
|
|
76
|
-
domains.each do |domain|
|
|
77
|
-
if network.use_custom_ports?
|
|
78
|
-
puts "• #{domain}".colorize(:cyan)
|
|
79
|
-
puts " HTTP: http://#{domain}:#{network.external_http}".colorize(:gray)
|
|
80
|
-
puts " HTTPS: https://#{domain}:#{network.external_https}".colorize(:gray)
|
|
81
|
-
else
|
|
82
|
-
puts "• #{domain}".colorize(:cyan)
|
|
83
|
-
puts " HTTP: http://#{domain}".colorize(:gray)
|
|
84
|
-
puts " HTTPS: https://#{domain}".colorize(:gray)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
if network.use_custom_ports?
|
|
89
|
-
puts "\n💡 공유기 포트포워딩을 설정하세요!".colorize(:yellow)
|
|
90
|
-
network.show_port_forwarding_guide
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
puts "\n🎉 모든 설정이 완료되었습니다!".colorize(:green)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|