static 1.0.1 → 1.0.3
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/LICENSE +5 -5
- data/Rakefile +15 -1
- data/lib/static.rb +14 -0
- data/lib/static/cli.rb +14 -0
- data/lib/static/cli/app/download.rb +14 -0
- data/lib/static/version.rb +15 -1
- data/vendor/manifests-vmc-plugin-0.6.2/Rakefile +38 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin.rb +313 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/errors.rb +21 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/loader.rb +31 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/loader/builder.rb +34 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/loader/normalizer.rb +149 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/loader/resolver.rb +79 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/plugin.rb +145 -0
- data/vendor/manifests-vmc-plugin-0.6.2/lib/manifests-vmc-plugin/version.rb +3 -0
- data/vendor/manifests-vmc-plugin-0.6.2/spec/manifests-vmc-plugin/loader/normalizer_spec.rb +176 -0
- data/vendor/manifests-vmc-plugin-0.6.2/spec/manifests-vmc-plugin/loader/plugin_spec.rb +365 -0
- data/vendor/manifests-vmc-plugin-0.6.2/spec/manifests-vmc-plugin_spec.rb +319 -0
- data/vendor/manifests-vmc-plugin-0.6.2/spec/spec_helper.rb +16 -0
- data/vendor/tunnel-vmc-plugin-0.2.2/helper-app/Gemfile.lock +50 -0
- metadata +28 -24
@@ -0,0 +1,21 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
class CircularDependency < RuntimeError
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_s
|
8
|
+
"Circular dependency in application '#@app'"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class UnknownSymbol < RuntimeError
|
13
|
+
def initialize(sym)
|
14
|
+
@sym = sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"Undefined symbol in manifest: '#@sym'"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "manifests-vmc-plugin/loader/builder"
|
2
|
+
require "manifests-vmc-plugin/loader/normalizer"
|
3
|
+
require "manifests-vmc-plugin/loader/resolver"
|
4
|
+
|
5
|
+
module VMCManifests
|
6
|
+
class Loader
|
7
|
+
include Builder
|
8
|
+
include Normalizer
|
9
|
+
include Resolver
|
10
|
+
|
11
|
+
def initialize(file, resolver)
|
12
|
+
@file = file
|
13
|
+
@resolver = resolver
|
14
|
+
end
|
15
|
+
|
16
|
+
def manifest
|
17
|
+
info = build(@file)
|
18
|
+
normalize! info
|
19
|
+
resolve info, @resolver
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# expand a path relative to the manifest file's directory
|
25
|
+
def from_manifest(path)
|
26
|
+
return path unless @file
|
27
|
+
|
28
|
+
File.expand_path(path, File.dirname(@file))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
module Builder
|
3
|
+
# parse a manifest and merge with its inherited manifests
|
4
|
+
def build(file)
|
5
|
+
manifest = YAML.load_file file
|
6
|
+
|
7
|
+
Array(manifest["inherit"]).each do |path|
|
8
|
+
manifest = merge_parent(path, manifest)
|
9
|
+
end
|
10
|
+
|
11
|
+
manifest
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# merge the manifest at `parent_path' into the `child'
|
17
|
+
def merge_parent(parent_path, child)
|
18
|
+
merge_manifest(build(from_manifest(parent_path)), child)
|
19
|
+
end
|
20
|
+
|
21
|
+
# deep hash merge
|
22
|
+
def merge_manifest(parent, child)
|
23
|
+
merge = proc do |_, old, new|
|
24
|
+
if new.is_a?(Hash) && old.is_a?(Hash)
|
25
|
+
old.merge(new, &merge)
|
26
|
+
else
|
27
|
+
new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
parent.merge(child, &merge)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
module Normalizer
|
3
|
+
MANIFEST_META = ["applications", "properties"]
|
4
|
+
|
5
|
+
def normalize!(manifest)
|
6
|
+
toplevel = toplevel_attributes(manifest)
|
7
|
+
|
8
|
+
apps = manifest["applications"]
|
9
|
+
apps ||= [{}]
|
10
|
+
|
11
|
+
default_paths_to_keys!(apps)
|
12
|
+
|
13
|
+
apps = convert_to_array(apps)
|
14
|
+
|
15
|
+
merge_toplevel!(toplevel, manifest, apps)
|
16
|
+
normalize_apps!(apps)
|
17
|
+
|
18
|
+
manifest["applications"] = apps
|
19
|
+
|
20
|
+
normalize_paths!(apps)
|
21
|
+
|
22
|
+
keyval = normalize_key_val(manifest)
|
23
|
+
manifest.clear.merge!(keyval)
|
24
|
+
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def normalize_paths!(apps)
|
31
|
+
apps.each do |app|
|
32
|
+
app["path"] = from_manifest(app["path"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def convert_to_array(apps)
|
37
|
+
return apps if apps.is_a?(Array)
|
38
|
+
|
39
|
+
ordered_by_deps(apps)
|
40
|
+
end
|
41
|
+
|
42
|
+
# sort applications in dependency order
|
43
|
+
# e.g. if A depends on B, B will be listed before A
|
44
|
+
def ordered_by_deps(apps, processed = Set[])
|
45
|
+
ordered = []
|
46
|
+
apps.each do |tag, info|
|
47
|
+
next if processed.include?(tag)
|
48
|
+
|
49
|
+
if deps = Array(info["depends-on"])
|
50
|
+
dep_apps = {}
|
51
|
+
deps.each do |dep|
|
52
|
+
dep_apps[dep] = apps[dep]
|
53
|
+
end
|
54
|
+
|
55
|
+
processed.add(tag)
|
56
|
+
|
57
|
+
ordered += ordered_by_deps(dep_apps, processed)
|
58
|
+
ordered << info
|
59
|
+
else
|
60
|
+
ordered << info
|
61
|
+
processed.add(tag)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
ordered.each { |app| app.delete("depends-on") }
|
66
|
+
|
67
|
+
ordered
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_paths_to_keys!(apps)
|
71
|
+
return if apps.is_a?(Array)
|
72
|
+
|
73
|
+
apps.each do |tag, app|
|
74
|
+
app["path"] ||= tag
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def normalize_apps!(apps)
|
79
|
+
apps.each do |app|
|
80
|
+
normalize_app!(app)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def merge_toplevel!(toplevel, manifest, apps)
|
85
|
+
return if toplevel.empty?
|
86
|
+
|
87
|
+
apps.collect! do |a|
|
88
|
+
toplevel.merge(a)
|
89
|
+
end
|
90
|
+
|
91
|
+
toplevel.each do |k, _|
|
92
|
+
manifest.delete k
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def normalize_app!(app)
|
97
|
+
if app["framework"].is_a?(Hash)
|
98
|
+
app["framework"] = app["framework"]["name"]
|
99
|
+
end
|
100
|
+
|
101
|
+
if app.key?("mem")
|
102
|
+
app["memory"] = app.delete("mem")
|
103
|
+
end
|
104
|
+
|
105
|
+
if app.key?("url") && app["url"].nil?
|
106
|
+
app["url"] = "none"
|
107
|
+
end
|
108
|
+
|
109
|
+
if app.key?("subdomain")
|
110
|
+
if app.key?("host")
|
111
|
+
app.delete("subdomain")
|
112
|
+
else
|
113
|
+
app["host"] = app.delete("subdomain")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def toplevel_attributes(manifest)
|
119
|
+
top =
|
120
|
+
manifest.reject { |k, _|
|
121
|
+
MANIFEST_META.include? k
|
122
|
+
}
|
123
|
+
|
124
|
+
# implicit toplevel path of .
|
125
|
+
top["path"] ||= "."
|
126
|
+
|
127
|
+
top
|
128
|
+
end
|
129
|
+
|
130
|
+
def normalize_key_val(val)
|
131
|
+
case val
|
132
|
+
when Hash
|
133
|
+
stringified = {}
|
134
|
+
|
135
|
+
val.each do |k, v|
|
136
|
+
stringified[k.to_sym] = normalize_key_val(v)
|
137
|
+
end
|
138
|
+
|
139
|
+
stringified
|
140
|
+
when Array
|
141
|
+
val.collect { |x| normalize_key_val(x) }
|
142
|
+
when nil
|
143
|
+
nil
|
144
|
+
else
|
145
|
+
val.to_s
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
module Resolver
|
3
|
+
def resolve(manifest, resolver)
|
4
|
+
new = {}
|
5
|
+
|
6
|
+
new[:applications] = manifest[:applications].collect do |app|
|
7
|
+
resolve_lexically(resolver, app, [manifest])
|
8
|
+
end
|
9
|
+
|
10
|
+
resolve_lexically(resolver, new, [new])
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# resolve symbols, with hashes introducing new lexical symbols
|
16
|
+
def resolve_lexically(resolver, val, ctx)
|
17
|
+
case val
|
18
|
+
when Hash
|
19
|
+
new = {}
|
20
|
+
|
21
|
+
val.each do |k, v|
|
22
|
+
new[k] = resolve_lexically(resolver, v, [val] + ctx)
|
23
|
+
end
|
24
|
+
|
25
|
+
new
|
26
|
+
when Array
|
27
|
+
val.collect do |v|
|
28
|
+
resolve_lexically(resolver, v, ctx)
|
29
|
+
end
|
30
|
+
when String
|
31
|
+
val.gsub(/\$\{([^\}]+)\}/) do
|
32
|
+
resolve_symbol(resolver, $1, ctx)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
val
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# resolve a symbol to its value, and then resolve that value
|
40
|
+
def resolve_symbol(resolver, sym, ctx)
|
41
|
+
if found = find_symbol(sym.to_sym, ctx)
|
42
|
+
resolve_lexically(resolver, found, ctx)
|
43
|
+
found
|
44
|
+
elsif dynamic = resolver.resolve_symbol(sym)
|
45
|
+
dynamic
|
46
|
+
else
|
47
|
+
fail("Unknown symbol in manifest: #{sym}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# search for a symbol introduced in the lexical context
|
52
|
+
def find_symbol(sym, ctx)
|
53
|
+
ctx.each do |h|
|
54
|
+
if val = resolve_in(h, sym)
|
55
|
+
return val
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# find a value, searching in explicit properties first
|
63
|
+
def resolve_in(hash, *where)
|
64
|
+
find_in_hash(hash, [:properties] + where) ||
|
65
|
+
find_in_hash(hash, where)
|
66
|
+
end
|
67
|
+
|
68
|
+
# helper for following a path of values in a hash
|
69
|
+
def find_in_hash(hash, where)
|
70
|
+
what = hash
|
71
|
+
where.each do |x|
|
72
|
+
return nil unless what.is_a?(Hash)
|
73
|
+
what = what[x]
|
74
|
+
end
|
75
|
+
|
76
|
+
what
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require "vmc/plugin"
|
4
|
+
require "manifests-vmc-plugin"
|
5
|
+
|
6
|
+
|
7
|
+
class ManifestsPlugin < VMC::App::Base
|
8
|
+
include VMCManifests
|
9
|
+
|
10
|
+
option :manifest, :aliases => "-m", :value => :file,
|
11
|
+
:desc => "Path to manifest file to use"
|
12
|
+
|
13
|
+
|
14
|
+
[ :start, :restart, :instances, :logs, :env, :health, :stats,
|
15
|
+
:scale, :app, :stop, :delete
|
16
|
+
].each do |wrap|
|
17
|
+
name_made_optional = change_argument(wrap, :app, :optional)
|
18
|
+
|
19
|
+
around(wrap) do |cmd, input|
|
20
|
+
wrap_with_optional_name(name_made_optional, cmd, input)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
add_input :push, :reset, :desc => "Reset to values in the manifest",
|
26
|
+
:default => false
|
27
|
+
|
28
|
+
around(:push) do |push, input|
|
29
|
+
wrap_push(push, input)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def wrap_with_optional_name(name_made_optional, cmd, input)
|
35
|
+
return cmd.call if input[:all]
|
36
|
+
|
37
|
+
unless manifest
|
38
|
+
# if the command knows how to handle this
|
39
|
+
if input.has?(:app) || !name_made_optional
|
40
|
+
return cmd.call
|
41
|
+
else
|
42
|
+
return no_apps
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
internal, external = apps_in_manifest(input)
|
47
|
+
|
48
|
+
return cmd.call if internal.empty? && !external.empty?
|
49
|
+
|
50
|
+
show_manifest_usage
|
51
|
+
|
52
|
+
if internal.empty? && external.empty?
|
53
|
+
internal = current_apps if internal.empty?
|
54
|
+
internal = all_apps if internal.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
internal = internal.collect { |app| app[:name] }
|
58
|
+
|
59
|
+
apps = internal + external
|
60
|
+
return no_apps if apps.empty?
|
61
|
+
|
62
|
+
apps.each.with_index do |app, num|
|
63
|
+
line unless quiet? || num == 0
|
64
|
+
cmd.call(input.without(:apps).merge_given(:app => app))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def apply_changes(app, input)
|
69
|
+
app.memory = megabytes(input[:memory]) if input.has?(:memory)
|
70
|
+
app.total_instances = input[:instances] if input.has?(:instances)
|
71
|
+
app.command = input[:command] if input.has?(:command)
|
72
|
+
app.production = input[:plan].upcase.start_with?("P") if input.has?(:plan)
|
73
|
+
app.framework = input[:framework] if input.has?(:framework)
|
74
|
+
app.runtime = input[:runtime] if input.has?(:runtime)
|
75
|
+
app.buildpack = input[:buildpack] if input.has?(:buildpack)
|
76
|
+
end
|
77
|
+
|
78
|
+
def wrap_push(push, input)
|
79
|
+
unless manifest
|
80
|
+
create_and_save_manifest(push, input)
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
particular, external = apps_in_manifest(input)
|
85
|
+
|
86
|
+
unless external.empty?
|
87
|
+
fail "Could not find #{b(external.join(", "))}' in the manifest."
|
88
|
+
end
|
89
|
+
|
90
|
+
apps = particular.empty? ? all_apps : particular
|
91
|
+
|
92
|
+
show_manifest_usage
|
93
|
+
|
94
|
+
spaced(apps) do |app_manifest|
|
95
|
+
push_with_manifest(app_manifest, push, input)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def push_with_manifest(app_manifest, push, input)
|
100
|
+
with_filters(
|
101
|
+
:push => {
|
102
|
+
:create_app => proc { |a|
|
103
|
+
setup_env(a, app_manifest)
|
104
|
+
a
|
105
|
+
},
|
106
|
+
:push_app => proc { |a|
|
107
|
+
setup_services(a, app_manifest)
|
108
|
+
a
|
109
|
+
}
|
110
|
+
}) do
|
111
|
+
app_input = push_input_for(app_manifest, input)
|
112
|
+
|
113
|
+
push.call(app_input)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def push_input_for(app_manifest, input)
|
118
|
+
existing_app = client.app_by_name(app_manifest[:name])
|
119
|
+
rebased_input = input.rebase_given(app_manifest)
|
120
|
+
|
121
|
+
if !existing_app || input[:reset]
|
122
|
+
input = rebased_input
|
123
|
+
else
|
124
|
+
warn_reset_changes if manifest_differs?(existing_app, rebased_input)
|
125
|
+
end
|
126
|
+
|
127
|
+
input.merge(
|
128
|
+
:path => from_manifest(app_manifest[:path]),
|
129
|
+
:name => app_manifest[:name],
|
130
|
+
:bind_services => false,
|
131
|
+
:create_services => false)
|
132
|
+
end
|
133
|
+
|
134
|
+
def manifest_differs?(app, input)
|
135
|
+
apply_changes(app, input)
|
136
|
+
app.changed?
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_and_save_manifest(push, input)
|
140
|
+
with_filters(
|
141
|
+
:push => { :push_app => proc { |a| ask_to_save(input, a); a } }) do
|
142
|
+
push.call
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|