shippy 0.2.5 → 0.2.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c8514e699c44724105cd11bc6b9034e7fe36c992403642bba781a16e7c74aac
4
- data.tar.gz: 5356c87f5316dff2637f5f78a0271a8fdcce4a41bfbd6fb128e3aca654087452
3
+ metadata.gz: e9ad6a9811193f1cba32cbbbb3caaadbe7baabd6cdeedd7b9d3462af3a311e41
4
+ data.tar.gz: 68c148787b05aaa7bd3cea51bad53ab73638c592d0dd1967329fcda427d64466
5
5
  SHA512:
6
- metadata.gz: e25771e02e107140eebaf7f8f0f90c66974040d3ebdb0258e819da8a508060d9e297d37e2e2b2e9a4d355f2f4f5491963aa7bd5440c45ca6751170d241fafc72
7
- data.tar.gz: ea3feb9af3d644ef5e82149d09da0479cb46f010e07cdb1422dd73a58d90afac106c0db47f3b7e59b5732dc310b8876491848263c0b228fdbb4b1866d71689f2
6
+ metadata.gz: 926e9d0c76ff27f776ff16917b4c6142c3446961f2f4b343e15ad88096a8bcdbd1e53de6e699686f058c6c51b506da9ad4608c1fc143af282bb68d981c90c1c6
7
+ data.tar.gz: dbf4e8d28ac653c354effda9ec3ec1ff901c4d3c1554e41db644d92b226c98c840fbf1f963b876f182f91861d2ff248f7854560374d28a6af3bcff847c6ef9e4
@@ -0,0 +1,32 @@
1
+ require "minitar"
2
+ require "zlib"
3
+ require "fileutils"
4
+
5
+ module Shippy
6
+ class Builder
7
+ attr_reader :archive_path
8
+
9
+ def initialize(app)
10
+ @app = app
11
+ @archive_path = "/tmp/v#{Time.now.to_i}.tar.gz"
12
+ end
13
+
14
+ def compile
15
+ Shippy::Compiler.new(@app, config: SHIPPY.config).compile
16
+ end
17
+
18
+ def archive
19
+ FileUtils.cd("builds/apps") do
20
+ File.open(@archive_path, "wb") do |file|
21
+ zlib = Zlib::GzipWriter.new(file)
22
+ Minitar.pack(@app.name, zlib)
23
+ end
24
+ end
25
+ @archive_path
26
+ end
27
+
28
+ def cleanup
29
+ FileUtils.rm_f(@archive_path)
30
+ end
31
+ end
32
+ end
@@ -3,223 +3,62 @@ class Shippy::Cli::App < Shippy::Cli::Base
3
3
 
4
4
  def initialize(...)
5
5
  super
6
- load_app
7
- end
8
-
9
- desc "compile", "compile application"
10
- def compile
11
- say "compiling #{app_name} on #{SHIPPY.host}...", :magenta
12
-
13
- SHIPPY.compile
6
+ @remote_app = Shippy::Remote::App.new(SHIPPY.host, app_name)
7
+ @docker = Shippy::Docker::Client.new(SHIPPY.host)
14
8
  end
15
9
 
16
10
  desc "deploy", "Deploy application"
17
11
  def deploy
18
- say "Deploying #{app_name} on #{SHIPPY.host}...", :magenta
19
-
20
- SHIPPY.compile
21
- archive
22
- backup
23
- upload
24
- create_network
25
- refresh
26
- stop_old
27
- pre_start_hooks
28
- start
29
- cleanup
30
- status
12
+ Shippy::Deployer.new(
13
+ app_name: app_name,
14
+ host: SHIPPY.host,
15
+ ui: self
16
+ ).deploy
31
17
  end
32
18
 
33
19
  desc "refresh", "Pull new images"
34
20
  def refresh
35
- say "Pulling images for #{app_name} on #{SHIPPY.host}...", :magenta
36
-
37
- within_app do
38
- execute :docker, "compose", "pull"
39
- end
21
+ say "Pulling images...", :magenta
22
+ @docker.compose_pull(@remote_app.current_path, stream: true)
40
23
  end
41
24
 
42
25
  desc "start", "Start application"
43
26
  def start
44
- say "Starting #{app_name} on #{SHIPPY.host}...", :magenta
45
-
46
- within_app do
47
- execute :docker, "compose", "up", "-d", SHIPPY.app&.deploy_flags
48
- end
27
+ say "Starting...", :magenta
28
+ app_config = Shippy::Repo.load_app(app_name)
29
+ @docker.compose_up(@remote_app.current_path, flags: app_config&.deploy_flags)
49
30
  end
50
31
 
51
32
  desc "restart", "Restart application"
52
- option :service, aliases: "-S", type: :string, desc: "Restart only a SERVICE"
33
+ option :service, aliases: "-S", type: :string
53
34
  def restart
54
- say "Restarting #{app_name} on #{SHIPPY.host}...", :magenta
55
- restart_options = options[:service] ? ["--", options[:service]] : []
56
-
57
- within_app do
58
- execute :docker, "compose", "restart", *restart_options
59
- end
35
+ say "Restarting...", :magenta
36
+ services = options[:service] ? [options[:service]] : []
37
+ @docker.compose_restart(@remote_app.current_path, services)
60
38
  end
61
39
 
62
40
  desc "stop", "Stop application"
63
41
  def stop
64
- say "Stopping #{app_name} on #{SHIPPY.host}...", :magenta
65
-
66
- within_app do
67
- execute :docker, "compose", "down", "--remove-orphans"
68
- end
42
+ say "Stopping...", :magenta
43
+ @docker.compose_down(@remote_app.current_path)
69
44
  end
70
45
 
71
46
  desc "logs", "Show app logs"
47
+ option :follow, aliases: "-f", type: :boolean
72
48
  def logs
73
- within_app do
74
- puts capture(:docker, "compose", "logs", verbosity: Logger::INFO)
75
- end
49
+ @docker.logs(@remote_app.current_path, follow: options[:follow])
76
50
  end
77
51
 
78
52
  desc "status", "Show app status"
79
53
  def status
80
- say "Status for #{app_name} on #{SHIPPY.host}...", :magenta
81
-
82
- within_app do
83
- puts capture(:docker, "compose", "ps", "-a")
84
- end
85
- end
86
-
87
- private
88
-
89
- def load_app
90
- with_app do
91
- SHIPPY.app = Shippy::Repo.load_app(app_name)
92
- end
93
- end
94
-
95
- def archive
96
- FileUtils.cd("builds/apps") do
97
- Minitar.pack(app_name, Zlib::GzipWriter.new(File.open(archive_path, "wb")))
98
- end
99
- end
100
-
101
- def prepare_directory
102
- on(SHIPPY.host) do
103
- execute :mkdir, "-p", SHIPPY.current_app_path
104
- end
105
- end
106
-
107
- def backup
108
- backups_path = self.backups_path
109
-
110
- on(SHIPPY.host) do
111
- if test("[ -d #{SHIPPY.current_app_path} ]")
112
- execute :mkdir, "-p", backups_path
113
- execute :mv, SHIPPY.current_app_path, backups_path
114
- end
115
- end
116
- end
117
-
118
- def stop_old
119
- backup_path = backups_path.join(app_name)
120
-
121
- on(SHIPPY.host) do
122
- if test("[ -f #{backup_path.join("docker-compose.yml")} ]")
123
- within backup_path do
124
- execute :docker, "compose", "down", "--remove-orphans"
125
- end
126
- end
127
- end
128
- end
129
-
130
- def upload
131
- source_path = SHIPPY.current_app_path.join("..").expand_path
132
- archive_path = self.archive_path
133
- prepare_directory
134
-
135
- on(SHIPPY.host) do
136
- tmp = capture "mktemp"
137
- upload! archive_path, tmp
138
-
139
- execute :tar, "-xzpf", tmp, "-C", source_path
140
- execute :rm, tmp
141
- end
142
- end
143
-
144
- def cleanup
145
- remove_archive
146
- remove_old_releases
147
- end
148
-
149
- def remove_archive
150
- archive_path = self.archive_path
151
-
152
- run_locally do
153
- execute :rm, archive_path
154
- end
155
- end
156
-
157
- def remove_old_releases
158
- app_backups_paths = backups_path.join("..").expand_path
159
-
160
- on(SHIPPY.host) do
161
- if test("[ -d #{app_backups_paths} ]")
162
- releases = capture(:ls, "-x", app_backups_paths).split
163
- directories = (releases - releases.last(SHIPPY.config.keep_releases)).map do |release|
164
- app_backups_paths.join(release).to_s
165
- end
166
-
167
- if directories.any?
168
- directories.each_slice(100) do |directories_batch|
169
- execute :rm, "-rf", *directories_batch
170
- end
171
- end
172
- end
173
- end
174
- end
175
-
176
- def pre_start_hooks
177
- say "Running #{app_name} hooks on #{SHIPPY.host}...", :magenta
178
-
179
- within_app do
180
- SHIPPY.app.each_hook do |service, options, command|
181
- execute :docker, "compose", "run", "--rm", options, service, command
182
- end
183
- end
54
+ @docker.status(@remote_app.current_path)
184
55
  end
185
56
 
186
- def create_network
187
- on(SHIPPY.host) do
188
- result = capture("docker", "network", "ls", "-q", "-f", "'name=lan_access'")
189
-
190
- if result.empty?
191
- execute :docker, "network", "create", "lan_access"
192
- end
193
- end
194
- end
195
-
196
- def archive_path
197
- @archive_path ||= "/tmp/v#{Time.now.to_i}.tar.gz"
198
- end
199
-
200
- def backups_path
201
- @backups_path ||= SHIPPY.deploy_path.join("backups", app_name, Time.now.to_i.to_s)
202
- end
203
-
204
- def with_app
205
- path = Pathname.new("apps").join(app_name)
206
-
207
- if path.exist?
208
- yield
209
- else
210
- error_on_missing_app
211
- end
212
- end
213
-
214
- def error_on_missing_app
215
- raise "No app by the name of '#{app_name}'"
216
- end
217
-
218
- def within_app(&block)
219
- on(SHIPPY.host) do
220
- within(SHIPPY.current_app_path) do
221
- instance_eval(&block)
222
- end
223
- end
57
+ desc "compile", "Compile application"
58
+ def compile
59
+ say "Compiling #{app_name}...", :magenta
60
+ # Load app config and pass to builder
61
+ app_config = Shippy::Repo.load_app(app_name)
62
+ Shippy::Builder.new(app_config).compile
224
63
  end
225
64
  end
@@ -13,10 +13,6 @@ class Shippy::Cli::Init < Thor::Group
13
13
  copy_file "templates/config/shippy.yml", "config/shippy.yml"
14
14
  end
15
15
 
16
- def create_secrets_file
17
- copy_file "templates/config/secrets.yml", "config/secrets.yml"
18
- end
19
-
20
16
  def init_gemfile
21
17
  return if ::File.exist?("Gemfile")
22
18
 
@@ -24,7 +20,9 @@ class Shippy::Cli::Init < Thor::Group
24
20
  end
25
21
 
26
22
  def add_shippy_to_gemfile
27
- insert_into_file "Gemfile", "gem 'shippy', '~> #{Shippy::VERSION}'"
23
+ unless File.read("Gemfile").include?("shippy")
24
+ insert_into_file "Gemfile", "gem 'shippy', '~> #{Shippy::VERSION}'"
25
+ end
28
26
 
29
27
  run "bundle install", abort_on_failure: false
30
28
  end
@@ -46,5 +44,10 @@ class Shippy::Cli::Init < Thor::Group
46
44
  insert_into_file ".gitignore", "/.bundle/\n"
47
45
  insert_into_file ".gitignore", "/builds/\n"
48
46
  insert_into_file ".gitignore", "/config/secrets.yml\n"
47
+ insert_into_file ".gitignore", "/config/master.key\n"
48
+ end
49
+
50
+ def generate_secret!
51
+ Shippy::SecretsManager.new.ensure_key_exists!
49
52
  end
50
53
  end
@@ -1,21 +1,26 @@
1
1
  class Shippy::Cli::Prune < Shippy::Cli::Base
2
2
  desc "all", "Prune unused images and stopped containers"
3
3
  def all
4
- containers
5
- images
4
+ maintainer.clean_all
6
5
  end
7
6
 
8
- desc "images", "Prune unused images older than 7 days"
7
+ desc "images", "Prune unused images"
8
+ option :hours, type: :numeric, desc: "Override default retention hours"
9
9
  def images
10
- on(SHIPPY.host) do
11
- execute :docker, :image, :prune, "--all", "--force", "--filter", "until=#{7.days.in_hours}h"
12
- end
10
+ hours = options[:hours] || Shippy::Maintainer::IMAGE_RETENTION_HOURS
11
+ maintainer.clean_images(hours: hours)
13
12
  end
14
13
 
15
- desc "containers", "Prune stopped containers older than 3 days"
14
+ desc "containers", "Prune stopped containers"
15
+ option :hours, type: :numeric, desc: "Override default retention hours"
16
16
  def containers
17
- on(SHIPPY.host) do
18
- execute :docker, :container, :prune, "--force", "--filter", "until=#{3.days.in_hours}h"
19
- end
17
+ hours = options[:hours] || Shippy::Maintainer::CONTAINER_RETENTION_HOURS
18
+ maintainer.clean_containers(hours: hours)
19
+ end
20
+
21
+ private
22
+
23
+ def maintainer
24
+ @maintainer ||= Shippy::Maintainer.new(SHIPPY.host, ui: self)
20
25
  end
21
26
  end
@@ -5,3 +5,4 @@ wildcard_domain: 'local.homelab.com'
5
5
  local_domain: local
6
6
  deploy_to: '/var/lib/homelab'
7
7
  secrets_file: 'config/secrets.yml.enc'
8
+ keep_releases: 10
@@ -1,42 +1,39 @@
1
1
  module Shippy
2
2
  class Commander
3
- attr_accessor :config_file, :verbosity, :repo, :app
3
+ attr_accessor :config_file, :verbosity
4
+
4
5
  delegate :host, :deploy_path, to: :config
5
6
 
6
7
  def initialize(config_file: nil, verbosity: :info)
7
- @config_file, @verbosity = config_file, verbosity
8
+ @config_file = config_file
9
+ @verbosity = verbosity
8
10
  end
9
11
 
10
12
  def config
11
13
  @config ||= Shippy::Config
12
14
  .new(config_file)
13
- .tap { |config| configure_sshkit_with(config) }
15
+ .tap { |c| configure_sshkit_with(c) }
14
16
  end
15
17
 
16
18
  def with_verbosity(level)
17
19
  old_level = verbosity
18
-
19
20
  self.verbosity = level
20
21
  SSHKit.config.output_verbosity = level
21
-
22
22
  yield
23
23
  ensure
24
24
  self.verbosity = old_level
25
25
  SSHKit.config.output_verbosity = old_level
26
26
  end
27
27
 
28
- def compile
29
- Shippy::Compiler.new(app, config: config).compile
30
- end
31
-
32
- def current_app_path
33
- deploy_path.join("apps", app.name)
34
- end
35
-
36
28
  private
37
29
 
38
30
  def configure_sshkit_with(config)
39
- SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = config.ssh_options }
31
+ SSHKit::Backend::Netssh.configure do |ssh|
32
+ ssh.ssh_options = config.ssh_options
33
+ ssh.pty = true
34
+ end
35
+
36
+ # Map commands if they aren't standard on the remote
40
37
  SSHKit.config.command_map[:docker] = "docker"
41
38
  SSHKit.config.command_map[:docker_compose] = "docker-compose"
42
39
  SSHKit.config.output_verbosity = verbosity
data/lib/shippy/config.rb CHANGED
@@ -12,15 +12,29 @@ module Shippy
12
12
  end
13
13
 
14
14
  def ssh_options
15
- {user: ssh_user, auth_methods: ["publickey"]}.compact
15
+ options = {
16
+ user: ssh_user,
17
+ port: ssh_port,
18
+ keys: ssh_keys,
19
+ auth_methods: ["publickey"]
20
+ }
21
+
22
+ options.compact
16
23
  end
17
24
 
18
25
  def ssh_user
19
- if data[:ssh].present?
20
- data.dig(:ssh, :user) || "root"
21
- else
22
- "root"
23
- end
26
+ data.dig(:ssh, :user) || "root"
27
+ end
28
+
29
+ def ssh_port
30
+ data.dig(:ssh, :port)
31
+ end
32
+
33
+ def ssh_keys
34
+ keys = data.dig(:ssh, :keys)
35
+ return if keys.nil?
36
+
37
+ Array(keys).map { |k| File.expand_path(k) }
24
38
  end
25
39
 
26
40
  def deploy_path
@@ -0,0 +1,60 @@
1
+ module Shippy
2
+ class Deployer
3
+ def initialize(app_name:, host:, ui:)
4
+ @app_name = app_name
5
+ @ui = ui
6
+ @app_config = Shippy::Repo.load_app(app_name)
7
+ @builder = Shippy::Builder.new(@app_config)
8
+ @remote_app = Shippy::Remote::App.new(host, app_name)
9
+ @docker = Shippy::Docker::Client.new(host)
10
+ end
11
+
12
+ def deploy
13
+ @ui.say "Deploying #{@app_name}...", :magenta
14
+
15
+ @builder.compile
16
+ archive = @builder.archive
17
+
18
+ @ui.say "Backing up current version...", :blue
19
+ backup_path = @remote_app.backup_current_release
20
+
21
+ @ui.say "Uploading new version...", :blue
22
+ @remote_app.prepare_directories
23
+ @remote_app.upload_release(archive)
24
+
25
+ @docker.ensure_network("lan_access")
26
+
27
+ @ui.say "Pulling images...", :blue
28
+ @docker.compose_pull(@remote_app.current_path, stream: true)
29
+
30
+ if backup_path
31
+ @ui.say "Stopping old version...", :yellow
32
+ @docker.compose_down(backup_path)
33
+ end
34
+
35
+ run_hooks
36
+
37
+ @ui.say "Starting application...", :green
38
+ @docker.compose_up(@remote_app.current_path, flags: @app_config&.deploy_flags)
39
+
40
+ @builder.cleanup
41
+ @remote_app.prune_old_releases(keep: SHIPPY.config.keep_releases)
42
+
43
+ @docker.status(@remote_app.current_path)
44
+ rescue => e
45
+ @ui.error "Deployment failed: #{e.message}"
46
+ raise e
47
+ end
48
+
49
+ private
50
+
51
+ def run_hooks
52
+ return unless @app_config
53
+
54
+ @app_config.each_hook do |service, options, command|
55
+ @ui.say "Running hook: #{command}", :cyan
56
+ @docker.run_hook(@remote_app.current_path, service, options, command)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,95 @@
1
+ require "sshkit"
2
+ require "sshkit/dsl"
3
+
4
+ module Shippy
5
+ module Docker
6
+ class Client
7
+ include SSHKit::DSL
8
+
9
+ def initialize(host)
10
+ @host = host
11
+ end
12
+
13
+ def ensure_network(name)
14
+ on(@host) do
15
+ if test("[ -z \"$(docker network ls -q -f name=#{name})\" ]")
16
+ execute :docker, :network, :create, name
17
+ end
18
+ end
19
+ end
20
+
21
+ def compose_up(path, flags: "")
22
+ on(@host) do
23
+ within(path) do
24
+ args = [:docker, :compose, :up, "-d"]
25
+ args.concat(Array(flags)) unless flags.nil? || flags.empty?
26
+ execute(*args)
27
+ end
28
+ end
29
+ end
30
+
31
+ def compose_down(path)
32
+ on(@host) do
33
+ if test("[ -f #{path.join("docker-compose.yml")} ]")
34
+ within(path) do
35
+ execute :docker, :compose, :down, "--remove-orphans"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def compose_pull(path, stream: false)
42
+ handler = stream ? Shippy::SshStreamHandler.new : nil
43
+ on(@host) do
44
+ within(path) do
45
+ execute :docker, :compose, :pull, interaction_handler: handler
46
+ end
47
+ end
48
+ end
49
+
50
+ def compose_restart(path, services = [])
51
+ on(@host) do
52
+ within(path) do
53
+ execute :docker, :compose, :restart, *services
54
+ end
55
+ end
56
+ end
57
+
58
+ def run_hook(path, service, options, command)
59
+ on(@host) do
60
+ within(path) do
61
+ # Ensure options are treated as args
62
+ args = [:docker, :compose, :run, "--rm"]
63
+ args << options if options && !options.empty?
64
+ args << service
65
+ args << command
66
+ execute(*args)
67
+ end
68
+ end
69
+ end
70
+
71
+ def logs(path, follow: false)
72
+ cmd_args = [:docker, "compose", "logs"]
73
+ cmd_args << "--follow" if follow
74
+
75
+ on(@host) do
76
+ within(path) do
77
+ if follow
78
+ execute(*cmd_args, interaction_handler: Shippy::SshStreamHandler.new)
79
+ else
80
+ puts capture(*cmd_args)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def status(path)
87
+ on(@host) do
88
+ within(path) do
89
+ puts capture(:docker, :compose, :ps, "-a")
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,23 @@
1
+ module Shippy
2
+ module Docker
3
+ class System
4
+ include SSHKit::DSL
5
+
6
+ def initialize(host)
7
+ @host = host
8
+ end
9
+
10
+ def prune_images(hours_until:)
11
+ on(@host) do
12
+ execute :docker, :image, :prune, "--all", "--force", "--filter", "until=#{hours_until}h"
13
+ end
14
+ end
15
+
16
+ def prune_containers(hours_until:)
17
+ on(@host) do
18
+ execute :docker, :container, :prune, "--force", "--filter", "until=#{hours_until}h"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ module Shippy
2
+ class Maintainer
3
+ IMAGE_RETENTION_HOURS = 24 * 7 # 7 days
4
+ CONTAINER_RETENTION_HOURS = 24 * 3 # 3 days
5
+
6
+ def initialize(host, ui: nil)
7
+ @host = host
8
+ @ui = ui
9
+ @docker = Shippy::Docker::System.new(host)
10
+ end
11
+
12
+ def clean_all
13
+ clean_containers
14
+ clean_images
15
+ end
16
+
17
+ def clean_images(hours: IMAGE_RETENTION_HOURS)
18
+ notify("Pruning images older than #{hours} hours...")
19
+ @docker.prune_images(hours_until: hours)
20
+ end
21
+
22
+ def clean_containers(hours: CONTAINER_RETENTION_HOURS)
23
+ notify("Pruning stopped containers older than #{hours} hours...")
24
+ @docker.prune_containers(hours_until: hours)
25
+ end
26
+
27
+ private
28
+
29
+ def notify(message)
30
+ @ui&.say(message, :yellow)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,75 @@
1
+ require "sshkit"
2
+ require "sshkit/dsl"
3
+
4
+ module Shippy
5
+ module Remote
6
+ class App
7
+ include SSHKit::DSL
8
+
9
+ def initialize(host, app_name)
10
+ @host = host
11
+ @app_name = app_name
12
+ end
13
+
14
+ def prepare_directories
15
+ path = current_path
16
+ on(@host) { execute :mkdir, "-p", path }
17
+ end
18
+
19
+ def upload_release(local_archive_path)
20
+ source_dir = current_path.join("..")
21
+
22
+ on(@host) do
23
+ remote_tar = capture("mktemp").strip
24
+ upload! local_archive_path, remote_tar
25
+
26
+ execute :tar, "-xzpf", remote_tar, "-C", source_dir
27
+
28
+ execute :rm, remote_tar
29
+ end
30
+ end
31
+
32
+ def backup_current_release
33
+ ts = Time.now.to_i.to_s
34
+ timestamp_dir = backups_path.join(ts)
35
+ current = current_path
36
+
37
+ on(@host) do
38
+ if test("[ -d #{current} ]")
39
+ execute :mkdir, "-p", timestamp_dir
40
+ execute :mv, current, timestamp_dir
41
+ end
42
+ end
43
+
44
+ timestamp_dir.join(@app_name)
45
+ end
46
+
47
+ def prune_old_releases(keep: 5)
48
+ base = backups_path
49
+
50
+ on(@host) do
51
+ if test("[ -d #{base} ]")
52
+ releases = capture(:ls, "-1", base).split
53
+
54
+ if releases.count > keep
55
+ to_delete = releases - releases.last(keep)
56
+
57
+ if to_delete.any?
58
+ paths = to_delete.map { |r| base.join(r).to_s }
59
+ execute :rm, "-rf", *paths
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def current_path
67
+ SHIPPY.deploy_path.join("apps", @app_name)
68
+ end
69
+
70
+ def backups_path
71
+ SHIPPY.deploy_path.join("backups", @app_name)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,12 @@
1
+ module Shippy
2
+ class SshStreamHandler
3
+ def on_data(command, stream_name, data, channel)
4
+ # command : The SSHKit::Command object
5
+ # stream_name : :stdout or :stderr
6
+ # data : The raw string chunk (e.g. "Pulling layer 1...\n")
7
+ # channel : The SSH channel (can be used to send input back)
8
+
9
+ print data
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Shippy
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  end
data/lib/shippy.rb CHANGED
@@ -18,6 +18,13 @@ require_relative "shippy/compose"
18
18
  require_relative "shippy/service"
19
19
  require_relative "shippy/repo"
20
20
  require_relative "shippy/compiler"
21
+ require_relative "shippy/builder"
22
+ require_relative "shippy/deployer"
23
+ require_relative "shippy/maintainer"
24
+ require_relative "shippy/ssh_stream_handler"
25
+ require_relative "shippy/remote/app"
26
+ require_relative "shippy/docker/client"
27
+ require_relative "shippy/docker/system"
21
28
 
22
29
  module Shippy
23
30
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shippy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marius Bobin
@@ -147,6 +147,20 @@ dependencies:
147
147
  - - "~>"
148
148
  - !ruby/object:Gem::Version
149
149
  version: '1.3'
150
+ - !ruby/object:Gem::Dependency
151
+ name: irb
152
+ requirement: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ type: :development
158
+ prerelease: false
159
+ version_requirements: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
150
164
  email:
151
165
  - marius@mbobin.me
152
166
  executables:
@@ -158,6 +172,7 @@ files:
158
172
  - README.md
159
173
  - bin/shippy
160
174
  - lib/shippy.rb
175
+ - lib/shippy/builder.rb
161
176
  - lib/shippy/cli.rb
162
177
  - lib/shippy/cli/app.rb
163
178
  - lib/shippy/cli/base.rb
@@ -175,10 +190,16 @@ files:
175
190
  - lib/shippy/compiler.rb
176
191
  - lib/shippy/compose.rb
177
192
  - lib/shippy/config.rb
193
+ - lib/shippy/deployer.rb
194
+ - lib/shippy/docker/client.rb
195
+ - lib/shippy/docker/system.rb
196
+ - lib/shippy/maintainer.rb
197
+ - lib/shippy/remote/app.rb
178
198
  - lib/shippy/repo.rb
179
199
  - lib/shippy/secrets.rb
180
200
  - lib/shippy/secrets_manager.rb
181
201
  - lib/shippy/service.rb
202
+ - lib/shippy/ssh_stream_handler.rb
182
203
  - lib/shippy/version.rb
183
204
  homepage: https://mbobin.me/shippy
184
205
  licenses: