fulmar 1.10.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +11 -4
  4. data/README.md +87 -102
  5. data/fulmar.gemspec +2 -2
  6. data/lib/fulmar/domain/{service/application_service.rb → model/application.rb} +16 -2
  7. data/lib/fulmar/domain/model/configuration.rb +185 -0
  8. data/lib/fulmar/domain/service/config_test_service.rb +32 -114
  9. data/lib/fulmar/domain/service/config_tests/hosts.rb +23 -0
  10. data/lib/fulmar/domain/service/config_tests/mariadb.rb +19 -0
  11. data/lib/fulmar/domain/service/config_tests/paths.rb +15 -0
  12. data/lib/fulmar/domain/service/config_tests/project.rb +5 -0
  13. data/lib/fulmar/domain/service/configuration_service.rb +13 -131
  14. data/lib/fulmar/domain/service/file_sync_service.rb +7 -7
  15. data/lib/fulmar/domain/service/helper/common_helper.rb +18 -32
  16. data/lib/fulmar/domain/service/plugin_service.rb +60 -0
  17. data/lib/fulmar/domain/service/{config_rendering_service.rb → template_rendering_service.rb} +3 -3
  18. data/lib/fulmar/domain/task/configuration.rake +9 -7
  19. data/lib/fulmar/domain/task/console.rake +3 -3
  20. data/lib/fulmar/domain/task/environment.rake +5 -12
  21. data/lib/fulmar/domain/task/initialization/base.rake +3 -9
  22. data/lib/fulmar/domain/task/versions.rake +8 -12
  23. data/lib/fulmar/infrastructure/default_files/Fulmar/project.config.yml +26 -0
  24. data/lib/fulmar/infrastructure/default_files/Fulmarfile +22 -0
  25. data/lib/fulmar/infrastructure/{service → model}/transfer/base.rb +4 -4
  26. data/lib/fulmar/infrastructure/{service → model}/transfer/rsync.rb +4 -2
  27. data/lib/fulmar/infrastructure/{service → model}/transfer/rsync_with_versions.rb +18 -20
  28. data/lib/fulmar/infrastructure/{service → model}/transfer/tar.rb +2 -2
  29. data/lib/fulmar/infrastructure/service/copy_service.rb +2 -2
  30. data/lib/fulmar/infrastructure/service/ssh_config_service.rb +8 -21
  31. data/lib/fulmar/service/bootstrap_service.rb +1 -5
  32. data/lib/fulmar/task_manager.rb +2 -8
  33. data/lib/fulmar/version.rb +1 -1
  34. data/spec/lib/fulmar/model/configuration_spec.rb +193 -0
  35. data/spec/lib/fulmar/service/plugin_service_spec.rb +29 -0
  36. metadata +29 -34
  37. data/lib/fulmar/domain/service/helper/database_helper.rb +0 -14
  38. data/lib/fulmar/domain/service/helper/flow_helper.rb +0 -16
  39. data/lib/fulmar/domain/service/helper/vhost_helper.rb +0 -21
  40. data/lib/fulmar/domain/task/database_sync.rake +0 -63
  41. data/lib/fulmar/infrastructure/service/composer_service.rb +0 -22
  42. data/lib/fulmar/infrastructure/service/database/database_service.rb +0 -118
  43. data/lib/fulmar/infrastructure/service/flow_service.rb +0 -47
  44. data/lib/fulmar/infrastructure/service/shell_service.rb +0 -10
  45. data/lib/fulmar/service/logger_service.rb +0 -7
  46. data/test/rsync_with_versions.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6e4cd1b4895e08c5b2548ea7680e13071e5a1d7
4
- data.tar.gz: 3f141c76b47aab32bd21a40a4629d79df2c92c95
3
+ metadata.gz: e8d6270f2a2b407f32d9419cd4bb55c228138b8e
4
+ data.tar.gz: 2c820de490c174e1c7a0df5db92434fe1a3f3302
5
5
  SHA512:
6
- metadata.gz: d1bedf9ff9ee0780bfe6056dc9b295d43aa4d13d32f114e052090e059860c1abb394088efc9d4a7ac1c9fc617b5db3ca885c72e513d3e827774d35575fe901b5
7
- data.tar.gz: 8ea30f40735b20aa8b0d18256c6c1467d8060fc5d0227f756b21dd75545d8e00128c69c135f83a22376530b146dbe9b927e92d720672b9834488aad0c38a8bb8
6
+ metadata.gz: 308c77aa59ef61e622a7988c5bbafb86b76694408d5d6b112e311813a9175c34260aff4954449f358e3722de481ff3570ac79c3e21d6c3ef904be72fbab9ada9
7
+ data.tar.gz: ae114e84ce4907f2feefe402f1409cbbfe76497c3e47dc15dbe676964df877150bf22f14dd7c763963fe4be954e9997e571c32979a7457c7d067a0ceb7255966
data/.gitignore CHANGED
@@ -14,7 +14,7 @@
14
14
  mkmf.log
15
15
  /.idea/
16
16
  *.gem
17
- /Vagrantfile
18
17
  /.vagrant
18
+ /Vagrantfile
19
19
  /vendor
20
20
  /test/deployment
data/.travis.yml CHANGED
@@ -1,11 +1,18 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.0
4
- - 2.1
5
- - 2.2
3
+ - 2.2.2
4
+ - 2.3.1
5
+ - 2.4.0
6
6
  - ruby-head
7
- before_install: gem install bundler -v 1.10.6
8
7
  matrix:
9
8
  fast_finish: true
10
9
  allow_failures:
11
10
  - rvm: ruby-head
11
+
12
+ before_script:
13
+ - bundle update
14
+
15
+ script:
16
+ - git config --global user.email "you@example.com"
17
+ - git config --global user.name "Whatever Name"
18
+ - bundle exec rspec spec
data/README.md CHANGED
@@ -12,28 +12,12 @@ the cache and the publish via a symlink. This avoids an inconsistent state on th
12
12
  machine and allows a quick revert to the old version, as long as other dependencies are
13
13
  compatible (i.e. database).
14
14
 
15
- It has (yet limited) support for MySQL / MariaDB and git. Remote databases can be accessed
16
- through an ssh tunnel.
17
-
18
- ## Warning
19
-
20
- Version 1.10.0 of Fulmar removes some features that we (CORE4) rarely used and which
21
- caused the deployments to slow down the gems needed to be compiled against the
22
- installed libraries. If you need support to query databases (more than just dumps)
23
- or use dependencies, you will need to explicitly specify version `~> 1.9.0`.
24
- Fulmar 2.0 will support these features via plugins.
15
+ It has (yet limited) support for MySQL / MariaDB and git.
25
16
 
26
17
  ## Prerequisites
27
18
 
28
- Fulmar 1.9 currently requires the [mysql2](https://github.com/brianmario/mysql2) gem which
29
- requires the mysql header files. So on a linux system, you want to install
30
- libmariadbclient-dev/libmysqlclient-dev or similar.
31
-
32
- You also need cmake and pkg-config to build the dependencies.
33
-
34
- - OSX: brew install mariadb cmake pkg-config
35
- - Ubuntu: apt-get install libmariadb-client-lgpl-dev build-essential cmake
36
- - Ubuntu (pre-xenial): apt-get install libmariadbclient-dev build-essential cmake
19
+ Fulmar 2.0 runs with Ruby >= 2.2.2. Plugins like [mariadb](https://github.com/CORE4/fulmar-plugin-mariadb) might have
20
+ additional requirements.
37
21
 
38
22
  ## Installation
39
23
 
@@ -57,20 +41,24 @@ Fulmar works like Rake. In fact it is just a collection of services on top of Ra
57
41
 
58
42
  ## A simple start
59
43
 
60
- Start by creating a file named "Fulmarfile" in your project root. Then add a Folder "Fulmar" with a project.config.yml in it. The name of the config file doesn't matter, it just needs to end with .config.yml. All \*.config.yml in the Fulmar directory are merged so you can split you configuration into
44
+ Start by creating a file named "Fulmarfile" in your project root. Then add a Folder "Fulmar" with a project.config.yml
45
+ in it. The name of the config file doesn't matter, it just needs to end with .config.yml. All \*.config.yml in the
46
+ Fulmar directory are merged so you can split you configuration into
61
47
  several files.
62
48
 
63
49
  $ touch Fulmarfile
64
50
  $ mkdir Fulmar
65
51
  $ touch Fulmar/project.config.yml
66
52
 
67
- You can now test your configuration.
53
+ You can now now test your configuration.
68
54
 
69
55
  $ fulmar test:config
70
56
 
71
- Everything should be fine. With `fulmar -T` you can get a quick overview on the available tasks. That is probably just one, the fulmar console. We'll come back to this later.
57
+ Everything should be fine. With `fulmar -T` you can get a quick overview on the available tasks. That is probably just
58
+ one, the fulmar console. We'll come back to this later.
72
59
 
73
- Now let's start with a first configuration. You might want to have a server ready which you can access via ssh and public key authentication. Add this to you configuration file (Fulmar/project.config.yml):
60
+ Now let's start with a first configuration. You might want to have a server ready which you can access via ssh and
61
+ public key authentication. Add this to you configuration file (Fulmar/project.config.yml):
74
62
 
75
63
  ```yaml
76
64
  environments:
@@ -85,15 +73,25 @@ environments:
85
73
  delete: false
86
74
  ```
87
75
 
88
- Now you can run a second test: `fulmar test:hosts`. This will test the ssh connection to your remote machine. You can add a username to the ssh configuration in the files section (below the hostname). However, if you need a finer grained host configuration, you should use your [ssh config file](http://www.cyberciti.biz/faq/create-ssh-config-file-on-linux-unix/).
76
+ Now you can run a second test: `fulmar test:hosts`. This will test the ssh connection to your remote machine. You can
77
+ add a username to the ssh configuration in the files section (below the hostname). However, if you need a finer
78
+ grained host configuration, you should use your
79
+ [ssh config file](http://www.cyberciti.biz/faq/create-ssh-config-file-on-linux-unix/).
89
80
 
90
- Most fulmar tasks need to run within a configured environment. Within the configuration, environments are split into targets. So actually, a task works within a target. In our example, we set up an environment named 'my_server' with a target named 'files'. You might want to use one environment for your development server, one for a preview server and one for the production machine. Within each environment you might have different hosts for the file and the database, so each environment can be split up into multiple targets (e.g. 'files' and 'database').
81
+ Most fulmar tasks need to run within a configured environment. Within the configuration, environments are split into
82
+ targets. So actually, a task works within a target. In our example, we set up an environment named 'my_server' with
83
+ a target named 'files'. You might want to use one environment for your development server, one for a preview server
84
+ and one for the production machine. Within each environment you might have different hosts for the file and the
85
+ database, so each environment can be split up into multiple targets (e.g. 'files' and 'database').
91
86
 
92
- There is one special environment 'all' which contains settings you want to use in all targets of all environements. These globally configured settings are overwritten with the specific ones from a target if they exist. Useful examples for this are 'local_path' and 'debug'.
87
+ There is one special environment 'all' which contains settings you want to use in all targets of all environements.
88
+ These globally configured settings are overwritten with the specific ones from a target if they exist. Useful examples
89
+ for this are 'local_path' and 'debug'.
93
90
 
94
91
  ### Tasks
95
92
 
96
- As you can see (`fulmar -T`), you cannot do anything more useful now. So it is time to write a first task. If you are familiar with rake, this should not be a problem.
93
+ As you can see (`fulmar -T`), you cannot do anything more useful now. So it is time to write a first task. If you are
94
+ familiar with rake, this should not be a problem.
97
95
 
98
96
  ```ruby
99
97
  namespace :deploy do
@@ -105,14 +103,17 @@ namespace :deploy do
105
103
  end
106
104
  ```
107
105
 
108
- This creates a task 'test' within the namespace 'deploy', which means you can access the task by running `fulmar deploy:test`. The task will load the configuration for 'myserver/files' and start a file sync (via rsync) to your remote host.
106
+ This creates a task 'test' within the namespace 'deploy', which means you can access the task by running
107
+ `fulmar deploy:test`. The task will load the configuration for 'myserver/files' and start a file sync (via rsync) to
108
+ your remote host.
109
109
 
110
110
  If you log in to your remote machine, you should now see the Fulmar directory and the Fulmarfile in /tmp.
111
111
  ```bash
112
112
  ssh my-ssh-server "ls -l /tmp | grep Fulmar"
113
113
  ```
114
114
 
115
- This task can be even shorter. Fulmar creates some helper tasks automatically from your configuration. Just like in rake, you can list them via `fulmar -T -A`:
115
+ This task can be even shorter. Fulmar creates some helper tasks automatically from your configuration. Just like in
116
+ rake, you can list them via `fulmar -T -A`:
116
117
  ```
117
118
  fulmar console # Open an interactive terminal within fulmar
118
119
  fulmar deploy:test # Deploy to test system
@@ -122,7 +123,8 @@ fulmar test:config #
122
123
  fulmar test:hosts #
123
124
  ```
124
125
 
125
- Fulmar creates task for every environment/target with this scheme. So you can just set a dependency to these tasks to load the configuration. Your task now only needs one line:
126
+ Fulmar creates task for every environment/target with this scheme. So you can just set a dependency to these tasks to
127
+ load the configuration. Your task now only needs one line:
126
128
 
127
129
  ```ruby
128
130
  namespace :deploy do
@@ -139,11 +141,15 @@ task :test => ['environment:my_server:files', 'prepare_files', 'do_some_more'] d
139
141
  end
140
142
  ```
141
143
 
142
- If you call one task from another, your configuration will be given to the subtask. If you change the target in the subtask it will be reverted when returning to your main task unless you did not set any configuration in the main task. This means the following:
143
- - You can use the dependency to a 'environment:' task to set the target initially (as it will be the first subtask to run)
144
+ If you call one task from another, your configuration will be given to the subtask. If you change the target in the
145
+ subtask it will be reverted when returning to your main task unless you did not set any configuration in the main task.
146
+ This means the following:
147
+ - You can use the dependency to a 'environment:' task to set the target initially (as it will be the first subtask to
148
+ run)
144
149
  - You can call a subtask and and you don't have to worry it might change your target.
145
150
 
146
- *The configuration object is a singleton. So if you change the actual configuration of a target, it will affect other tasks*
151
+ *The configuration object is a singleton. So if you change the actual configuration of a target, it will affect other
152
+ tasks*
147
153
 
148
154
  ## Features
149
155
 
@@ -151,7 +157,8 @@ So now you can deploy the Fulmarfiles itself which is nice. So what else can you
151
157
 
152
158
  ### Shell
153
159
 
154
- Very often you need to call other programs or script from a shell to compile your assets or clear the cache. Within a task, you can access the local and remote shell:
160
+ Very often you need to call other programs or script from a shell to compile your assets or clear the cache. Within a
161
+ task, you can access the local and remote shell:
155
162
 
156
163
  ```ruby
157
164
  task :tutorial => 'environment:my_server:files' do
@@ -161,7 +168,8 @@ task :tutorial => 'environment:my_server:files' do
161
168
  end
162
169
  ```
163
170
 
164
- You can run multiple commands. If one fails, everything stops immediately. This is intended to stop the deployment of broken files.
171
+ You can run multiple commands. If one fails, everything stops immediately. This is intended to stop the deployment of
172
+ broken files.
165
173
 
166
174
  ```ruby
167
175
  task :tutorial => 'environment:my_server:files' do
@@ -188,7 +196,8 @@ end
188
196
 
189
197
  ### File synchronisation
190
198
 
191
- At the moment, there are two ways to synchronize your files to a remote host. The basic rsync and "rsync with versions". You already know how to set up the simple file sync. You can add a few options, if you like:
199
+ At the moment, there are two ways to synchronize your files to a remote host. The basic rsync and "rsync with versions".
200
+ You already know how to set up the simple file sync. You can add a few options, if you like:
192
201
 
193
202
  ```yaml
194
203
  environments:
@@ -210,16 +219,20 @@ environments:
210
219
  ```
211
220
 
212
221
  These are:
213
- - **delete**: true/false, Should additional files be deleted from the remote host? The default is "true", as you don't want to have legacy files opening security holes on your server.
222
+ - **delete**: true/false, Should additional files be deleted from the remote host? The default is "true", as you don't
223
+ want to have legacy files opening security holes on your server.
214
224
  - **exclude**: regexp, which works with "--exclude" from rsync. Usually, you should prefer the next option
215
- - **exclude_file**: Filename of the exclude file relative to "local_path". If you have a file called .rsyncignore in "local_path" this will be used automatically.
225
+ - **exclude_file**: Filename of the exclude file relative to "local_path". If you have a file called .rsyncignore in
226
+ "local_path" this will be used automatically.
216
227
  - **chown**: chown files on remote machine to this user
217
228
  - **chmod**: change mode of files on remote machines
218
- - **direction**: up/down, Should the files be uploaded or downloaded? The latter is useful if you want to download images created by a cms on the remote machine.
229
+ - **direction**: up/down, Should the files be uploaded or downloaded? The latter is useful if you want to download
230
+ images created by a cms on the remote machine.
219
231
 
220
232
  ### rsync with versions
221
233
 
222
- When deploying to a production machine, you want your minimize the downtime for the website. Fulmar can help by syncing different versions which are symlinked after they are set up.
234
+ When deploying to a production machine, you want your minimize the downtime for the website. Fulmar can help by syncing
235
+ different versions which are symlinked after they are set up.
223
236
 
224
237
  ```yaml
225
238
  environments:
@@ -238,13 +251,20 @@ environments:
238
251
  #shared_dir: shared
239
252
  ```
240
253
 
241
- You do no necessarily need to set "limit_releases" or "shared". "temp_dir", "releases_dir" and "shared_dir" are relative to the remote_path and usually do not need to be changed.
254
+ You do no necessarily need to set "limit_releases" or "shared". "temp_dir", "releases_dir" and "shared_dir" are
255
+ relative to the remote_path and usually do not need to be changed.
242
256
 
243
257
  So what does this all mean?
244
258
 
245
- If you still have your deploy:test task you can now deploy a first version of your files. Probably create the "data" dir first and put a file in to see what happens. Fulmar will sync you project into a temporary directory ("temp_dir") and then copy it the the releases directory with the current timestamp as a directory name. So in this case, a directory like "/tmp/releases/2015-04-27_123456" will be created. Fulmar will then look of shared/data exists and if not, copy it from the releases directory. Shared directories in releases will be deleted an symlinked to shared. So you want to list all directories which are filled by you web application (images uploaded from the cms, etc).
259
+ If you still have your deploy:test task you can now deploy a first version of your files. Probably create the "data"
260
+ dir first and put a file in to see what happens. Fulmar will sync you project into a temporary directory ("temp_dir")
261
+ and then copy it the the releases directory with the current timestamp as a directory name. So in this case, a directory
262
+ like "/tmp/releases/2015-04-27_123456" will be created. Fulmar will then look of shared/data exists and if not, copy it
263
+ from the releases directory. Shared directories in releases will be deleted an symlinked to shared. So you want to
264
+ list all directories which are filled by you web application (images uploaded from the cms, etc).
246
265
 
247
- Then you can build up the cache or whatever needs to be done within that directory before putting it live. This last step is not yet covered in our little deployment task. So:
266
+ Then you can build up the cache or whatever needs to be done within that directory before putting it live. This last
267
+ step is not yet covered in our little deployment task. So:
248
268
 
249
269
  ```ruby
250
270
  namespace :deploy do
@@ -260,75 +280,26 @@ namespace :deploy do
260
280
  end
261
281
  ```
262
282
 
263
- The transfer() method of the file_sync service returns the created release dir and you can do what needs to be done before calling file_sync.publish to create the symlink (current => releases/2015-04-27_123456).
264
-
265
- As you probably figured out, your webserver uses the "current" symlink to get the base directory of your web application. Here, you would point it to /tmp/current/public or something like that.
266
-
267
- ### Database access
268
-
269
- Within a task you can access and update databases (mariadb/mysql at the time of writing). Remote databases do not need to be accessible directly since fulmar uses an ssh tunnel to connect to the host first. So the database host is often just "localhost" (the default value). You can specify a different host if you database server is on another host, which is the case on most simple web spaces.
270
-
271
- The field "maria:database" is required for type "maria". The other fields are optional, though you probably need the fields "user" and "password". Below you can see the default values for the optional fields.
272
-
273
- ```yaml
274
- environments:
275
- staging:
276
- database:
277
- host: project-staging
278
- type: maria
279
- maria:
280
- database: db_name
281
- user: root
282
- password:
283
- port: 3306
284
- host: localhost
285
- encoding: utf-8
286
- ```
287
-
288
- ```ruby
289
- require 'pp'
290
-
291
- task :database => 'environment:staging:database' do
292
- database.create 'test_db'
293
- database.clear # deletes all tables within the database
294
- remote_file_name = database.dump # dumps the database to the returned file
295
- database.load_dump(remote_file_name) # loads an sql dump
296
- local_filename = database.download_dump # downloads an sql dump to your machine
297
- end
298
- ```
283
+ The transfer() method of the file_sync service returns the created release dir and you can do what needs to be done
284
+ before calling file_sync.publish to create the symlink (current => releases/2015-04-27_123456).
299
285
 
300
- You can query the database like this:
301
-
302
- ```ruby
303
- results = database.query 'SELECT name, email FROM users'
304
- results.each do |row|
305
- puts "#{row['name']} <#{row['email']}>"
306
- end
307
- ```
308
-
309
- You can use all features of the mysql2 gem via `database.client`.
310
-
311
- If you configured more than one database on different environments, fulmar will
312
- create task to sync these databases via mysql_dump. This allows you to update a
313
- staging or preview database with the data from the production system.
314
-
315
- ```
316
- fulmar database:update:preview:from_live
317
- ```
318
-
319
- The task to copy data *to* the live database is hidden (it has no description).
286
+ As you probably figured out, your webserver uses the "current" symlink to get the base directory of your web
287
+ application. Here, you would point it to /tmp/current/public or something like that.
320
288
 
321
289
  ### Configuration templates
322
290
 
323
- Sometimes you need different versions of files on your different environments. An exmaple might be the .htaccess file. You can use the [Ruby ERB templating engine](http://www.stuartellis.eu/articles/erb/) to generate the different versions. The configuration of your environment are available via the `@config` variable.
291
+ Sometimes you need different versions of files on your different environments. An exmaple might be the .htaccess file.
292
+ You can use the [Ruby ERB templating engine](http://www.stuartellis.eu/articles/erb/) to generate the different
293
+ versions. The configuration of your environment are available via the `@config` variable.
324
294
 
325
- Templates need to be named with the schema `<original_filename>.erb`. All files you want to render need to be listed in the configuration:
295
+ Templates need to be named with the schema `<original_filename>.erb`. All files you want to render need to be listed in
296
+ the configuration:
326
297
 
327
298
  ```yaml
328
299
  environments:
329
300
  staging:
330
301
  files:
331
- config_templates:
302
+ templates:
332
303
  - .htaccess.erb
333
304
  application_environment: Production/Live
334
305
  ```
@@ -338,3 +309,17 @@ Then add the variable to your template:
338
309
  ```
339
310
  SetEnv APPLICATION_ENVIRONMENT "<%= @config['application_environment'] %>"
340
311
  ```
312
+
313
+ ## Plugins
314
+
315
+ Fulmar can be extended with plugins.
316
+
317
+ ```yaml
318
+ plugins:
319
+ mariadb:
320
+ # Put mariadb plugin configuration here
321
+ git:
322
+ # Put git plugin configuration here
323
+ ```
324
+
325
+ If you want to add your own plugin, have a look at the [example plugin](https://github.com/CORE4/fulmar-plugin-example).
data/fulmar.gemspec CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.7'
22
22
 
23
- spec.add_runtime_dependency 'rake', '~>10'
23
+ spec.add_runtime_dependency 'rake', '~>11'
24
24
  spec.add_runtime_dependency 'fulmar-shell', '~>1', '>=1.7.0'
25
- spec.add_runtime_dependency 'ruby_wings', '~>0.1', '>=0.1.1'
26
25
  spec.add_runtime_dependency 'colorize', '~>0'
26
+ spec.add_runtime_dependency 'activesupport', '~> 5.0'
27
27
  end
@@ -1,10 +1,11 @@
1
1
  require 'rake'
2
+ require 'fulmar/domain/service/plugin_service'
2
3
 
3
4
  module Fulmar
4
5
  module Domain
5
6
  module Service
6
7
  # The main application which extends rake
7
- class ApplicationService < Rake::Application
8
+ class Application < Rake::Application
8
9
  def initialize
9
10
  super
10
11
  @rakefiles = %w(fulmarfile Fulmarfile fulmarfile.rb Fulmarfile.rb)
@@ -33,6 +34,9 @@ module Fulmar
33
34
  glob("#{fulmar_task_dir}/initialization/*.rake") do |name|
34
35
  Rake.load_rakefile name
35
36
  end
37
+ Fulmar::Domain::Service::PluginService.instance.rake_files.each do |name|
38
+ Rake.load_rakefile name
39
+ end
36
40
  super
37
41
  end
38
42
 
@@ -62,10 +66,20 @@ module Fulmar
62
66
  '-V',
63
67
  'Display the program version.',
64
68
  lambda do |_value|
65
- puts "fulmar #{Fulmar::VERSION} (using rake, version #{RAKEVERSION})"
69
+ puts "fulmar #{Fulmar::VERSION} (using rake, version #{Rake::VERSION})"
66
70
  exit
67
71
  end
68
72
  ]
73
+ options << [
74
+ '--debug',
75
+ nil,
76
+ 'Run in debug mode.',
77
+ lambda do |_value|
78
+ configuration = Fulmar::Domain::Service::ConfigurationService.instance
79
+ configuration.debug = true
80
+ end
81
+ ]
82
+ options
69
83
  end
70
84
  end
71
85
  end
@@ -0,0 +1,185 @@
1
+ require 'yaml'
2
+ require 'fulmar/domain/model/project'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/hash/deep_merge'
5
+ require 'active_support/hash_with_indifferent_access'
6
+
7
+ module Fulmar
8
+ module Domain
9
+ module Model
10
+ # Loads and prepares the configuration from the yaml file
11
+ class Configuration
12
+ attr_accessor :environment, :target, :base_path
13
+ attr_reader :debug
14
+
15
+ def initialize(data, base_path = '', debug = false)
16
+ @data = data
17
+ @base_path = base_path
18
+ @debug = debug
19
+ prepare_data
20
+ end
21
+
22
+ # Allow access of configuration via array/hash access methods (read access)
23
+ def [](id)
24
+ ready? ? @data[:environments][@environment][@target][id] : nil
25
+ end
26
+
27
+ # Allow access of configuration via array/hash access methods (write access)
28
+ def []=(id, value)
29
+ if ready?
30
+ @data[:environments][@environment][@target][id] = value
31
+ else
32
+ fail 'Environment or target not set. Please set both variables via configuration.environment = \'xxx\' / '\
33
+ 'configuration.target = \'yyy\''
34
+ end
35
+ end
36
+
37
+ # Set the environment and target in one call
38
+ def set(environment, target = nil)
39
+ # For convenience, allow something like "environment:target" as string
40
+ if environment.class == String
41
+ fields = environment.split(':')
42
+ @environment = fields.first.to_sym
43
+ @target = fields.size > 1 ? fields[1].to_sym : nil
44
+ else
45
+ @environment = environment
46
+ @target = target unless target.nil?
47
+ end
48
+ end
49
+
50
+ # Checks if environment and target are set
51
+ def ready?
52
+ return false if @environment.nil? || @target.nil?
53
+ fail 'Environment is invalid' if @data[:environments][@environment].nil?
54
+ fail 'Target is invalid' if @data[:environments][@environment][@target].nil?
55
+ true
56
+ end
57
+
58
+ # Return the project
59
+ def project
60
+ @project ||= Fulmar::Domain::Model::Project.new(@data[:project])
61
+ end
62
+
63
+ def plugins
64
+ @data[:plugins] || {}
65
+ end
66
+
67
+ # Allows iterating over all targets from all configured environments
68
+ def each
69
+ @data[:environments].each_key do |env|
70
+ @data[:environments][env].each_pair do |target, data|
71
+ yield(env, target, data)
72
+ end
73
+ end
74
+ end
75
+
76
+ # Return the combined user and host
77
+ # @todo Is there another way to do this?
78
+ def ssh_user_and_host
79
+ self[:user].blank? ? self[:hostname] : self[:user] + '@' + self[:hostname]
80
+ end
81
+
82
+ # Handle dependencies
83
+ # @todo Refactor this to work with the dependencies plugin
84
+ def dependencies(env = nil)
85
+ if env.nil? || !@data[:dependencies].key?(env)
86
+ @data[:dependencies][:all]
87
+ else
88
+ @data[:dependencies][:all].deep_merge(@data[:dependencies][env])
89
+ end
90
+ end
91
+
92
+ # Allow access to host list
93
+ def hosts
94
+ @data[:hosts]
95
+ end
96
+
97
+ # Check for a feature
98
+ # @todo Do we still need this? Maybe replace this with a "plugin?" method?
99
+ def feature?(feature)
100
+ return @data[:features].include? feature.to_s unless @data[:features].nil?
101
+ case feature
102
+ when :maria
103
+ key? :maria
104
+ else
105
+ false
106
+ end
107
+ end
108
+
109
+ # Checks if a configuration key exists in one of the targets
110
+ def key?(key)
111
+ each { |_env, _target, data| return true unless data[key].nil? }
112
+ false
113
+ end
114
+
115
+ # Merge another configuration into the currently active one
116
+ # Useful for supplying a default configuration, as values are not overwritten.
117
+ # Hashes are merged.
118
+ # @param [Hash] other
119
+ def merge(other)
120
+ return unless @environment && @target
121
+ @data[:environments][@environment][@target] = other.deep_merge(@data[:environments][@environment][@target])
122
+ end
123
+
124
+ protected
125
+
126
+ # Prepares the configuration
127
+ def prepare_data
128
+ handle_inheritance
129
+ merge_hosts
130
+ absolutize_paths
131
+ end
132
+
133
+ # Merges the :all-configuration into targets.
134
+ # [:environments][:all] into *all* targets
135
+ # [:environments][:something][:all] into all targets from environment :something
136
+ def handle_inheritance
137
+ global_config = @data[:environments].delete(:all) || {}
138
+ environments = @data[:environments].keys
139
+ environments.each do |env|
140
+ environment_config = @data[:environments][env].delete(:all) || {}
141
+ targets = @data[:environments][env].keys
142
+ targets.each do |target|
143
+ local_config = @data[:environments][env][target] || {}
144
+ @data[:environments][env][target] = global_config.deep_merge(environment_config).deep_merge(local_config)
145
+ @data[:environments][env][target][:debug] = @debug
146
+ end
147
+ end
148
+ end
149
+
150
+ # Merges the host configuration into the targets referring to it
151
+ def merge_hosts
152
+ each do |env, target, data|
153
+ next if data[:host].nil?
154
+ host = data[:host].to_sym
155
+ @data[:environments][env][target] = @data[:hosts][host].deep_merge(data) unless @data[:hosts][host].nil?
156
+ end
157
+ end
158
+
159
+ # Prepends the base to the path if it is not already absolute
160
+ def absolutize(path, base = @base_path)
161
+ return base if path == '.'
162
+ path[0, 1] == '/' ? path : base + '/' + path
163
+ end
164
+
165
+ # Checks if a key is a local path
166
+ def local_path?(key)
167
+ key.to_s.split('_').last == 'path' && !key.to_s.include?('remote')
168
+ end
169
+
170
+ # Check all keys ending with '_path' and prepend either the
171
+ # @base_path or the local_path from the environment
172
+ def absolutize_paths
173
+ each do |_env, _target, data|
174
+ data.keys.each do |key|
175
+ data[:local_path] = absolutize(data[:local_path]) if data[:local_path]
176
+ if local_path?(key) && data[key].class == String
177
+ data[key] = absolutize(data[key], data[:local_path] || @base_path)
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end