deplomat 0.1.13 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +261 -2
- data/VERSION +1 -1
- data/deplomat.gemspec +7 -3
- data/lib/deplomat/directives.rb +58 -1
- data/lib/deplomat/exceptions.rb +1 -0
- data/lib/deplomat/node.rb +19 -0
- data/spec/deployment_requisites/1_req.rb +3 -0
- data/spec/deployment_requisites/2_req.rb +3 -0
- data/spec/deployment_requisites/not_a_task.rb +0 -0
- data/spec/directives_spec.rb +40 -1
- data/spec/fixtures/.deployment_requisites_counter +1 -0
- data/spec/node_spec.rb +15 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c91e9845ccf8392c7fff51614f56677df22aa87f00d29a1147fcf510c54ef88a
|
4
|
+
data.tar.gz: 4a35bfdfa7ae33d72234e4ef46d6b612b0bc5743285d3055662c44efb5ba62e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4192a3789c9f630cb1640830ec417d3e05c4331a37c371ab265d6691ec788812a9606bd1a950d2971611a682f43344a88e060c9e27c163daca4cf73cbe033196
|
7
|
+
data.tar.gz: 0e1044ee65039255401f375057b61cea4d55f121c3d851f789bab155f90e0f70a4cc80e109e70da0f1f46566298305636b5a24ffed2766f1c16bb2b96239745a
|
data/README.rdoc
CHANGED
@@ -1,3 +1,262 @@
|
|
1
|
-
|
1
|
+
# Deplomat - a stack agnostic deployment system
|
2
|
+
Deplomat is a stack agnostic deployment system that uses bash and ssh commands.
|
3
|
+
The purpose of Deplomat is to be a suitable deployment system for all and easily scale from
|
4
|
+
a one-man operation to big teams and multiple servers.
|
2
5
|
|
3
|
-
|
6
|
+
How does it work?
|
7
|
+
-----------------
|
8
|
+
* It uses SSH to send commands to remote servers and can also execute scripts on a local machine.
|
9
|
+
* The SSH connection is opened in such a way that it is persistent, so it's fast to execute multiple commands over it;
|
10
|
+
* The deployment script is a simple ruby script. You just create your own methods and then call them.
|
11
|
+
|
12
|
+
Let's take a look at an example script, step by step. For the purposes of this tutorial, we'll simplify things.
|
13
|
+
The process will resemble a typical web-app deployment, but won't be too specific or complicated.
|
14
|
+
|
15
|
+
#!/usr/bin/env ruby
|
16
|
+
require 'rubygems'
|
17
|
+
require 'deplomat'
|
18
|
+
|
19
|
+
$env = ARGV[0] || "staging"
|
20
|
+
$local = Deplomat::LocalNode.new(path: "/home/user/myproject")
|
21
|
+
$server = Deplomat::RemoteNode.new host: "myproject.com", port: 22, user: "deploy"
|
22
|
+
$branch = ARGV[1] || $env
|
23
|
+
$app_name = "myproject"
|
24
|
+
$project_dir = "/var/www/#{$app_name}"
|
25
|
+
$release_dir = Time.now.to_i
|
26
|
+
|
27
|
+
We've defined a bunch of global variables here. They're global to easily distinguish them from unimportant things, but they might as well
|
28
|
+
have been regular local variables. We have also created a `LocalNode` object - it is used to run commands on the local machine; and a `RemoteNode`
|
29
|
+
object, which, you guessed it, is used to run commands on a remote machine. You can theoretically have as many different remote nodes
|
30
|
+
as you want, but in our example we'll just have one.
|
31
|
+
|
32
|
+
Ok, now let's write the actual deployment code.
|
33
|
+
|
34
|
+
# Create the release dir
|
35
|
+
$server.create_dir("#{$project_dir}/#$env/releases/#{$release_dir}")
|
36
|
+
|
37
|
+
# Upload to the remote dir
|
38
|
+
$server.upload("#{$local.current_path}/*", "#{$project_dir}/#$env/releases/#{$release_dir}/")
|
39
|
+
|
40
|
+
# cd to the release dir we've just created and uploaded, we'll do things inside it.
|
41
|
+
$server.cd("#{$project_dir}/#$env/releases/#{$release_dir}")
|
42
|
+
|
43
|
+
Here, we used standard `Deplomat::RemoteNode` methods. First, we used `Deplomat::RemoteNode#create_dir` which
|
44
|
+
ran a `"mkdir -p /home/user/myproject/staging/releases/[timestamp]"` command on the server for us. Then we used the
|
45
|
+
`Deplomat::RemoteNode#upload` which uploaded all the files from our project directory to the server. And, finally,
|
46
|
+
we've changed current directory on the server to the one we've just created. So far so good, but we now need
|
47
|
+
to do a few things on the server before we can restart our webapp:
|
48
|
+
|
49
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/database.yml", "config/")
|
50
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/secrets.yml", "config/")
|
51
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/cable.yml", "config/")
|
52
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/redis.yml", "config/")
|
53
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/log", "./")
|
54
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/public/uploads", "public/")
|
55
|
+
|
56
|
+
Here, we created symlinks to the files and directories that persist across deployments. For example, the files users
|
57
|
+
upload should not evaporate with each new release and so we put them in the `/var/www/myproject/staging/shared/public/uploads`
|
58
|
+
directory and then symlink them to the release directory.
|
59
|
+
|
60
|
+
For the final steps, we need to migrate the database, instruct the server to restart and symlink the release directory:
|
61
|
+
|
62
|
+
# Migrate DB. Our migration script requires a login shell to work properly,
|
63
|
+
# so we instruct deplomat to run it using a login shell.
|
64
|
+
$server.execute("bin/migrate_db #{$env}", login_shell: true)
|
65
|
+
|
66
|
+
# Restart the server
|
67
|
+
$server.execute("mkdir -p tmp")
|
68
|
+
$server.touch("tmp/restart.txt")
|
69
|
+
|
70
|
+
if $server.file_exists?("#{$project_dir}/#$env/current")
|
71
|
+
$server.mv("#{$project_dir}/#$env/current", "#{$project_dir}/#$env/previous")
|
72
|
+
end
|
73
|
+
$server.create_symlink("#{$project_dir}/#$env/releases/#{$release_dir}", "#{$project_dir}/#$env/current")
|
74
|
+
|
75
|
+
You can see how we used `#file_exists?`, `#touch`, `#mv` methods here. Those are also standard methods of `Deplomat::Node`.
|
76
|
+
Notice how we checked if `"#{$project_dir}/#$env/current"` exists first, because it might not exist on our first deployment.
|
77
|
+
However if it does exist, it's wise to rename this symlink into `previous` so we can later undo the deployment easily by renaming that
|
78
|
+
symlink back to `current`.
|
79
|
+
|
80
|
+
Our script is ready, now you can run `./deploy` (assuming it's in the root dir of your project and you've made it executable).
|
81
|
+
|
82
|
+
|
83
|
+
Adding more structure with methods
|
84
|
+
----------------------------------
|
85
|
+
Our script above is ok, however as your project grows you'll discover you'd want to add more structure to it. It
|
86
|
+
makes sense to group some actions into methods, so you can have something like this:
|
87
|
+
|
88
|
+
#!/usr/bin/env ruby
|
89
|
+
require 'rubygems'
|
90
|
+
require 'deplomat'
|
91
|
+
require 'deployment_steps' # << THIS IS WHERE WE PUT OUR METHODS THAT WE USE BELOW
|
92
|
+
|
93
|
+
$env = ARGV[0] || "staging"
|
94
|
+
$local = Deplomat::LocalNode.new(path: "/home/user/myproject")
|
95
|
+
$server = Deplomat::RemoteNode.new host: "myproject.com", port: 22, user: "deploy"
|
96
|
+
$branch = ARGV[1] || $env
|
97
|
+
$app_name = "myproject"
|
98
|
+
$project_dir = "/var/www/#{$app_name}"
|
99
|
+
$release_dir = Time.now.to_i
|
100
|
+
|
101
|
+
create_release_dir_and_upload!
|
102
|
+
create_symlinks!
|
103
|
+
migrate_db!
|
104
|
+
restart_server!
|
105
|
+
|
106
|
+
This script looks much nicer. We moved deployment code into separate methods into a file we called
|
107
|
+
`deployment_steps.rb` and it looks like this:
|
108
|
+
|
109
|
+
def create_release_dir_and_upload!
|
110
|
+
# Create the release dir
|
111
|
+
$server.create_dir("#{$project_dir}/#$env/releases/#{$release_dir}")
|
112
|
+
# Upload to the remote dir
|
113
|
+
$server.upload("#{$local.current_path}/*", "#{$project_dir}/#$env/releases/#{$release_dir}/")
|
114
|
+
# cd to the release dir we've just created and uploaded, we'll do things inside it.
|
115
|
+
$server.cd("#{$project_dir}/#$env/releases/#{$release_dir}")
|
116
|
+
end
|
117
|
+
|
118
|
+
def create_symlinks!
|
119
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/database.yml", "config/")
|
120
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/secrets.yml", "config/")
|
121
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/cable.yml", "config/")
|
122
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/config/redis.yml", "config/")
|
123
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/log", "./")
|
124
|
+
$server.create_symlink("#{$project_dir}/#$env/shared/public/uploads", "public/")
|
125
|
+
end
|
126
|
+
|
127
|
+
def migrate_db!
|
128
|
+
# Migrate DB. Our migration script requires a login shell to work properly,
|
129
|
+
# so we instruct deplomat to run it using a login shell.
|
130
|
+
$server.execute("bin/migrate_db #{$env}", login_shell: true)
|
131
|
+
end
|
132
|
+
|
133
|
+
def restart_server!
|
134
|
+
# Restart the server
|
135
|
+
$server.execute("mkdir -p tmp")
|
136
|
+
$server.touch("tmp/restart.txt")
|
137
|
+
|
138
|
+
if $server.file_exists?("#{$project_dir}/#$env/current")
|
139
|
+
$server.mv("#{$project_dir}/#$env/current", "#{$project_dir}/#$env/previous")
|
140
|
+
end
|
141
|
+
$server.create_symlink("#{$project_dir}/#$env/releases/#{$release_dir}", "#{$project_dir}/#$env/current")
|
142
|
+
end
|
143
|
+
|
144
|
+
Notice, we were able to use the same `$server`, `$release_dir`, `$env` and some other variables inside that file precisely
|
145
|
+
because we made them global. While global vars are not great for large systems, deployment scripts such as this
|
146
|
+
one can take advantage of them without complicating things too much.
|
147
|
+
|
148
|
+
Deployment requisites
|
149
|
+
---------------------
|
150
|
+
Sometimes you need to run a piece of code while deploying the project, but you only need to run it once - that is,
|
151
|
+
on one deployment after which it will never be run again. Much like Ruby On Rails runs migrations once and then only runs
|
152
|
+
new migrations when necessary.
|
153
|
+
|
154
|
+
An example would be when you need to add something to the config file. While you can do it manually by logging into your server
|
155
|
+
and editing the config file, it's much more desirable to automate that process, because then you won't forget to do it.
|
156
|
+
|
157
|
+
Deplomat has a special feature called Deployment requisites, which allow you to
|
158
|
+
|
159
|
+
* Write special ruby scripts called "requisites", which have access to all the variables and features your script has access to;
|
160
|
+
* Enumarate the tasks that are being run on each deployment and keep track of them by using a special requisite counter (a file created on the server);
|
161
|
+
* Assign each task to be run before or after a particular deployment method in your script;
|
162
|
+
|
163
|
+
Let's see how we do that. The first step would be to replace method calls with a call to `#add_task` in your deployment script:
|
164
|
+
|
165
|
+
#!/usr/bin/env ruby
|
166
|
+
require 'rubygems'
|
167
|
+
require 'deplomat'
|
168
|
+
require 'deployment_steps' # << THIS IS WHERE WE PUT OUR METHODS THAT WE USE BELOW
|
169
|
+
|
170
|
+
$env = ARGV[0] || "staging"
|
171
|
+
$local = Deplomat::LocalNode.new(path: "/home/user/myproject")
|
172
|
+
$server = Deplomat::RemoteNode.new host: "myproject.com", port: 22, user: "deploy"
|
173
|
+
$branch = ARGV[1] || $env
|
174
|
+
$app_name = "myproject"
|
175
|
+
$project_dir = "/var/www/#{$app_name}"
|
176
|
+
$release_dir = Time.now.to_i
|
177
|
+
|
178
|
+
add_task :create_release_dir_and_upload!
|
179
|
+
add_task :create_symlinks!
|
180
|
+
add_task :migrate_db!
|
181
|
+
add_task :restart_server!
|
182
|
+
|
183
|
+
execute_tasks!
|
184
|
+
|
185
|
+
This script's behavior is equivalent to the one we had previously. We can make it shorter though by writing:
|
186
|
+
|
187
|
+
# --- top part with requires and global var settings ommited ---
|
188
|
+
|
189
|
+
add_task :create_release_dir_and_upload!, :create_symlinks!, :migrate_db!, :restart_server!
|
190
|
+
execute_tasks!
|
191
|
+
|
192
|
+
|
193
|
+
We'll add two lines of code that will read the current requisite number from the server and then load the requisites from
|
194
|
+
a local directory called `./deployment_requisites/`. You can change the defaults by passing an additional `path:` argument
|
195
|
+
to the `load_requisites!` method, but we're not going to do it here.
|
196
|
+
|
197
|
+
# --- top part with requires and global var settings ommited ---
|
198
|
+
|
199
|
+
# This method is kind of a callback, it's called automatically after every #before_task
|
200
|
+
# or #after_task call. We need to define it manually here,
|
201
|
+
# otherwise requisite number will be stuck on the same number
|
202
|
+
# and never updated.
|
203
|
+
def update_requisite_number!(n)
|
204
|
+
$server.update_requisite_number!(n)
|
205
|
+
end
|
206
|
+
|
207
|
+
# read current requisite number from the server
|
208
|
+
req_n = $server.current_requisite_number
|
209
|
+
|
210
|
+
# load requisites
|
211
|
+
load_requisites!(req_n)
|
212
|
+
|
213
|
+
add_task :create_release_dir_and_upload!, :create_symlinks!, :migrate_db!, :restart_server!
|
214
|
+
execute_tasks!
|
215
|
+
|
216
|
+
The `#execute_tasks!` method will now not only run your methods, but also run the requisites associated with each task. Now
|
217
|
+
you might ask, where's the actual code for the requisites? Let's create two files in the `./deployment_requisites/` dir on
|
218
|
+
your local machine:
|
219
|
+
|
220
|
+
# ./deployment_requisites/1_add_var1_to_config_file.rb
|
221
|
+
before_task(:migrate_db, 1) do
|
222
|
+
$server.execute("cat 'var1: true' >> config/secrets.yml")
|
223
|
+
end
|
224
|
+
|
225
|
+
# ./deployment_requisites/2_add_var2_to_config_file.rb
|
226
|
+
after_task(:create_release_dir_and_upload!, 2) do
|
227
|
+
$server.execute("cat 'var2: true' >> config/secrets.yml")
|
228
|
+
end
|
229
|
+
|
230
|
+
Notice two things here:
|
231
|
+
|
232
|
+
* Filenames start with a number. That's very important: each new requisite file should get a number that's larger than the previous number;
|
233
|
+
* When calling `before_task` or `after_task` the second argument should always be the number that equals that
|
234
|
+
consecutive number in the file name;
|
235
|
+
* You can have only one call to `before_task` or `after_task` per requisite file.
|
236
|
+
|
237
|
+
When you deploy, that's what's going to happen:
|
238
|
+
|
239
|
+
* Deplomat will check for the requisite number in the counter file at `"#{@current_path}/.deployment_requisites_counter"` (that's
|
240
|
+
the default location, can be changed by passing an additional argument to `Deplomat::Node#current_requisite_number` and
|
241
|
+
`Deplomat::Node#update_requisite_number`, see sources);
|
242
|
+
|
243
|
+
* Use that fetched counter number to run only requisites with the numbers that are higher;
|
244
|
+
|
245
|
+
* Update that number upon each requisite script completion (so if there's an error somehwere, we're still left at the exact requisite number
|
246
|
+
that ran successfully). The update is done by calling the `#update_requisite_number!` we defined in our deployment script.
|
247
|
+
|
248
|
+
|
249
|
+
Where can I find the list of all methods?
|
250
|
+
----------------------------------------
|
251
|
+
For now, because this is a relatively small library, you're better off browsing the sources, specifically:
|
252
|
+
|
253
|
+
* [Deplomat::Node](lib/deplomat/node.rb)
|
254
|
+
* [Deplomat::LocalNode](lib/deplomat/local_node.rb)
|
255
|
+
* [Deplomat::RemoteNode](lib/deplomat/remotenode.rb)
|
256
|
+
|
257
|
+
TODO
|
258
|
+
-----
|
259
|
+
|
260
|
+
* `Deplomat::Node` methods to read and update .yml files easily (usecase: update config files in requisite scripts).
|
261
|
+
* Include `git-archive-all` script
|
262
|
+
* Include scripts for default Ruby On Rails deployments (perhaps as a separate repo)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/deplomat.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: deplomat 0.
|
5
|
+
# stub: deplomat 0.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "deplomat".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Roman Snitko".freeze]
|
14
|
-
s.date = "2018-
|
14
|
+
s.date = "2018-12-17"
|
15
15
|
s.description = "Stack agnostic deployment system that uses bash and ssh commands".freeze
|
16
16
|
s.email = "roman.snitko@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -35,7 +35,11 @@ Gem::Specification.new do |s|
|
|
35
35
|
"lib/deplomat/local_node.rb",
|
36
36
|
"lib/deplomat/node.rb",
|
37
37
|
"lib/deplomat/remote_node.rb",
|
38
|
+
"spec/deployment_requisites/1_req.rb",
|
39
|
+
"spec/deployment_requisites/2_req.rb",
|
40
|
+
"spec/deployment_requisites/not_a_task.rb",
|
38
41
|
"spec/directives_spec.rb",
|
42
|
+
"spec/fixtures/.deployment_requisites_counter",
|
39
43
|
"spec/fixtures/cleaning/.keep",
|
40
44
|
"spec/fixtures/dir1/file1",
|
41
45
|
"spec/fixtures/dir1/file2",
|
data/lib/deplomat/directives.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
$partials
|
1
|
+
$partials = {}
|
2
|
+
$deplomat_tasks = []
|
3
|
+
$deplomat_tasks_callbacks = {}
|
2
4
|
|
3
5
|
def execute_in(env:)
|
4
6
|
yield if $env == env
|
@@ -23,3 +25,58 @@ def print_to_terminal(message, color: nil, newline: true)
|
|
23
25
|
message = message.send(color) if color
|
24
26
|
$stdout.print message
|
25
27
|
end
|
28
|
+
|
29
|
+
# This wrapper allows us to insert external before/after
|
30
|
+
# tasks into every method executed by deplomat.
|
31
|
+
# Very useful for the one-time-tasks implementation.
|
32
|
+
def add_task(*tasks)
|
33
|
+
$deplomat_tasks += tasks
|
34
|
+
end
|
35
|
+
alias :add_tasks :add_task
|
36
|
+
|
37
|
+
def before_task(task_name, requisite_number, &block)
|
38
|
+
$deplomat_tasks_callbacks[task_name] ||= {}
|
39
|
+
$deplomat_tasks_callbacks[task_name][:before] ||= []
|
40
|
+
$deplomat_tasks_callbacks[task_name][:before] << lambda { block.call; update_requisite_number!(requisite_number) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def after_task(task_name, requisite_number, &block)
|
44
|
+
$deplomat_tasks_callbacks[task_name] ||= {}
|
45
|
+
$deplomat_tasks_callbacks[task_name][:after] ||= []
|
46
|
+
$deplomat_tasks_callbacks[task_name][:after] << lambda { block.call; update_requisite_number!(requisite_number) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute_tasks!
|
50
|
+
$deplomat_tasks.each do |t|
|
51
|
+
$deplomat_tasks_callbacks[t][:before].each { |block| block.call } if $deplomat_tasks_callbacks[t] && $deplomat_tasks_callbacks[t][:before]
|
52
|
+
self.send(t)
|
53
|
+
$deplomat_tasks_callbacks[t][:after].each { |block| block.call } if $deplomat_tasks_callbacks[t] && $deplomat_tasks_callbacks[t][:after]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# This should be redefined in the deployment script.
|
58
|
+
def update_requisite_number!(n)
|
59
|
+
# call Node#update_requisite_number! here
|
60
|
+
# or do something else, more complicated.
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_requisites!(counter, requisites_path: "./deployment_requisites")
|
64
|
+
|
65
|
+
files = Dir["#{requisites_path}/*.rb"].map do |fn|
|
66
|
+
number = fn.split("/").last.match(/\A\d+_/).to_a.first&.chomp("_")
|
67
|
+
number ? [number, fn] : nil
|
68
|
+
end
|
69
|
+
.compact # ignore files that don't start with a number
|
70
|
+
.sort { |x,y| x.first <=> y.first } # Sort files according to the number in front of their names
|
71
|
+
.map { |fn| fn.last } # lose the folded Array we used to store the consecutive requisite number
|
72
|
+
.slice(counter..-1) # lose files whose consecutive requisite number is below or equals the counter
|
73
|
+
|
74
|
+
log = []
|
75
|
+
files.each do |fn|
|
76
|
+
log << "Loading requisite: #{fn.split("/").last }"
|
77
|
+
require fn
|
78
|
+
end
|
79
|
+
|
80
|
+
return { counter: counter + files.length, log: log }
|
81
|
+
|
82
|
+
end
|
data/lib/deplomat/exceptions.rb
CHANGED
data/lib/deplomat/node.rb
CHANGED
@@ -125,6 +125,25 @@ module Deplomat
|
|
125
125
|
execute("git checkout #{target}")
|
126
126
|
end
|
127
127
|
|
128
|
+
def update_requisite_number!(n, counter_file_path: "#{@current_path}/.deployment_requisites_counter")
|
129
|
+
current_number = current_requisite_number(counter_file_path)
|
130
|
+
if n <= current_number
|
131
|
+
raise Deplomat::RequisitesNumberError, message: "New requisite number (#{n}) is below or equals the current one (#{current_number}). " +
|
132
|
+
"Something must have gone wrong."
|
133
|
+
else
|
134
|
+
File.open(counter_file_path, "w") { |f| f.puts n }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def current_requisite_number(fn="#{@current_path}/.deployment_requisites_counter")
|
139
|
+
if file_exists?(fn)
|
140
|
+
return File.readlines(fn).first.to_i
|
141
|
+
else
|
142
|
+
raise Deplomat::RequisitesNumberError, message: "Requisite counter file `#{@current_path}/.deployment_requisites_counter` doesn't exist. " +
|
143
|
+
"Please create it manually and symlink it in the deployment script if necessary."
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
128
147
|
def clean(path: @current_path, except: [], leave: [0, :last])
|
129
148
|
# Gets us all entries sorted by date, most recent ones first
|
130
149
|
entries_by_date = execute("ls -t", path, log_command: false)[:out].split("\n")
|
File without changes
|
data/spec/directives_spec.rb
CHANGED
@@ -2,6 +2,10 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "directives" do
|
4
4
|
|
5
|
+
before(:each) do
|
6
|
+
$deplomat_tasks = []
|
7
|
+
end
|
8
|
+
|
5
9
|
it "executes arbitrary ruby code in a particular environment" do
|
6
10
|
$env = 'staging'
|
7
11
|
expect(self).to receive(:hello).exactly(1).times
|
@@ -20,6 +24,41 @@ describe "directives" do
|
|
20
24
|
execute_partial "hello_partial", x: 'hello'
|
21
25
|
end
|
22
26
|
|
23
|
-
it "
|
27
|
+
it "executes tasks consecutively, running before and after callbacks" do
|
28
|
+
|
29
|
+
$out = ""
|
30
|
+
def task1; $out += "task1"; end
|
31
|
+
def task2; $out += "task2"; end
|
32
|
+
def task3; $out += ",task3"; end
|
33
|
+
|
34
|
+
before_task(:task1, 1) { $out += "before_task1:" }
|
35
|
+
after_task(:task1, 2) { $out += ":after_task1," }
|
36
|
+
before_task(:task2, 3) { $out += "before_task2:" }
|
37
|
+
after_task(:task2, 4) { $out += ":after_task2" }
|
38
|
+
before_task(:task4, 5) { $out += "before_task4:" }
|
39
|
+
after_task(:task4, 6) { $out += ":after_task4" }
|
40
|
+
|
41
|
+
add_task :task1, :task2, :task3
|
42
|
+
|
43
|
+
execute_tasks!
|
44
|
+
expect($out).to eq("before_task1:task1:after_task1,before_task2:task2:after_task2,task3")
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
it "loads and runs requisites" do
|
49
|
+
|
50
|
+
def task1;end
|
51
|
+
def task2;end
|
52
|
+
|
53
|
+
add_task :task1, :task2
|
54
|
+
|
55
|
+
result = load_requisites!(1, requisites_path: "#{File.expand_path(File.dirname(__FILE__))}/deployment_requisites")
|
56
|
+
expect(result[:counter]).to eq(2)
|
57
|
+
expect(result[:log]).to eq(["Loading requisite: 2_req.rb"])
|
58
|
+
execute_tasks!
|
59
|
+
expect($deployment_requisites_test_file1).to be_nil
|
60
|
+
expect($deployment_requisites_test_file2).to be_truthy
|
61
|
+
|
62
|
+
end
|
24
63
|
|
25
64
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
1
|
data/spec/node_spec.rb
CHANGED
@@ -44,6 +44,21 @@ describe Deplomat::Node do
|
|
44
44
|
@node.execute("ls #{@fixtures_dir}/dir1", message: ["hello", "bye"])
|
45
45
|
end
|
46
46
|
|
47
|
+
it "updates requisite counter" do
|
48
|
+
|
49
|
+
requisites_counter_fn = "#{Dir.pwd}/spec/.deployment_requisites_counter"
|
50
|
+
@node.cd Dir.pwd + "/spec"
|
51
|
+
|
52
|
+
File.open(requisites_counter_fn, "w") { |f| f.puts 1 }
|
53
|
+
@node.update_requisite_number!(2)
|
54
|
+
expect(File.readlines(requisites_counter_fn).first.to_i).to eq(2)
|
55
|
+
|
56
|
+
expect(-> { @node.update_requisite_number!(1) }).to raise_exception(Deplomat::RequisitesNumberError)
|
57
|
+
|
58
|
+
File.unlink(requisites_counter_fn)
|
59
|
+
expect(-> { @node.update_requisite_number!(3) }).to raise_exception(Deplomat::RequisitesNumberError)
|
60
|
+
end
|
61
|
+
|
47
62
|
end
|
48
63
|
|
49
64
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deplomat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Snitko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sys-proctable
|
@@ -104,7 +104,11 @@ files:
|
|
104
104
|
- lib/deplomat/local_node.rb
|
105
105
|
- lib/deplomat/node.rb
|
106
106
|
- lib/deplomat/remote_node.rb
|
107
|
+
- spec/deployment_requisites/1_req.rb
|
108
|
+
- spec/deployment_requisites/2_req.rb
|
109
|
+
- spec/deployment_requisites/not_a_task.rb
|
107
110
|
- spec/directives_spec.rb
|
111
|
+
- spec/fixtures/.deployment_requisites_counter
|
108
112
|
- spec/fixtures/cleaning/.keep
|
109
113
|
- spec/fixtures/dir1/file1
|
110
114
|
- spec/fixtures/dir1/file2
|