apperol 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +2 -0
- data/apperol.gemspec +26 -0
- data/bin/apperol +5 -0
- data/lib/apperol/cli.rb +244 -0
- data/lib/apperol/version.rb +3 -0
- data/lib/apperol.rb +5 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: db158e412b26ecb904f357742bd9e6560d573627
|
4
|
+
data.tar.gz: 9d9939c3ec0104b2a3c1b860268b9a6cee9c22ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d15027aed70b6c7850587104b070477df35d4eecca06a640c4ab5a1c583b61a88682aa4cbf7a50881cd1ef2a33dff2eba09e124cb170b923d6201b8c3b5b366d
|
7
|
+
data.tar.gz: 059e5e660c1b8c106ec44492df8d5505e128fe04f264f7ff78c065778ee15a41a8f2849cb5985d3ca7a1d885825daf453b6b1a1a1aedc2fbe686325f1c71ceda
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Yannick
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Apperol
|
2
|
+
|
3
|
+
Create apps from heroku repositories on GitHub.
|
4
|
+
Use app.json to customize options.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'apperol'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install apperol
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
```
|
25
|
+
Usage: apperol [options] [app_extension]
|
26
|
+
-p, --personal Force app in personal apps instead of orgs
|
27
|
+
-s, --stack stack Stack for app on heroku
|
28
|
+
-b, --branch branch Branch to setup app from
|
29
|
+
-r, --repo repo GitHub repository source
|
30
|
+
-o, --org org GitHub org where the current repo is located
|
31
|
+
-u, --user user GitHub user where the current repo is located
|
32
|
+
-h, --help Displays Help
|
33
|
+
|
34
|
+
```
|
35
|
+
|
36
|
+
## Information
|
37
|
+
|
38
|
+
- App will be named [current_dir]-[app_extension] e.g.: direwolf-staging
|
39
|
+
- Apperol CLI will have many options based on `env` part of the `app.json`
|
40
|
+
- Apperol CLI uses heroku org by default, use `-r user/repo` to specify yours.
|
41
|
+
|
42
|
+
## Contributing
|
43
|
+
|
44
|
+
1. Fork it ( https://github.com/[my-github-username]/apperol/fork )
|
45
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
46
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
47
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
48
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/apperol.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'apperol/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "apperol"
|
8
|
+
spec.version = Apperol::VERSION
|
9
|
+
spec.authors = ["Yannick"]
|
10
|
+
spec.email = ["yannick@heroku.com"]
|
11
|
+
spec.summary = %q{Create heroku app from heroku repository}
|
12
|
+
spec.description = %q{Create heroku app from heroku repository with app.json}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
|
24
|
+
spec.add_dependency "netrc", "~> 0.8.0"
|
25
|
+
spec.add_dependency "spinning_cursor", "~> 0.3.0"
|
26
|
+
end
|
data/bin/apperol
ADDED
data/lib/apperol/cli.rb
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "net/http"
|
3
|
+
require "openssl"
|
4
|
+
require "json"
|
5
|
+
require "optparse"
|
6
|
+
require "netrc"
|
7
|
+
require "spinning_cursor"
|
8
|
+
|
9
|
+
module Apperol
|
10
|
+
class CLI
|
11
|
+
EX_USAGE = 64
|
12
|
+
|
13
|
+
def initialize(args = [])
|
14
|
+
@options = {}
|
15
|
+
|
16
|
+
parser = OptionParser.new do|opts|
|
17
|
+
opts.banner = "Usage: apperol [options] [app_extension]"
|
18
|
+
app_json["env"].each do |key, definition|
|
19
|
+
option_key_name = key.downcase.gsub("_", "-")
|
20
|
+
option_key_key = key.downcase.to_sym
|
21
|
+
# Set default
|
22
|
+
@options[option_key_key] = definition["value"]
|
23
|
+
opts.on("--#{option_key_name} value", "#{definition["description"]} (Default: '#{definition["value"]}')") do |value|
|
24
|
+
@options[option_key_key] = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
opts.on("-p", "--personal", "Force app in personal apps instead of orgs") do
|
28
|
+
@options[:personal] = true
|
29
|
+
end
|
30
|
+
opts.on("-r", "--repo repo", "GitHub repository used for the deploy") do |repo|
|
31
|
+
@options[:repo] = repo
|
32
|
+
end
|
33
|
+
opts.on("-o", "--org org", "GitHub org where current repo is located") do |org|
|
34
|
+
@options[:org] = org
|
35
|
+
end
|
36
|
+
opts.on("-u", "--user user", "GitHub user where current repo is located") do |user|
|
37
|
+
@options[:org] = user
|
38
|
+
end
|
39
|
+
opts.on("-s", "--stack stack", "Stack for app on heroku") do |stack|
|
40
|
+
@options[:stack] = stack
|
41
|
+
end
|
42
|
+
opts.on("-b", "--branch branch", "Branch to setup app from") do |branch|
|
43
|
+
@options[:branch] = branch
|
44
|
+
end
|
45
|
+
opts.on('-h', '--help', 'Displays Help') do
|
46
|
+
puts opts
|
47
|
+
exit EX_USAGE
|
48
|
+
end
|
49
|
+
end
|
50
|
+
parser.parse!(args)
|
51
|
+
|
52
|
+
@app_extension = args.shift
|
53
|
+
|
54
|
+
unless @app_extension
|
55
|
+
$stderr.puts("usage: apperol [options] [app_extension]")
|
56
|
+
exit EX_USAGE
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def call
|
61
|
+
heroku_app_setup
|
62
|
+
end
|
63
|
+
|
64
|
+
def heroku_app_setup
|
65
|
+
req = Net::HTTP::Post.new(app_setup_url, initheader = heroku_headers)
|
66
|
+
req.basic_auth *heroku_creds
|
67
|
+
req.body = app_setup_payload
|
68
|
+
$stdout.puts("Setting up heroku app #{heroku_app_name}-#{@app_extension}")
|
69
|
+
res = heroku_http.request(req)
|
70
|
+
if res.code != "202"
|
71
|
+
$stderr.puts JSON.parse(res.body)["message"]
|
72
|
+
exit 1
|
73
|
+
else
|
74
|
+
poll_app_status(JSON.parse(res.body))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def poll_app_status(response)
|
79
|
+
req = Net::HTTP::Get.new(app_setup_url(response["id"]), initheader = heroku_headers)
|
80
|
+
req.basic_auth *heroku_creds
|
81
|
+
res = {}
|
82
|
+
spin_wait("Setting up your app", "App setup done") do
|
83
|
+
loop do
|
84
|
+
res = JSON.parse(heroku_http.request(req).body)
|
85
|
+
break unless res["status"] == "pending"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
if res["status"] == "succeeded"
|
89
|
+
unless res["postdeploy"].empty?
|
90
|
+
$stdout.puts(res["postdeploy"]["output"])
|
91
|
+
end
|
92
|
+
$stdout.puts("Application #{res["app"]["name"]}.herokuapp.com has been successfully created.")
|
93
|
+
exit 0
|
94
|
+
else
|
95
|
+
$stderr.puts("error: #{res["failure_message"]}")
|
96
|
+
unless res["manifest_errors"].empty?
|
97
|
+
$stderr.puts(" #{res["manifest_errors"]}")
|
98
|
+
end
|
99
|
+
unless res["postdeploy"].empty?
|
100
|
+
$stderr.puts(res["postdeploy"]["output"])
|
101
|
+
end
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def spin_wait(banner_txt, message_txt)
|
107
|
+
SpinningCursor.run do
|
108
|
+
banner banner_txt
|
109
|
+
type :dots
|
110
|
+
message message_txt
|
111
|
+
end
|
112
|
+
|
113
|
+
yield
|
114
|
+
|
115
|
+
SpinningCursor.stop
|
116
|
+
end
|
117
|
+
|
118
|
+
def app_setup_url(id = nil)
|
119
|
+
URI("https://api.heroku.com/app-setups/#{id}")
|
120
|
+
end
|
121
|
+
|
122
|
+
def heroku_headers
|
123
|
+
{
|
124
|
+
"Content-Type" => "application/json",
|
125
|
+
"Accept" => "application/vnd.heroku+json; version=3"
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def heroku_http
|
130
|
+
@heroku_http ||= Net::HTTP.new(app_setup_url.hostname, app_setup_url.port).tap do |http|
|
131
|
+
http.use_ssl = true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def app_setup_payload
|
136
|
+
payload = {
|
137
|
+
app: {
|
138
|
+
name: "#{heroku_app_name}-#{@app_extension}",
|
139
|
+
stack: stack
|
140
|
+
},
|
141
|
+
source_blob: { url: github_tarball_location },
|
142
|
+
overrides: { env: {}}
|
143
|
+
}
|
144
|
+
required_not_filled = []
|
145
|
+
app_json["env"].each do |key, definition|
|
146
|
+
value = @options[key.downcase.to_sym]
|
147
|
+
if definition["required"] && value.strip.empty?
|
148
|
+
required_not_filled << key
|
149
|
+
end
|
150
|
+
payload[:overrides][:env][key] = value
|
151
|
+
end
|
152
|
+
unless required_not_filled.empty?
|
153
|
+
$stderr.puts("error: Required fields not filled. Please specify them. #{required_not_filled}")
|
154
|
+
exit 1
|
155
|
+
end
|
156
|
+
payload[:app][:organization] = "heroku" unless personal_app?
|
157
|
+
payload.to_json
|
158
|
+
end
|
159
|
+
|
160
|
+
def github_tarball_location
|
161
|
+
$stdout.puts("Getting tarball location for empirical branch #{github_branch}")
|
162
|
+
req = Net::HTTP::Get.new(github_url)
|
163
|
+
req.basic_auth *github_creds
|
164
|
+
res = github_http.request(req)
|
165
|
+
if res["Location"]
|
166
|
+
res["Location"]
|
167
|
+
else
|
168
|
+
$stderr.puts("error: No tarball found for #{github_url} : #{JSON.parse(res.body)["message"]}")
|
169
|
+
exit 1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def github_http
|
174
|
+
@github_http ||= Net::HTTP.new(github_url.hostname, github_url.port).tap do |http|
|
175
|
+
http.use_ssl = true
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def personal_app?
|
180
|
+
@options.fetch(:personal, false)
|
181
|
+
end
|
182
|
+
|
183
|
+
def repo
|
184
|
+
@options.fetch(:repo, default_repo)
|
185
|
+
end
|
186
|
+
|
187
|
+
def default_repo
|
188
|
+
"#{org}/#{heroku_app_name}"
|
189
|
+
end
|
190
|
+
|
191
|
+
def org
|
192
|
+
@options.fetch(:org, "heroku")
|
193
|
+
end
|
194
|
+
|
195
|
+
def rollbar_token
|
196
|
+
@options.fetch(:rollbar_token) {
|
197
|
+
$stdout.puts("Missing rollbar_token option, its not required but you won't have exception tracking")
|
198
|
+
""
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
def github_branch
|
203
|
+
@options.fetch(:branch, "master")
|
204
|
+
end
|
205
|
+
|
206
|
+
def stack
|
207
|
+
@options.fetch(:stack, "cedar-14")
|
208
|
+
end
|
209
|
+
|
210
|
+
def rack_env
|
211
|
+
@options.fetch(:rack_env) {
|
212
|
+
@app_extension == "production" ? "production" : "staging"
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
def github_url
|
217
|
+
@github_url ||= URI("https://api.github.com/repos/#{repo}/tarball/#{github_branch}")
|
218
|
+
end
|
219
|
+
|
220
|
+
def github_creds
|
221
|
+
netrc["api.github.com"]
|
222
|
+
end
|
223
|
+
|
224
|
+
def heroku_creds
|
225
|
+
netrc["api.heroku.com"]
|
226
|
+
end
|
227
|
+
|
228
|
+
def netrc
|
229
|
+
@netrc ||= Netrc.read
|
230
|
+
end
|
231
|
+
|
232
|
+
def heroku_app_name
|
233
|
+
Dir.pwd.split("/").last
|
234
|
+
end
|
235
|
+
|
236
|
+
def app_json
|
237
|
+
unless File.exists?("app.json")
|
238
|
+
$stderr.puts("No app.json file here")
|
239
|
+
exit 1
|
240
|
+
end
|
241
|
+
@app_json ||= JSON.parse(File.read("app.json"))
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
data/lib/apperol.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apperol
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yannick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: netrc
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.8.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: spinning_cursor
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.3.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.3.0
|
69
|
+
description: Create heroku app from heroku repository with app.json
|
70
|
+
email:
|
71
|
+
- yannick@heroku.com
|
72
|
+
executables:
|
73
|
+
- apperol
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- apperol.gemspec
|
83
|
+
- bin/apperol
|
84
|
+
- lib/apperol.rb
|
85
|
+
- lib/apperol/cli.rb
|
86
|
+
- lib/apperol/version.rb
|
87
|
+
homepage: ''
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 2.2.2
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Create heroku app from heroku repository
|
111
|
+
test_files: []
|