addons 0.0.9 → 0.1.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/Gemfile +4 -0
- data/Gemfile.lock +34 -28
- data/README.md +48 -2
- data/addons.gemspec +2 -0
- data/lib/addons.rb +1 -1
- data/lib/addons/config.rb +1 -18
- data/lib/addons/railtie.rb +10 -0
- data/lib/addons/recipes/instructions.rb +133 -0
- data/lib/addons/recipes/recipe.rb +233 -0
- data/lib/addons/recipes/recipes.rb +91 -0
- data/lib/addons/recipes/types.rb +23 -0
- data/lib/addons/version.rb +1 -1
- data/lib/generators/addons/install_generator.rb +24 -0
- data/lib/tasks/addons_recipe.rake +44 -0
- metadata +36 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b1f48899f1dcbaf1336913a3e4212ee1221bf0a
|
|
4
|
+
data.tar.gz: 6761392fc427226f1466592bf4942effee8b3c5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d6ec04265ee6c58aea869d1f88a365079deb94fd68efa7c01bb4b1513eb7962f57955007003fa0dc9cf4f42e132407248ac6fed2193b425ee1518bd8d5d231e
|
|
7
|
+
data.tar.gz: 7f27557fc9550412a72917aa0ed1631e45ae50e7b2a88709e663a382291e4675da5c27a59b6872887697623f48daa33b04cace039ad5e565d77d20b9a823141e
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,39 +1,38 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
addons (0.0
|
|
5
|
-
figaro
|
|
4
|
+
addons (0.1.0)
|
|
6
5
|
json
|
|
7
6
|
|
|
8
7
|
GEM
|
|
9
8
|
remote: https://rubygems.org/
|
|
10
9
|
specs:
|
|
11
|
-
actionmailer (4.0.
|
|
12
|
-
actionpack (= 4.0.
|
|
10
|
+
actionmailer (4.0.3)
|
|
11
|
+
actionpack (= 4.0.3)
|
|
13
12
|
mail (~> 2.5.4)
|
|
14
|
-
actionpack (4.0.
|
|
15
|
-
activesupport (= 4.0.
|
|
13
|
+
actionpack (4.0.3)
|
|
14
|
+
activesupport (= 4.0.3)
|
|
16
15
|
builder (~> 3.1.0)
|
|
17
16
|
erubis (~> 2.7.0)
|
|
18
17
|
rack (~> 1.5.2)
|
|
19
18
|
rack-test (~> 0.6.2)
|
|
20
|
-
activemodel (4.0.
|
|
21
|
-
activesupport (= 4.0.
|
|
19
|
+
activemodel (4.0.3)
|
|
20
|
+
activesupport (= 4.0.3)
|
|
22
21
|
builder (~> 3.1.0)
|
|
23
|
-
activerecord (4.0.
|
|
24
|
-
activemodel (= 4.0.
|
|
22
|
+
activerecord (4.0.3)
|
|
23
|
+
activemodel (= 4.0.3)
|
|
25
24
|
activerecord-deprecated_finders (~> 1.0.2)
|
|
26
|
-
activesupport (= 4.0.
|
|
25
|
+
activesupport (= 4.0.3)
|
|
27
26
|
arel (~> 4.0.0)
|
|
28
27
|
activerecord-deprecated_finders (1.0.3)
|
|
29
|
-
activesupport (4.0.
|
|
28
|
+
activesupport (4.0.3)
|
|
30
29
|
i18n (~> 0.6, >= 0.6.4)
|
|
31
30
|
minitest (~> 4.2)
|
|
32
31
|
multi_json (~> 1.3)
|
|
33
32
|
thread_safe (~> 0.1)
|
|
34
33
|
tzinfo (~> 0.3.37)
|
|
35
34
|
arel (4.0.2)
|
|
36
|
-
atomic (1.1.
|
|
35
|
+
atomic (1.1.16)
|
|
37
36
|
builder (3.1.4)
|
|
38
37
|
diff-lcs (1.2.5)
|
|
39
38
|
erubis (2.7.0)
|
|
@@ -48,25 +47,28 @@ GEM
|
|
|
48
47
|
treetop (~> 1.4.8)
|
|
49
48
|
mime-types (1.25.1)
|
|
50
49
|
minitest (4.7.5)
|
|
51
|
-
multi_json (1.
|
|
52
|
-
|
|
50
|
+
multi_json (1.9.2)
|
|
51
|
+
netrc (0.7.7)
|
|
52
|
+
polyglot (0.3.4)
|
|
53
53
|
rack (1.5.2)
|
|
54
54
|
rack-test (0.6.2)
|
|
55
55
|
rack (>= 1.0)
|
|
56
|
-
rails (4.0.
|
|
57
|
-
actionmailer (= 4.0.
|
|
58
|
-
actionpack (= 4.0.
|
|
59
|
-
activerecord (= 4.0.
|
|
60
|
-
activesupport (= 4.0.
|
|
56
|
+
rails (4.0.3)
|
|
57
|
+
actionmailer (= 4.0.3)
|
|
58
|
+
actionpack (= 4.0.3)
|
|
59
|
+
activerecord (= 4.0.3)
|
|
60
|
+
activesupport (= 4.0.3)
|
|
61
61
|
bundler (>= 1.3.0, < 2.0)
|
|
62
|
-
railties (= 4.0.
|
|
62
|
+
railties (= 4.0.3)
|
|
63
63
|
sprockets-rails (~> 2.0.0)
|
|
64
|
-
railties (4.0.
|
|
65
|
-
actionpack (= 4.0.
|
|
66
|
-
activesupport (= 4.0.
|
|
64
|
+
railties (4.0.3)
|
|
65
|
+
actionpack (= 4.0.3)
|
|
66
|
+
activesupport (= 4.0.3)
|
|
67
67
|
rake (>= 0.8.7)
|
|
68
68
|
thor (>= 0.18.1, < 2.0)
|
|
69
69
|
rake (10.1.1)
|
|
70
|
+
rest_client (1.7.2)
|
|
71
|
+
netrc (~> 0.7.7)
|
|
70
72
|
rspec (2.14.1)
|
|
71
73
|
rspec-core (~> 2.14.0)
|
|
72
74
|
rspec-expectations (~> 2.14.0)
|
|
@@ -75,7 +77,7 @@ GEM
|
|
|
75
77
|
rspec-expectations (2.14.5)
|
|
76
78
|
diff-lcs (>= 1.1.3, < 2.0)
|
|
77
79
|
rspec-mocks (2.14.5)
|
|
78
|
-
sprockets (2.
|
|
80
|
+
sprockets (2.11.0)
|
|
79
81
|
hike (~> 1.2)
|
|
80
82
|
multi_json (~> 1.0)
|
|
81
83
|
rack (~> 1.0)
|
|
@@ -85,13 +87,13 @@ GEM
|
|
|
85
87
|
activesupport (>= 3.0)
|
|
86
88
|
sprockets (~> 2.8)
|
|
87
89
|
thor (0.18.1)
|
|
88
|
-
thread_safe (0.1
|
|
89
|
-
atomic
|
|
90
|
+
thread_safe (0.3.1)
|
|
91
|
+
atomic (>= 1.1.7, < 2)
|
|
90
92
|
tilt (1.4.1)
|
|
91
93
|
treetop (1.4.15)
|
|
92
94
|
polyglot
|
|
93
95
|
polyglot (>= 0.3.1)
|
|
94
|
-
tzinfo (0.3.
|
|
96
|
+
tzinfo (0.3.39)
|
|
95
97
|
|
|
96
98
|
PLATFORMS
|
|
97
99
|
ruby
|
|
@@ -99,5 +101,9 @@ PLATFORMS
|
|
|
99
101
|
DEPENDENCIES
|
|
100
102
|
addons!
|
|
101
103
|
bundler (~> 1.3)
|
|
104
|
+
figaro
|
|
105
|
+
json
|
|
106
|
+
rails
|
|
102
107
|
rake
|
|
108
|
+
rest_client
|
|
103
109
|
rspec (~> 2.6)
|
data/README.md
CHANGED
|
@@ -16,9 +16,55 @@ Or install it yourself as:
|
|
|
16
16
|
|
|
17
17
|
$ gem install addons
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Recipe Runner
|
|
20
|
+
|
|
21
|
+
To run all recipes related to your app, run this in your Terminal:
|
|
22
|
+
```shell
|
|
23
|
+
rake addons:recipe:run[all]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Recipes
|
|
27
|
+
|
|
28
|
+
#### Mailgun SMTP
|
|
29
|
+
|
|
30
|
+
Run the recipe
|
|
31
|
+
```shell
|
|
32
|
+
rake addons:recipe:run[mailgun_smtp]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Copy and paste this to the rails console to test your email settings
|
|
36
|
+
```ruby
|
|
37
|
+
class TestMailer < ActionMailer::Base
|
|
38
|
+
|
|
39
|
+
default :from => "info@addonlist.com"
|
|
40
|
+
|
|
41
|
+
def welcome_email
|
|
42
|
+
mail(:to => "youremail@example.com", :subject => "Test Mailgun SMTP mail", :body => "Test Mailgun SMTP mail body")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
TestMailer.welcome_email.deliver
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Mailgun HTTP
|
|
50
|
+
|
|
51
|
+
Run the recipe
|
|
52
|
+
```shell
|
|
53
|
+
rake addons:recipe:run[mailgun_http]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Copy and paste this to the rails console to test your email settings
|
|
57
|
+
```ruby
|
|
58
|
+
email = {
|
|
59
|
+
to: "youremail@example.com",
|
|
60
|
+
from: "info@addonlist.com",
|
|
61
|
+
subject: "Test Mailgun HTTP mail",
|
|
62
|
+
body: "Test Mailgun HTTP mail body"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Addons::Mailgun.send_simple_message(email)
|
|
66
|
+
```
|
|
20
67
|
|
|
21
|
-
TODO: Write usage instructions here
|
|
22
68
|
|
|
23
69
|
## Contributing
|
|
24
70
|
|
data/addons.gemspec
CHANGED
|
@@ -18,9 +18,11 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
+
spec.add_dependency "rest_client"
|
|
21
22
|
spec.add_dependency "json"
|
|
22
23
|
spec.add_dependency "figaro"
|
|
23
24
|
|
|
25
|
+
spec.add_development_dependency "rails"
|
|
24
26
|
spec.add_development_dependency "bundler", "~> 1.3"
|
|
25
27
|
spec.add_development_dependency "rake"
|
|
26
28
|
spec.add_development_dependency "rspec", "~> 2.6"
|
data/lib/addons.rb
CHANGED
data/lib/addons/config.rb
CHANGED
|
@@ -2,7 +2,7 @@ require 'net/http'
|
|
|
2
2
|
require 'open-uri'
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
5
|
-
BASE_CONFIG_URL = "https://
|
|
5
|
+
BASE_CONFIG_URL = "https://launchpack.io/api/v1/apps/config"
|
|
6
6
|
|
|
7
7
|
module Addons
|
|
8
8
|
class Config
|
|
@@ -44,23 +44,6 @@ module Addons
|
|
|
44
44
|
|
|
45
45
|
services = JSON.parse(response)
|
|
46
46
|
|
|
47
|
-
# gitignore config/application.yml so we aren't storing sensitive info in .git
|
|
48
|
-
# TODO: replace figaro with figaro-addons and application.yml with addons.yml
|
|
49
|
-
gitignore = Rails.root.join('.gitignore')
|
|
50
|
-
if File.readlines(gitignore).grep(/config\/application.yml/).size == 0
|
|
51
|
-
File.open(gitignore, 'a') do |file|
|
|
52
|
-
file.puts 'config/application.yml'
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
figaro = Rails.root.join('Gemfile')
|
|
57
|
-
if File.readlines(figaro).grep(/gem 'figaro'/).size == 0
|
|
58
|
-
File.open(figaro, 'a') do |file|
|
|
59
|
-
file.puts ""
|
|
60
|
-
file.puts "gem 'figaro' # Addons dependency"
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
47
|
# write config/application.yml to load env vars on startup
|
|
65
48
|
filename = Rails.root.join('config', 'application.yml')
|
|
66
49
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module Addons
|
|
2
|
+
module Recipe
|
|
3
|
+
module Instructions
|
|
4
|
+
class Git
|
|
5
|
+
GIT_EXISTS = {
|
|
6
|
+
"run_msg" => "Verifying you're running this recipe in the top level of a Git repository.",
|
|
7
|
+
"type" => InstructionTypes::DIRECTORY_EXISTS,
|
|
8
|
+
"command" => "./.git",
|
|
9
|
+
"error_msg" => "Error: This is not the top level of a git repository. Must exit.",
|
|
10
|
+
}
|
|
11
|
+
GIT_ALL_CODE_COMMITTED = {
|
|
12
|
+
"run_msg" => "Verifying that you have no uncommitted code in Git",
|
|
13
|
+
"type" => InstructionTypes::TERMINAL_COMMAND,
|
|
14
|
+
"command" => "git status",
|
|
15
|
+
"expected_result" => {
|
|
16
|
+
"type" => ResultTypes::DOES_NOT_CONTAIN_STRING,
|
|
17
|
+
"values" => ["Changes not staged for commit", "Changes to be committed"]
|
|
18
|
+
},
|
|
19
|
+
"error_msg" => "Error: You have unstaged files or uncommitted changes in Git. Please commit or stash everything so we can start on a clean slate.",
|
|
20
|
+
}
|
|
21
|
+
GIT_COMMIT_RECIPE = {
|
|
22
|
+
"run_msg" => "Committing changes made by #{Addons::Recipe::RECIPE_NAME_PLACEHOLDER} version: #{Addons::Recipe::RECIPE_VERSION_PLACEHOLDER} Launchpack.io recipe",
|
|
23
|
+
"type" => InstructionTypes::TERMINAL_COMMAND,
|
|
24
|
+
"command" => "git add .; git commit -m \"LAUNCHPACK RECIPE - #{Addons::Recipe::RECIPE_NAME_PLACEHOLDER} version: #{Addons::Recipe::RECIPE_VERSION_PLACEHOLDER}\"",
|
|
25
|
+
"expected_result" => {
|
|
26
|
+
"type" => ResultTypes::NO_EXPECTATION
|
|
27
|
+
},
|
|
28
|
+
"error_msg" => "Error: Git commit for this recipe failed.",
|
|
29
|
+
}
|
|
30
|
+
GIT_RESET_HARD_HEAD = {
|
|
31
|
+
"run_msg" => "There was an error, so we're undoing our changes. If you cannot fix this based on the error message, contact support@addonlist.com.",
|
|
32
|
+
"type" => InstructionTypes::TERMINAL_COMMAND,
|
|
33
|
+
"command" => "git add .; git reset --hard HEAD",
|
|
34
|
+
"expected_result" => {
|
|
35
|
+
"type" => ResultTypes::DOES_NOT_CONTAIN_STRING,
|
|
36
|
+
"values" => ["HEAD is now at"]
|
|
37
|
+
},
|
|
38
|
+
"error_msg" => "Error: we could not reset our work in Git. We hope we cleaned up this recipe, but can't be sure."
|
|
39
|
+
}
|
|
40
|
+
end # class Git
|
|
41
|
+
|
|
42
|
+
class Rails
|
|
43
|
+
RAILS_INITIALIZERS_EXIST = {
|
|
44
|
+
"run_msg" => "Verifying you have a config/initializers directory, per Rails 4",
|
|
45
|
+
"type" => InstructionTypes::DIRECTORY_EXISTS,
|
|
46
|
+
"command" => "./config/initializers",
|
|
47
|
+
"error_msg" => "Error: This does not look like the Rails directory: config/initializers not found"
|
|
48
|
+
}
|
|
49
|
+
RAILS_INITIALIZERS_ADDONS_DIR = {
|
|
50
|
+
"run_msg" => "Creating config/initializers/addons directory",
|
|
51
|
+
"type" => InstructionTypes::DIRECTORY_CREATE,
|
|
52
|
+
"command" => "./config/initializers/addons",
|
|
53
|
+
"error_msg" => "Error: Could not create ./config/initializers/addons directory"
|
|
54
|
+
}
|
|
55
|
+
end # class Rails
|
|
56
|
+
|
|
57
|
+
class Bundler
|
|
58
|
+
BUNDLER_INSTALL = {
|
|
59
|
+
"run_msg" => "running bundle install",
|
|
60
|
+
"type" => InstructionTypes::TERMINAL_COMMAND,
|
|
61
|
+
"command" => "bundle install",
|
|
62
|
+
"expected_result" => {
|
|
63
|
+
"type" => ResultTypes::NO_EXPECTATION
|
|
64
|
+
},
|
|
65
|
+
"error_msg" => "Error: You have unstaged files or uncommitted changes in Git. Please commit or stash everything so we can start on a clean slate."
|
|
66
|
+
}
|
|
67
|
+
end # class Bundler
|
|
68
|
+
|
|
69
|
+
class Addons
|
|
70
|
+
ADDON_RECIPES_INSTALLED_EXISTS = {
|
|
71
|
+
"run_msg" => "Checking for your ./addon_recipes_installed.yml file",
|
|
72
|
+
"type" => InstructionTypes::TERMINAL_COMMAND,
|
|
73
|
+
"command" => "touch ./addon_recipes_installed.yml",
|
|
74
|
+
"expected_result" => {
|
|
75
|
+
"type" => ResultTypes::NO_EXPECTATION
|
|
76
|
+
},
|
|
77
|
+
"error_msg" => "Error: Problem creating addon_recipes_installed.yml to record your installed recipes"
|
|
78
|
+
}
|
|
79
|
+
RECIPE_HASNT_BEEN_RUN_BEFORE = {
|
|
80
|
+
"run_msg" => "Verifying you have not installed this recipe before",
|
|
81
|
+
"type" => InstructionTypes::TERMINAL_COMMAND,
|
|
82
|
+
"command" => "grep '<RECIPE_HANDLE_PLACEHOLDER>' ./addon_recipes_installed.yml",
|
|
83
|
+
"expected_result" => {
|
|
84
|
+
"type" => ResultTypes::IS_EXACT_STRING,
|
|
85
|
+
"values" => [""]
|
|
86
|
+
},
|
|
87
|
+
"error_msg" => "Error: This recipe (<RECIPE_NAME_PLACEHOLDER>) has been run before. It cannot be run again."
|
|
88
|
+
}
|
|
89
|
+
RECORD_RECIPE_INSTALLED = {
|
|
90
|
+
"run_msg" => "Adding \"<RECIPE_HANDLE_PLACEHOLDER>: '<RECIPE_VERSION_PLACEHOLDER>'\" to ./addon_recipes_installed.yml",
|
|
91
|
+
"type" => InstructionTypes::MODIFY_CODE,
|
|
92
|
+
"command" => {
|
|
93
|
+
"type" => ModifyCodeTypes::ADD_CODE_WITHOUT_TAGS,
|
|
94
|
+
"file" => "./addon_recipes_installed.yml",
|
|
95
|
+
"code" => "<RECIPE_HANDLE_PLACEHOLDER>: '<RECIPE_VERSION_PLACEHOLDER>'",
|
|
96
|
+
},
|
|
97
|
+
"error_msg" => "Error: Could not record recipe installation in ./addon_recipes_installed.yml"
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end # module Instructions
|
|
102
|
+
|
|
103
|
+
class Commons
|
|
104
|
+
GIT_READY_RECIPE = [
|
|
105
|
+
# makes sure user has .git
|
|
106
|
+
Addons::Recipe::Instructions::Git::GIT_EXISTS,
|
|
107
|
+
# make sure no uncommitted code
|
|
108
|
+
Addons::Recipe::Instructions::Git::GIT_ALL_CODE_COMMITTED,
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
START_RECIPE = [
|
|
112
|
+
# verify config/initializers directory
|
|
113
|
+
Addons::Recipe::Instructions::Rails::RAILS_INITIALIZERS_EXIST,
|
|
114
|
+
|
|
115
|
+
# make sure we're recording recipes that are being run
|
|
116
|
+
Addons::Recipe::Instructions::Addons::ADDON_RECIPES_INSTALLED_EXISTS,
|
|
117
|
+
# make sure this recipe hasn't been run before
|
|
118
|
+
Addons::Recipe::Instructions::Addons::RECIPE_HASNT_BEEN_RUN_BEFORE,
|
|
119
|
+
|
|
120
|
+
# create config/initializers/addons directory
|
|
121
|
+
Addons::Recipe::Instructions::Rails::RAILS_INITIALIZERS_ADDONS_DIR,
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
END_RECIPE = [
|
|
125
|
+
# log this recipe installed in addon_recipes_installed.yml
|
|
126
|
+
Addons::Recipe::Instructions::Addons::RECORD_RECIPE_INSTALLED,
|
|
127
|
+
# commit changes in Git
|
|
128
|
+
Addons::Recipe::Instructions::Git::GIT_COMMIT_RECIPE,
|
|
129
|
+
]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end # module Recipe
|
|
133
|
+
end # module Addons
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
module Addons
|
|
2
|
+
module Recipe
|
|
3
|
+
|
|
4
|
+
RECIPE_NAME_PLACEHOLDER = "<RECIPE_NAME_PLACEHOLDER>"
|
|
5
|
+
RECIPE_VERSION_PLACEHOLDER = "<RECIPE_VERSION_PLACEHOLDER>"
|
|
6
|
+
RECIPE_HANDLE_PLACEHOLDER = "<RECIPE_HANDLE_PLACEHOLDER>"
|
|
7
|
+
|
|
8
|
+
@@step_count = 1
|
|
9
|
+
@@no_uncommitted_code_on_start_recipe = false
|
|
10
|
+
|
|
11
|
+
def self.error(instruction, recipe)
|
|
12
|
+
if instruction["error_msg"]
|
|
13
|
+
print_message(instruction["error_msg"], recipe)
|
|
14
|
+
end
|
|
15
|
+
puts "✖"
|
|
16
|
+
|
|
17
|
+
cleanup_git_on_error
|
|
18
|
+
exit
|
|
19
|
+
end
|
|
20
|
+
def self.success(instruction, recipe)
|
|
21
|
+
if instruction["success_msg"]
|
|
22
|
+
print_message(instruction["success_msg"], recipe)
|
|
23
|
+
end
|
|
24
|
+
puts "✓"
|
|
25
|
+
end
|
|
26
|
+
def self.print_message(message, recipe)
|
|
27
|
+
puts gsub_recipe_vars(message, recipe)
|
|
28
|
+
end
|
|
29
|
+
def self.gsub_recipe_vars(message, recipe)
|
|
30
|
+
return message.gsub(RECIPE_VERSION_PLACEHOLDER, recipe["revision"]).gsub(RECIPE_NAME_PLACEHOLDER, recipe["name"]).gsub(RECIPE_HANDLE_PLACEHOLDER, recipe["handle"])
|
|
31
|
+
end
|
|
32
|
+
def self.cleanup_git_on_error
|
|
33
|
+
if @@no_uncommitted_code_on_start_recipe
|
|
34
|
+
# clear any uncommitted code we may have added, ONLY so long as we started with no uncommitted code.
|
|
35
|
+
# NOTE: we definitely don't want to reset a user's own uncommitted code
|
|
36
|
+
instruction = Addons::Recipe::Instructions::Git::GIT_RESET_HARD_HEAD
|
|
37
|
+
|
|
38
|
+
result = `#{instruction["command"]}`
|
|
39
|
+
# print the error message if we didnt get the expected result, use result.include? because GIT_RESET_HARD_HEAD uses CONTAINS_STRING
|
|
40
|
+
puts instruction["error_msg"] unless result.include? instruction["expected_result"]["values"][0]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.run(recipe)
|
|
45
|
+
puts "\r\nRunning Launchpack.io #{recipe["platform"]} recipe for: #{recipe["name"]}\r\n\r\n"
|
|
46
|
+
|
|
47
|
+
Addons::Recipe::Commons::GIT_READY_RECIPE.each do |git_instruction|
|
|
48
|
+
run_instruction(git_instruction, recipe)
|
|
49
|
+
|
|
50
|
+
# if we didn't exit, and we see all code in Git is committed already, we can reset git if we see an error
|
|
51
|
+
@@no_uncommitted_code_on_start_recipe = true if git_instruction == Addons::Recipe::Instructions::Git::GIT_ALL_CODE_COMMITTED
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Addons::Recipe::Commons::START_RECIPE.each do |start_instruction|
|
|
55
|
+
run_instruction(start_instruction, recipe)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
recipe["instructions"].each do |instruction|
|
|
59
|
+
run_instruction(instruction, recipe)
|
|
60
|
+
end # do / instruction
|
|
61
|
+
|
|
62
|
+
Addons::Recipe::Commons::END_RECIPE.each do |end_instruction|
|
|
63
|
+
run_instruction(end_instruction, recipe)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end # run
|
|
67
|
+
|
|
68
|
+
def self.run_instruction(instruction, recipe)
|
|
69
|
+
puts "### Step #{@@step_count}: ###"
|
|
70
|
+
|
|
71
|
+
process_instruction(instruction, recipe)
|
|
72
|
+
|
|
73
|
+
puts ""
|
|
74
|
+
@@step_count += 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.recipe_tag(recipe, type)
|
|
78
|
+
if type == "comment"
|
|
79
|
+
" \# LAUNCHPACK RECIPE - " + recipe["name"] + " version: " + recipe["revision"]
|
|
80
|
+
elsif type == "start_comment"
|
|
81
|
+
"\# LAUNCHPACK RECIPE COMMENT START - " + recipe["name"] + " version: " + recipe["revision"] + "\n"
|
|
82
|
+
elsif type == "end_comment"
|
|
83
|
+
"\n\# LAUNCHPACK RECIPE COMMENT END - " + recipe["name"] + " version: " + recipe["revision"] + "\n"
|
|
84
|
+
elsif type == "start"
|
|
85
|
+
"\# LAUNCHPACK RECIPE CODE START - " + recipe["name"] + " version: " + recipe["revision"]
|
|
86
|
+
elsif type == "end"
|
|
87
|
+
"\# LAUNCHPACK RECIPE CODE END - " + recipe["name"] + " version: " + recipe["revision"]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.process_instruction(instruction, recipe)
|
|
92
|
+
print_message(instruction["run_msg"], recipe)
|
|
93
|
+
|
|
94
|
+
case instruction["type"]
|
|
95
|
+
|
|
96
|
+
when InstructionTypes::DIRECTORY_EXISTS
|
|
97
|
+
# check a directory exists
|
|
98
|
+
if Dir.exists?(instruction["command"])
|
|
99
|
+
success(instruction, recipe)
|
|
100
|
+
else
|
|
101
|
+
# exit
|
|
102
|
+
return error(instruction, recipe)
|
|
103
|
+
end
|
|
104
|
+
# end InstructionTypes::DIRECTORY_EXISTS
|
|
105
|
+
|
|
106
|
+
when InstructionTypes::DIRECTORY_CREATE
|
|
107
|
+
# check a directory exists
|
|
108
|
+
if Dir.exists?(instruction["command"]) || Dir.mkdir(instruction["command"])
|
|
109
|
+
success(instruction, recipe)
|
|
110
|
+
else
|
|
111
|
+
# exit
|
|
112
|
+
return error(instruction, recipe)
|
|
113
|
+
end
|
|
114
|
+
# end InstructionTypes::DIRECTORY_CREATE
|
|
115
|
+
|
|
116
|
+
when InstructionTypes::TERMINAL_COMMAND
|
|
117
|
+
result = `#{gsub_recipe_vars(instruction["command"], recipe)}`
|
|
118
|
+
|
|
119
|
+
case instruction["expected_result"]["type"]
|
|
120
|
+
when ResultTypes::NO_EXPECTATION
|
|
121
|
+
success(instruction, recipe)
|
|
122
|
+
when ResultTypes::CONTAINS_STRING
|
|
123
|
+
if result.include? instruction["expected_result"]["values"][0]
|
|
124
|
+
success(instruction, recipe)
|
|
125
|
+
else
|
|
126
|
+
return error(instruction, recipe)
|
|
127
|
+
end
|
|
128
|
+
when ResultTypes::IS_EXACT_STRING
|
|
129
|
+
if instruction["expected_result"]["values"][0] == result
|
|
130
|
+
return success(instruction, recipe)
|
|
131
|
+
else
|
|
132
|
+
return error(instruction, recipe)
|
|
133
|
+
end
|
|
134
|
+
when ResultTypes::DOES_NOT_CONTAIN_STRING
|
|
135
|
+
instruction["expected_result"]["values"].each do |value|
|
|
136
|
+
if result.include?(value)
|
|
137
|
+
return error(instruction, recipe)
|
|
138
|
+
end
|
|
139
|
+
end # end value loop
|
|
140
|
+
success(instruction, recipe)
|
|
141
|
+
end # ResultTypes case
|
|
142
|
+
# end InstructionTypes::TERMINAL_COMMAND
|
|
143
|
+
|
|
144
|
+
when InstructionTypes::MODIFY_CODE
|
|
145
|
+
case instruction["command"]["type"]
|
|
146
|
+
when ModifyCodeTypes::ADD_CODE_REPLACE_FILE
|
|
147
|
+
# open the file for appending
|
|
148
|
+
File.open(instruction["command"]["file"], 'w') do |file|
|
|
149
|
+
file.puts instruction["command"]["code"]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
success(instruction, recipe)
|
|
153
|
+
when ModifyCodeTypes::ADD_CODE, ModifyCodeTypes::COMMENT_CODE
|
|
154
|
+
# open the file for editing
|
|
155
|
+
if instruction["command"]["comment_code_if"]
|
|
156
|
+
original_content = File.open(instruction["command"]["file"]).read()
|
|
157
|
+
new_file = original_content
|
|
158
|
+
updated = false
|
|
159
|
+
|
|
160
|
+
content_to_comment = original_content.scan(instruction["command"]["comment_code_if"])
|
|
161
|
+
|
|
162
|
+
content_to_comment.each do |content|
|
|
163
|
+
replace_with = recipe_tag(recipe, "start_comment")
|
|
164
|
+
replace_with += content.gsub(/^/, "# ")
|
|
165
|
+
replace_with += recipe_tag(recipe, "end_comment")
|
|
166
|
+
|
|
167
|
+
new_file = new_file.gsub(content, replace_with)
|
|
168
|
+
|
|
169
|
+
updated = true
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
if updated
|
|
173
|
+
File.open(instruction["command"]["file"], "w") do |file|
|
|
174
|
+
file.write(new_file)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end # end comment_code_if
|
|
178
|
+
|
|
179
|
+
if instruction["command"]["code"]
|
|
180
|
+
File.open(instruction["command"]["file"], 'a+') do |file|
|
|
181
|
+
file.puts ""
|
|
182
|
+
file.puts recipe_tag(recipe, "start")
|
|
183
|
+
file.puts gsub_recipe_vars(instruction["command"]["code"], recipe)
|
|
184
|
+
file.puts recipe_tag(recipe, "end")
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
success(instruction, recipe)
|
|
189
|
+
|
|
190
|
+
when ModifyCodeTypes::ADD_CODE_WITHOUT_TAGS
|
|
191
|
+
File.open(instruction["command"]["file"], 'a+') do |file|
|
|
192
|
+
file.puts gsub_recipe_vars(instruction["command"]["code"], recipe)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
success(instruction, recipe)
|
|
196
|
+
end # ModifyCodeTypes case
|
|
197
|
+
# end InstructionTypes::MODIFY_CODE
|
|
198
|
+
|
|
199
|
+
when InstructionTypes::SEARCH_AND_COMMENT
|
|
200
|
+
instruction["search_queries"].each_with_index do |query, i|
|
|
201
|
+
search_results = `grep '#{query}' -rI --exclude-dir=\.git ./`
|
|
202
|
+
|
|
203
|
+
if search_results && search_results.length > 0
|
|
204
|
+
# get filenames from each search result
|
|
205
|
+
filenames = search_results.scan(/\.\/\/(.*?):/).uniq
|
|
206
|
+
filenames = filenames.map {|inner_array| inner_array[0] }
|
|
207
|
+
|
|
208
|
+
filenames.each do |filename|
|
|
209
|
+
# create dynamic instruction with filename and instruction["comment_code_if"]
|
|
210
|
+
dynamic_instruction = {
|
|
211
|
+
"run_msg" => "Commenting #{instruction['comment_code_if'][i]} from #{filename}",
|
|
212
|
+
"type" => InstructionTypes::MODIFY_CODE,
|
|
213
|
+
"command" => {
|
|
214
|
+
"type" => ModifyCodeTypes::COMMENT_CODE,
|
|
215
|
+
"file" => filename,
|
|
216
|
+
"comment_code_if" => instruction["comment_code_if"][i],
|
|
217
|
+
},
|
|
218
|
+
"error_msg" => "Error: Could not comment code in #{filename}"
|
|
219
|
+
}
|
|
220
|
+
# process dynamic instruction
|
|
221
|
+
process_instruction dynamic_instruction, recipe
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
end
|
|
225
|
+
end # end search_queries.each
|
|
226
|
+
success(instruction, recipe)
|
|
227
|
+
# end InstructionTypes::SEARCH_AND_COMMENT
|
|
228
|
+
|
|
229
|
+
end # InstructionTypes case
|
|
230
|
+
end # process_instruction
|
|
231
|
+
|
|
232
|
+
end # Recipe
|
|
233
|
+
end # Addons
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
static_recipes = [
|
|
2
|
+
{
|
|
3
|
+
name: "Mailgun SMTP",
|
|
4
|
+
handle: "mailgun_smtp",
|
|
5
|
+
version: "1",
|
|
6
|
+
platform: "Rails 4",
|
|
7
|
+
instructions: [
|
|
8
|
+
# search for existing smtp_settings and comment them out
|
|
9
|
+
{
|
|
10
|
+
run_msg: "Comment exsiting ActionMailer settings throughout the codebase",
|
|
11
|
+
type: InstructionTypes::SEARCH_AND_COMMENT,
|
|
12
|
+
search_queries: ["ActionMailer::Base.delivery_method", "ActionMailer::Base.smtp_settings"],
|
|
13
|
+
comment_code_if: [/ActionMailer::Base.delivery_method\s*=.*$/, /ActionMailer::Base.smtp_settings\s*=\s*{.*}/m],
|
|
14
|
+
error_msg: "Error: Please remove existing ActionMailer::Base.smtp_settings\n(found with: \"grep 'ActionMailer::Base.smtp_settings' -rI --exclude-dir=\.git ./\")"
|
|
15
|
+
},
|
|
16
|
+
# add config/initializers/mailgun.rb
|
|
17
|
+
{
|
|
18
|
+
run_msg: "Adding config/initializers/addons/mailgun_smtp.rb to configure ActiveMailer for Mailgun SMTP",
|
|
19
|
+
type: InstructionTypes::MODIFY_CODE,
|
|
20
|
+
command: {
|
|
21
|
+
type: ModifyCodeTypes::ADD_CODE_REPLACE_FILE,
|
|
22
|
+
file: "config/initializers/addons/mailgun_smtp.rb",
|
|
23
|
+
code: "\
|
|
24
|
+
ActionMailer::Base.smtp_settings = {\n\
|
|
25
|
+
:port => ENV['MAILGUN_SMTP_PORT'],\n\
|
|
26
|
+
:address => ENV['MAILGUN_SMTP_SERVER'],\n\
|
|
27
|
+
:user_name => ENV['MAILGUN_SMTP_LOGIN'],\n\
|
|
28
|
+
:password => ENV['MAILGUN_SMTP_PASSWORD'],\n\
|
|
29
|
+
:domain => 'example.com',\n\
|
|
30
|
+
:authentication => :plain,\n\
|
|
31
|
+
}\n\
|
|
32
|
+
ActionMailer::Base.delivery_method = :smtp\n\
|
|
33
|
+
"
|
|
34
|
+
},
|
|
35
|
+
error_msg: "There was a problem adding Addons::Mailgun.send_simple_email",
|
|
36
|
+
},
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "Mailgun HTTP",
|
|
41
|
+
handle: "mailgun_http",
|
|
42
|
+
version: "1",
|
|
43
|
+
platform: "Rails 4",
|
|
44
|
+
instructions: [
|
|
45
|
+
# install gem rest_client with Gemfile and run from terminal
|
|
46
|
+
{
|
|
47
|
+
run_msg: "Adding gem 'rest_client' to Gemfile",
|
|
48
|
+
type: InstructionTypes::MODIFY_CODE,
|
|
49
|
+
command: {
|
|
50
|
+
type: ModifyCodeTypes::ADD_CODE,
|
|
51
|
+
file: "Gemfile",
|
|
52
|
+
code: "gem 'rest_client'",
|
|
53
|
+
comment_code_if: /rest_client/,
|
|
54
|
+
},
|
|
55
|
+
error_msg: "Error: Could not add gem 'rest_client' to Gemfile"
|
|
56
|
+
},
|
|
57
|
+
Addons::Recipe::Instructions::Bundler::BUNDLER_INSTALL,
|
|
58
|
+
# add Addons::Mailgun.send_simple_email(email) code
|
|
59
|
+
{
|
|
60
|
+
run_msg: "Adding Addons::Mailgun.send_simple_email for sending simple emails",
|
|
61
|
+
type: InstructionTypes::MODIFY_CODE,
|
|
62
|
+
command: {
|
|
63
|
+
type: ModifyCodeTypes::ADD_CODE_REPLACE_FILE,
|
|
64
|
+
file: "config/initializers/addons/mailgun_http.rb",
|
|
65
|
+
code: "\
|
|
66
|
+
require 'rest_client'\n\
|
|
67
|
+
\n\
|
|
68
|
+
module Addons\n\
|
|
69
|
+
class Mailgun\n\
|
|
70
|
+
def self.send_simple_message(email)\n\
|
|
71
|
+
from = email[:from] || email[\"from\"] || email.from\n\
|
|
72
|
+
to = email[:to] || email[\"to\"] || email.to\n\
|
|
73
|
+
subject = email[:subject] || email[\"subject\"] || email.subject\n\
|
|
74
|
+
body = email[:body] || email[\"body\"] || email.body\n\
|
|
75
|
+
\n\
|
|
76
|
+
RestClient.post \"https://api:\#{ENV['MAILGUN_API_KEY']}\"\\\n\
|
|
77
|
+
\"@api.mailgun.net/v2/\#{ENV['MAILGUN_SMTP_LOGIN'].match(/@(.*)$/)[1]}/messages\",\n\
|
|
78
|
+
:from => from,\n\
|
|
79
|
+
:to => to,\n\
|
|
80
|
+
:subject => subject,\n\
|
|
81
|
+
:text => body\n\
|
|
82
|
+
end\n\
|
|
83
|
+
end # Mailgun\n\
|
|
84
|
+
end # Addons\n\
|
|
85
|
+
"
|
|
86
|
+
},
|
|
87
|
+
error_msg: "There was a problem adding Addons::Mailgun.send_simple_email",
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
}, # end Mailgun HTTP
|
|
91
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class InstructionTypes
|
|
2
|
+
DIRECTORY_EXISTS = 0
|
|
3
|
+
DIRECTORY_CREATE = 1
|
|
4
|
+
TERMINAL_COMMAND = 2
|
|
5
|
+
MODIFY_CODE = 3
|
|
6
|
+
SEARCH_AND_COMMENT = 4
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class ResultTypes
|
|
10
|
+
DOES_NOT_CONTAIN_STRING = 0
|
|
11
|
+
NO_EXPECTATION = 1
|
|
12
|
+
IS_EXACT_STRING = 2
|
|
13
|
+
CONTAINS_STRING = 3
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class ModifyCodeTypes
|
|
17
|
+
COMMENT_CODE = 0
|
|
18
|
+
ADD_CODE = 1
|
|
19
|
+
REMOVE_CODE = 2
|
|
20
|
+
REPLACE_CODE = 3
|
|
21
|
+
ADD_CODE_REPLACE_FILE = 4
|
|
22
|
+
ADD_CODE_WITHOUT_TAGS = 5
|
|
23
|
+
end
|
data/lib/addons/version.rb
CHANGED
|
@@ -26,6 +26,30 @@ module Addons
|
|
|
26
26
|
create_file "config/initializers/addons.rb", "# Addons initializer content\nAddons::Config.init()"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def add_application_yml_to_gitignore
|
|
30
|
+
# gitignore config/application.yml so we aren't storing sensitive info in .git
|
|
31
|
+
# TODO: replace figaro with figaro-addons and application.yml with addons.yml
|
|
32
|
+
gitignore = Rails.root.join('.gitignore')
|
|
33
|
+
if File.readlines(gitignore).grep(/config\/application.yml/).size == 0
|
|
34
|
+
File.open(gitignore, 'a') do |file|
|
|
35
|
+
file.puts 'config/application.yml'
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add_figaro_gem
|
|
41
|
+
figaro = Rails.root.join('Gemfile')
|
|
42
|
+
if File.readlines(figaro).grep(/gem 'figaro'/).size == 0
|
|
43
|
+
File.open(figaro, 'a') do |file|
|
|
44
|
+
file.puts ""
|
|
45
|
+
file.puts "gem 'figaro' # Addons dependency"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# bundle install
|
|
50
|
+
result = `bundle install`
|
|
51
|
+
end
|
|
52
|
+
|
|
29
53
|
def bootstrap_app_id_and_token
|
|
30
54
|
# write config/application.yml to load env vars on startup
|
|
31
55
|
filename = Rails.root.join('config', 'application.yml')
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'rest_client'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
require 'addons/recipes/types'
|
|
5
|
+
require 'addons/recipes/recipe'
|
|
6
|
+
require 'addons/recipes/instructions'
|
|
7
|
+
|
|
8
|
+
namespace :addons do
|
|
9
|
+
namespace :recipe do
|
|
10
|
+
|
|
11
|
+
desc "Run a recipe by name"
|
|
12
|
+
task :run, :recipe_handle do |t, args|
|
|
13
|
+
recipes = nil
|
|
14
|
+
recipeUrl = "https://launchpack.io/api/v1/apps/recipes?app_id=#{ENV['ADDONS_APP_ID']}&app_token=#{ENV['ADDONS_APP_TOKEN']}"
|
|
15
|
+
|
|
16
|
+
response = RestClient.get recipeUrl
|
|
17
|
+
recipes = JSON.parse response.body
|
|
18
|
+
|
|
19
|
+
# parse instructions into ruby
|
|
20
|
+
recipes.each { |recipe| recipe["instructions"] = JSON.parse(recipe["instructions"]) }
|
|
21
|
+
|
|
22
|
+
@run = false
|
|
23
|
+
if "all" == args[:recipe_handle]
|
|
24
|
+
puts "Running all your Launchpack.io recipes"
|
|
25
|
+
|
|
26
|
+
recipes.each do |recipe|
|
|
27
|
+
Addons::Recipe.run recipe
|
|
28
|
+
end
|
|
29
|
+
elsif handle = args[:recipe_handle]
|
|
30
|
+
recipes.each do |recipe|
|
|
31
|
+
# run recipe if it's the correct one
|
|
32
|
+
if handle == recipe["handle"]
|
|
33
|
+
@run = true
|
|
34
|
+
Addons::Recipe.run recipe
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
puts "Error: Recipe not found." unless @run
|
|
38
|
+
else
|
|
39
|
+
puts "Error: Incorrect usage. Expecting: `rake addons:recipe:run[recipe_handle]`"
|
|
40
|
+
end
|
|
41
|
+
end # end task: run
|
|
42
|
+
|
|
43
|
+
end # end namespace: recipe
|
|
44
|
+
end # end namespace: addons
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: addons
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Addonlist
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-04-
|
|
11
|
+
date: 2014-04-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rest_client
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: json
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,6 +52,20 @@ dependencies:
|
|
|
38
52
|
- - '>='
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
54
|
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rails
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
41
69
|
- !ruby/object:Gem::Dependency
|
|
42
70
|
name: bundler
|
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -96,8 +124,14 @@ files:
|
|
|
96
124
|
- addons.gemspec
|
|
97
125
|
- lib/addons.rb
|
|
98
126
|
- lib/addons/config.rb
|
|
127
|
+
- lib/addons/railtie.rb
|
|
128
|
+
- lib/addons/recipes/instructions.rb
|
|
129
|
+
- lib/addons/recipes/recipe.rb
|
|
130
|
+
- lib/addons/recipes/recipes.rb
|
|
131
|
+
- lib/addons/recipes/types.rb
|
|
99
132
|
- lib/addons/version.rb
|
|
100
133
|
- lib/generators/addons/install_generator.rb
|
|
134
|
+
- lib/tasks/addons_recipe.rake
|
|
101
135
|
- spec/addons_spec.rb
|
|
102
136
|
homepage: ''
|
|
103
137
|
licenses:
|