deplomat 0.1.13 → 0.2.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/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
|