appengine-tools 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/appengine-tools/appcfg.rb +62 -10
- data/lib/appengine-tools/boot.rb +28 -5
- data/lib/appengine-tools/bundler.rb +87 -52
- data/lib/appengine-tools/dev_appserver.rb +50 -14
- data/lib/appengine-tools/update_check.rb +118 -0
- data/spec/appengine-web.xml +8 -1
- data/spec/config.ru +3 -0
- data/spec/update_check_spec.rb +176 -0
- metadata +4 -2
data/Rakefile
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
require 'appengine-sdk'
|
18
18
|
require 'appengine-tools/boot'
|
19
19
|
require 'appengine-tools/bundler'
|
20
|
+
require 'appengine-tools/update_check'
|
20
21
|
require 'yaml'
|
21
22
|
|
22
23
|
module AppEngine
|
@@ -26,6 +27,9 @@ module AppEngine
|
|
26
27
|
import Java.ComGoogleAppengineToolsAdmin.AppAdminFactory
|
27
28
|
import Java.ComGoogleAppengineToolsAdmin.AppVersionUpload
|
28
29
|
import Java.ComGoogleAppengineToolsUtil.Logging
|
30
|
+
import java.io.FileOutputStream
|
31
|
+
import java.util.jar.JarOutputStream
|
32
|
+
import java.util.zip.ZipEntry
|
29
33
|
|
30
34
|
class JRubyAppVersionUpload < AppVersionUpload
|
31
35
|
def initialize(connection, app)
|
@@ -88,12 +92,16 @@ EOF
|
|
88
92
|
return
|
89
93
|
end
|
90
94
|
if command && ! NO_XML_COMMANDS.include?(command)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
95
|
+
path = parsed_args[0]
|
96
|
+
AppEngine::Admin.bundle_app(path)
|
97
|
+
AppEngine::Development.boot_jruby
|
98
|
+
updater = AppEngine::Admin::UpdateCheck.new(path)
|
99
|
+
updater.nag
|
100
|
+
else
|
101
|
+
AppEngine::Development.boot_jruby
|
96
102
|
end
|
103
|
+
puts "running AppCfg"
|
104
|
+
run_appcfg(args)
|
97
105
|
end
|
98
106
|
|
99
107
|
def run_appcfg(args)
|
@@ -119,27 +127,33 @@ EOF
|
|
119
127
|
end
|
120
128
|
|
121
129
|
def gem(*args)
|
130
|
+
AppEngine::Admin.bundle_deps('.')
|
122
131
|
AppEngine::Development.boot_jruby
|
132
|
+
puts "=> Running RubyGems"
|
123
133
|
require 'rubygems'
|
124
134
|
require 'rubygems/command_manager'
|
125
135
|
Gem.configuration = Gem::ConfigFile.new(args)
|
126
136
|
Gem.use_paths('.gems')
|
127
137
|
Gem::Command.add_specific_extra_args(
|
128
138
|
'install', %w(--no-rdoc --no-ri))
|
139
|
+
saved_gems = all_gem_specs
|
129
140
|
begin
|
130
141
|
Gem::CommandManager.instance.run(Gem.configuration.args)
|
131
142
|
rescue Gem::SystemExitException => e
|
132
143
|
exit e.exit_code unless e.exit_code == 0
|
133
144
|
end
|
134
|
-
unless
|
145
|
+
unless ((all_gem_specs == saved_gems) &&
|
146
|
+
File.exist?('WEB-INF/lib/gems.jar'))
|
135
147
|
Dir.mkdir 'WEB-INF' unless File.exists? 'WEB-INF'
|
136
148
|
Dir.mkdir 'WEB-INF/lib' unless File.exists? 'WEB-INF/lib'
|
137
149
|
Dir.chdir '.gems'
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
150
|
+
puts '=> Packaging gems'
|
151
|
+
write_jar('../WEB-INF/lib/gems.jar') do |jar|
|
152
|
+
add_to_jar('gems', jar)
|
153
|
+
add_to_jar('specifications', jar)
|
142
154
|
end
|
155
|
+
puts 'If you upgraded any gems, please run `appcfg.rb gem cleanup`'
|
156
|
+
puts 'to remove the old versions.'
|
143
157
|
end
|
144
158
|
end
|
145
159
|
|
@@ -154,7 +168,45 @@ Must be run from the application directory.
|
|
154
168
|
EOF
|
155
169
|
end
|
156
170
|
|
171
|
+
def all_gem_specs
|
172
|
+
Gem.source_index.map do |_, spec|
|
173
|
+
spec
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def write_jar(filename)
|
178
|
+
filename = File.expand_path(filename)
|
179
|
+
jar = JarOutputStream.new(FileOutputStream.new(filename))
|
180
|
+
begin
|
181
|
+
yield jar
|
182
|
+
ensure
|
183
|
+
jar.close
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_to_jar(path, jar)
|
188
|
+
directory = File.directory?(path)
|
189
|
+
if directory
|
190
|
+
path << '/'
|
191
|
+
end
|
192
|
+
entry = ZipEntry.new(path)
|
193
|
+
jar.put_next_entry(entry)
|
194
|
+
unless directory
|
195
|
+
data = IO.read(path)
|
196
|
+
jar.write(data.to_java_bytes, 0, data.size)
|
197
|
+
end
|
198
|
+
jar.close_entry
|
199
|
+
if directory
|
200
|
+
Dir.entries(path).each do |filename|
|
201
|
+
unless ['.', '..'].include?(filename)
|
202
|
+
add_to_jar(path + filename, jar)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
157
208
|
def run(*args)
|
209
|
+
AppEngine::Admin.bundle_deps('.')
|
158
210
|
AppEngine::Development.boot_app('.', args)
|
159
211
|
end
|
160
212
|
|
data/lib/appengine-tools/boot.rb
CHANGED
@@ -20,7 +20,7 @@ module AppEngine
|
|
20
20
|
module Development
|
21
21
|
|
22
22
|
class << self
|
23
|
-
def boot_jruby(root=nil)
|
23
|
+
def boot_jruby(root=nil, options={})
|
24
24
|
unless defined?(JRUBY_VERSION)
|
25
25
|
require 'rubygems'
|
26
26
|
require 'appengine-jruby-jars'
|
@@ -31,8 +31,19 @@ module AppEngine
|
|
31
31
|
AppEngine::SDK::TOOLS_JAR,
|
32
32
|
]
|
33
33
|
|
34
|
+
args = options[:args] || ARGV
|
35
|
+
should_exec = options[:exec]
|
36
|
+
should_exec ||= should_exec.nil?
|
37
|
+
|
34
38
|
ENV['GEM_HOME'] = Gem.dir
|
35
|
-
|
39
|
+
ENV['GEM_PATH'] = Gem.path.compact.join(File::SEPARATOR)
|
40
|
+
appcfg = [File.expand_path(File.join(File.dirname($0),
|
41
|
+
'appcfg.rb'))]
|
42
|
+
if should_exec
|
43
|
+
exec_jruby(root, jars, appcfg + args)
|
44
|
+
else
|
45
|
+
run_jruby(root, jars, appcfg + args)
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
38
49
|
|
@@ -48,16 +59,28 @@ module AppEngine
|
|
48
59
|
exec_jruby(root, jars, jruby_args)
|
49
60
|
end
|
50
61
|
|
51
|
-
def
|
62
|
+
def build_command(root, jars, args)
|
52
63
|
app_jars = root ? Dir.glob("#{root}/WEB-INF/lib/*.jar") : []
|
53
64
|
classpath = (app_jars + jars).join(File::PATH_SEPARATOR)
|
54
65
|
utf = "-Dfile.encoding=UTF-8"
|
55
|
-
|
66
|
+
command = %W(java #{utf} -cp #{classpath} org.jruby.Main) + args
|
56
67
|
if ENV['VERBOSE']
|
57
|
-
puts
|
68
|
+
puts command.map {|a| a.inspect}.join(' ')
|
58
69
|
end
|
70
|
+
command
|
71
|
+
end
|
72
|
+
|
73
|
+
def exec_jruby(root, jars, args)
|
74
|
+
java_command = build_command(root, jars, args)
|
59
75
|
exec *java_command
|
60
76
|
end
|
77
|
+
|
78
|
+
def run_jruby(root, jars, args)
|
79
|
+
java_command = build_command(root, jars, args)
|
80
|
+
if !system(*java_command)
|
81
|
+
puts 'Error executing jruby'
|
82
|
+
end
|
83
|
+
end
|
61
84
|
end
|
62
85
|
end
|
63
86
|
end
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
18
|
require 'appengine-rack'
|
19
|
+
require 'appengine-tools/boot'
|
19
20
|
require 'appengine-tools/web-xml'
|
20
21
|
require 'appengine-tools/xml-formatter'
|
21
22
|
require 'fileutils'
|
@@ -78,10 +79,10 @@ module AppEngine
|
|
78
79
|
end
|
79
80
|
|
80
81
|
class AppBundler
|
81
|
-
EXISTING_JRUBY = /^appengine-jruby-.*jar$/
|
82
|
+
EXISTING_JRUBY = /^(jruby-abridged|appengine-jruby)-.*jar$/
|
82
83
|
EXISTING_RACK = /jruby-rack.*jar$/
|
83
84
|
EXISTING_APIS = /^appengine-api.*jar$/
|
84
|
-
JRUBY_RACK = 'jruby-rack-0.9.
|
85
|
+
JRUBY_RACK = 'jruby-rack-0.9.5.jar'
|
85
86
|
JRUBY_RACK_URL = 'http://kenai.com/projects/jruby-rack/' +
|
86
87
|
"downloads/download/#{JRUBY_RACK}"
|
87
88
|
RACKUP = %q{Dir.chdir('..') if Dir.pwd =~ /WEB-INF$/;} +
|
@@ -92,11 +93,15 @@ module AppEngine
|
|
92
93
|
end
|
93
94
|
|
94
95
|
def bundle
|
96
|
+
bundle_deps
|
97
|
+
convert_config_ru
|
98
|
+
end
|
99
|
+
|
100
|
+
def bundle_deps
|
95
101
|
create_webinf
|
96
102
|
copy_jruby
|
97
103
|
copy_rack
|
98
104
|
copy_sdk
|
99
|
-
convert_config_ru
|
100
105
|
end
|
101
106
|
|
102
107
|
def app
|
@@ -117,7 +122,6 @@ module AppEngine
|
|
117
122
|
end
|
118
123
|
|
119
124
|
def convert_config_ru
|
120
|
-
AppEngine::Development.boot_jruby(app.root)
|
121
125
|
if !File.exists?(app.config_ru)
|
122
126
|
if File.exists?(app.web_xml)
|
123
127
|
unless File.exists?(app.aeweb_xml)
|
@@ -137,22 +141,14 @@ module AppEngine
|
|
137
141
|
end
|
138
142
|
|
139
143
|
def copy_jruby
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
FileUtils.cp([AppEngine::JRubyJars.jruby_jar,
|
145
|
-
AppEngine::JRubyJars.rubygems_jar],
|
146
|
-
app.webinf_lib)
|
147
|
-
end
|
148
|
-
# TODO else warn if out of date
|
144
|
+
require 'appengine-jruby-jars'
|
145
|
+
update_jars(
|
146
|
+
"JRuby", EXISTING_JRUBY, [AppEngine::JRubyJars.jruby_jar],
|
147
|
+
/rubygems/, [AppEngine::JRubyJars.rubygems_jar])
|
149
148
|
end
|
150
149
|
|
151
150
|
def copy_rack
|
152
|
-
|
153
|
-
if current_rack.empty?
|
154
|
-
# TODO cache this somewhere
|
155
|
-
puts "=> Retrieving jruby-rack"
|
151
|
+
update_jars('jruby-rack', EXISTING_RACK, [JRUBY_RACK]) do
|
156
152
|
require 'open-uri'
|
157
153
|
open(JRUBY_RACK_URL) do |src|
|
158
154
|
open(File.join(app.webinf_lib, JRUBY_RACK), 'wb') do |dest|
|
@@ -163,15 +159,10 @@ module AppEngine
|
|
163
159
|
end
|
164
160
|
|
165
161
|
def copy_sdk
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
jars = Dir.glob(
|
171
|
-
"#{AppEngine::SDK::SDK_ROOT}/lib/user/appengine-api*.jar")
|
172
|
-
# TODO if there's more than 1 we need to check the api version.
|
173
|
-
FileUtils.cp(jars[0], app.webinf_lib)
|
174
|
-
end
|
162
|
+
require 'appengine-sdk'
|
163
|
+
glob = "appengine-api-1.0-sdk-*.jar"
|
164
|
+
jars = Dir.glob("#{AppEngine::SDK::SDK_ROOT}/lib/user/#{glob}")
|
165
|
+
update_jars('appengine-sdk', EXISTING_APIS, jars)
|
175
166
|
end
|
176
167
|
|
177
168
|
private
|
@@ -180,6 +171,40 @@ module AppEngine
|
|
180
171
|
Dir.entries(app.webinf_lib).grep(regex) rescue []
|
181
172
|
end
|
182
173
|
|
174
|
+
def update_jars(name, regex, jars, opt_regex=nil, opt_jars=[])
|
175
|
+
existing = find_jars(regex)
|
176
|
+
if existing.empty?
|
177
|
+
message = "Installing #{name}"
|
178
|
+
jars_to_install = jars + opt_jars
|
179
|
+
else
|
180
|
+
has_optional_jars = existing.any? {|j| j =~ opt_regex}
|
181
|
+
expected_jars = jars
|
182
|
+
expected_jars.concat(opt_jars) if has_optional_jars
|
183
|
+
expected = expected_jars.map {|path| File.basename(path)}
|
184
|
+
if existing.size != expected.size ||
|
185
|
+
(expected & existing) != expected
|
186
|
+
message = "Updating #{name}"
|
187
|
+
jars_to_install = expected_jars
|
188
|
+
end
|
189
|
+
end
|
190
|
+
if jars_to_install
|
191
|
+
puts message
|
192
|
+
remove_jars(existing)
|
193
|
+
if block_given?
|
194
|
+
yield
|
195
|
+
else
|
196
|
+
FileUtils.cp(jars_to_install, app.webinf_lib)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def remove_jars(jars)
|
202
|
+
paths = jars.map do |jar|
|
203
|
+
"#{app.webinf_lib}/#{jar}"
|
204
|
+
end
|
205
|
+
FileUtils.rm_f(paths)
|
206
|
+
end
|
207
|
+
|
183
208
|
def valid_build
|
184
209
|
return false unless File.exists? app.build_status
|
185
210
|
return false unless File.exists? app.web_xml
|
@@ -193,41 +218,51 @@ module AppEngine
|
|
193
218
|
end
|
194
219
|
|
195
220
|
def generate_xml
|
196
|
-
return if valid_build
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
221
|
+
return if valid_build
|
222
|
+
if defined? JRUBY_VERSION
|
223
|
+
puts "=> Generating configuration files"
|
224
|
+
Dir.glob("#{app.webinf_lib}/*.jar").each do |path|
|
225
|
+
$: << path
|
226
|
+
end
|
227
|
+
app_root = app.root
|
228
|
+
builder = WebXmlBuilder.new do
|
229
|
+
# First configure the basic jruby-rack settings.
|
230
|
+
add_jruby_rack_defaults(RACKUP)
|
205
231
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
232
|
+
# Now read the user's rackup file
|
233
|
+
# TODO generate a skeleton if it's missing
|
234
|
+
Dir.chdir(app_root) do
|
235
|
+
eval IO.read('config.ru'), nil, 'config.ru', 1
|
236
|
+
end
|
210
237
|
end
|
238
|
+
open(app.web_xml, 'w') do |webxml|
|
239
|
+
xml = AppEngine::Rack::XmlFormatter.format(builder.to_xml)
|
240
|
+
webxml.write(xml)
|
241
|
+
end
|
242
|
+
open(app.aeweb_xml, 'w') do |aeweb|
|
243
|
+
xml = AppEngine::Rack::XmlFormatter.format(app.rack_app.to_xml)
|
244
|
+
aeweb.write(xml)
|
245
|
+
end
|
246
|
+
yaml = {
|
247
|
+
:config_ru => File.stat(app.config_ru).mtime,
|
248
|
+
:aeweb_xml => File.stat(app.aeweb_xml).mtime,
|
249
|
+
:web_xml => File.stat(app.web_xml).mtime }
|
250
|
+
open(app.build_status, 'w') { |f| YAML.dump(yaml, f) }
|
251
|
+
else
|
252
|
+
AppEngine::Development.boot_jruby(app.root,
|
253
|
+
:args => ['bundle', app.root],
|
254
|
+
:exec => false)
|
211
255
|
end
|
212
|
-
open(app.web_xml, 'w') do |webxml|
|
213
|
-
xml = AppEngine::Rack::XmlFormatter.format(builder.to_xml)
|
214
|
-
webxml.write(xml)
|
215
|
-
end
|
216
|
-
open(app.aeweb_xml, 'w') do |aeweb|
|
217
|
-
xml = AppEngine::Rack::XmlFormatter.format(app.rack_app.to_xml)
|
218
|
-
aeweb.write(xml)
|
219
|
-
end
|
220
|
-
yaml = {
|
221
|
-
:config_ru => File.stat(app.config_ru).mtime,
|
222
|
-
:aeweb_xml => File.stat(app.aeweb_xml).mtime,
|
223
|
-
:web_xml => File.stat(app.web_xml).mtime }
|
224
|
-
open(app.build_status, 'w') { |f| YAML.dump(yaml, f) }
|
225
256
|
end
|
226
257
|
end
|
227
258
|
|
228
259
|
def self.bundle_app(root_path)
|
229
260
|
AppBundler.new(root_path).bundle
|
230
261
|
end
|
262
|
+
|
263
|
+
def self.bundle_deps(root_path)
|
264
|
+
AppBundler.new(root_path).bundle_deps
|
265
|
+
end
|
231
266
|
|
232
267
|
end
|
233
268
|
end
|
@@ -17,29 +17,65 @@
|
|
17
17
|
require 'appengine-sdk'
|
18
18
|
require 'appengine-tools/boot'
|
19
19
|
require 'appengine-tools/bundler'
|
20
|
+
require 'appengine-tools/update_check'
|
20
21
|
|
21
22
|
module AppEngine
|
22
23
|
module Development
|
23
|
-
DEV_APPSERVER = 'com.google.appengine.tools.development.DevAppServerMain'
|
24
|
+
DEV_APPSERVER = ['com.google.appengine.tools.development.DevAppServerMain']
|
24
25
|
class JRubyDevAppserver
|
25
26
|
class << self
|
26
27
|
def run(args)
|
27
|
-
path
|
28
|
-
if
|
29
|
-
|
30
|
-
|
31
|
-
puts "=> Press Ctrl-C to shutdown server"
|
32
|
-
ENV['BOOTING_DEVAPPSERVER'] = 'true'
|
33
|
-
end
|
28
|
+
path, java_args, server_args = parse_argv(ARGV)
|
29
|
+
if path and File::directory?(path)
|
30
|
+
puts "=> Booting DevAppServer"
|
31
|
+
puts "=> Press Ctrl-C to shutdown server"
|
34
32
|
AppEngine::Admin.bundle_app(path)
|
33
|
+
updater = AppEngine::Admin::UpdateCheck.new(path)
|
34
|
+
updater.nag if updater.can_nag?
|
35
|
+
end
|
36
|
+
start_java(path, java_args, server_args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_argv(argv)
|
40
|
+
java_args = []
|
41
|
+
server_args = ['--disable_update_check']
|
42
|
+
start_on_first_thread = false
|
43
|
+
if RUBY_PLATFORM =~ /darwin/ ||
|
44
|
+
(RUBY_PLATFORM == 'java' &&
|
45
|
+
java.lang.System.getProperty("os.name").downcase == "mac os x")
|
46
|
+
start_on_first_thread = true
|
47
|
+
end
|
48
|
+
argv.each do |arg|
|
49
|
+
if arg =~ /^--jvm_flag=(.+)/
|
50
|
+
java_args << $1
|
51
|
+
elsif arg =~ /--startOnFirstThread=(true|false)/
|
52
|
+
start_on_first_thread = ($1 == "true")
|
53
|
+
else
|
54
|
+
server_args << arg
|
55
|
+
end
|
56
|
+
end
|
57
|
+
if start_on_first_thread
|
58
|
+
java_args << '-XstartOnFirstThread'
|
59
|
+
end
|
60
|
+
return server_args[-1], java_args, server_args
|
61
|
+
end
|
62
|
+
|
63
|
+
def start_java(path, java_args, server_args)
|
64
|
+
if path
|
65
|
+
jruby_home = get_jruby_home(path)
|
66
|
+
java_args << "-Djruby.home=#{jruby_home}" if jruby_home
|
67
|
+
server_args[-1] = File.expand_path(path)
|
68
|
+
Dir.chdir(path)
|
35
69
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
70
|
+
ENV.delete 'GEM_HOME'
|
71
|
+
ENV.delete 'GEM_PATH'
|
72
|
+
java_args << '-classpath'
|
73
|
+
java_args << AppEngine::SDK::TOOLS_JAR
|
74
|
+
command = ['java'] + java_args + DEV_APPSERVER + server_args
|
75
|
+
if ENV['VERBOSE']
|
76
|
+
puts "exec #{command.map{|x| x.inspect}.join(' ')}"
|
41
77
|
end
|
42
|
-
|
78
|
+
exec(*command)
|
43
79
|
end
|
44
80
|
|
45
81
|
def get_jruby_home(path)
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/ruby1.8 -w
|
2
|
+
#
|
3
|
+
# Copyright:: Copyright 2009 Google Inc.
|
4
|
+
# Original Author:: Ryan Brown (mailto:ribrdb@google.com)
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'yaml'
|
19
|
+
require 'open-uri'
|
20
|
+
begin
|
21
|
+
require 'google-appengine'
|
22
|
+
rescue LoadError
|
23
|
+
end
|
24
|
+
require 'rubygems/version'
|
25
|
+
|
26
|
+
module AppEngine
|
27
|
+
module Admin
|
28
|
+
class UpdateCheck
|
29
|
+
NAG_FILE = "~/.appcfg_rb_nag"
|
30
|
+
MAX_NAG_FREQUENCY = 60 * 60 * 24 * 7
|
31
|
+
DEFAULT_URL = 'http://appengine-jruby.googlecode.com/hg/updatecheck'
|
32
|
+
|
33
|
+
def initialize(approot, url=nil, nag_file=nil)
|
34
|
+
@url = url || DEFAULT_URL
|
35
|
+
@approot = approot
|
36
|
+
@nag_file = nag_file || File.expand_path(NAG_FILE)
|
37
|
+
end
|
38
|
+
|
39
|
+
def local_versions
|
40
|
+
sdk_version = Gem::Version.new(AppEngine::VERSION) rescue nil
|
41
|
+
{
|
42
|
+
'google-appengine' => sdk_version,
|
43
|
+
'appengine-apis' => find_gem_version('appengine-apis'),
|
44
|
+
'dm-appengine' => find_gem_version('dm-appengine'),
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_gem_version(name)
|
49
|
+
specs = Dir.glob("#{@approot}/.gems/specifications/#{name}-*.gemspec")
|
50
|
+
versions = specs.map do |filename|
|
51
|
+
if filename =~ /-(\w+\.\w+.\w+).gemspec$/
|
52
|
+
Gem::Version.new($1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
versions.sort[-1]
|
56
|
+
end
|
57
|
+
|
58
|
+
def remote_versions
|
59
|
+
versions = YAML.load(open(@url).read)
|
60
|
+
versions.inject({}) do |versions, (name, version)|
|
61
|
+
versions[name] = Gem::Version.new(version)
|
62
|
+
versions
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def check_for_updates
|
67
|
+
local = local_versions
|
68
|
+
unless local['google-appengine']
|
69
|
+
puts 'Skipping update check'
|
70
|
+
return
|
71
|
+
end
|
72
|
+
latest = remote_versions
|
73
|
+
local.each do |name, version|
|
74
|
+
current = latest[name]
|
75
|
+
if version && version < current
|
76
|
+
prefix = if name == 'google-appengine'
|
77
|
+
"sudo"
|
78
|
+
else
|
79
|
+
"appcfg.rb"
|
80
|
+
end
|
81
|
+
puts "There is a new version of #{name}: #{current} " +
|
82
|
+
"(You have #{version})"
|
83
|
+
puts "Please run #{prefix} gem update #{name}."
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_nag_file
|
89
|
+
@nag ||= if File.exist?(@nag_file)
|
90
|
+
YAML.load_file(@nag_file)
|
91
|
+
else
|
92
|
+
{'opt_in' => true, 'timestamp' => 0}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def write_nag_file(options)
|
97
|
+
open(@nag_file, 'w') do |file|
|
98
|
+
file.write(YAML.dump(options))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def nag
|
103
|
+
nag = parse_nag_file
|
104
|
+
return if Time.now.to_i - nag['timestamp'] < MAX_NAG_FREQUENCY
|
105
|
+
check_for_updates
|
106
|
+
nag['timestamp'] = Time.now.to_i
|
107
|
+
write_nag_file(nag)
|
108
|
+
rescue OpenURI::HTTPError, Errno::ENOENT
|
109
|
+
# check_for_updates will raise an error if we're not online
|
110
|
+
# just ignore it, and don't update the nag timestamp.
|
111
|
+
end
|
112
|
+
|
113
|
+
def can_nag?
|
114
|
+
parse_nag_file['opt_in']
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/spec/appengine-web.xml
CHANGED
@@ -2,11 +2,18 @@
|
|
2
2
|
<application>tools-test</application>
|
3
3
|
<version>foo</version>
|
4
4
|
<public-root>/public</public-root>
|
5
|
-
<static-files
|
5
|
+
<static-files>
|
6
|
+
<include expiration='365d' path='/public/images/**'/>
|
7
|
+
<include path='/public/**'/>
|
8
|
+
</static-files>
|
6
9
|
<resource-files/>
|
7
10
|
<system-properties>
|
8
11
|
<property name='os.arch' value=''/>
|
9
12
|
<property name='jruby.management.enabled' value='false'/>
|
13
|
+
<property name='jruby.rack.input.rewindable' value='false'/>
|
10
14
|
</system-properties>
|
11
15
|
<ssl-enabled>true</ssl-enabled>
|
16
|
+
<inbound-services>
|
17
|
+
<service>xmpp_message</service>
|
18
|
+
</inbound-services>
|
12
19
|
</appengine-web-app>
|
data/spec/config.ru
CHANGED
@@ -2,6 +2,9 @@ require 'appengine-rack'
|
|
2
2
|
|
3
3
|
AppEngine::Rack.configure_app(
|
4
4
|
:application => 'tools-test', :version => 'foo', :ssl_enabled => true)
|
5
|
+
AppEngine::Rack.app.inbound_services << :xmpp_message
|
6
|
+
AppEngine::Rack.app.static_files.include('/public/images/**', '365d')
|
7
|
+
AppEngine::Rack.app.static_files.include('/public/**')
|
5
8
|
|
6
9
|
ruby_app = lambda {|env| [200, {}, "Hello Rack!"]}
|
7
10
|
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require 'appengine-tools/update_check'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module AppEngine
|
6
|
+
VERSION = "3.2.1"
|
7
|
+
end
|
8
|
+
|
9
|
+
describe AppEngine::Admin::UpdateCheck do
|
10
|
+
before :each do
|
11
|
+
@update_check = AppEngine::Admin::UpdateCheck.new('approot', 'url', 'nag')
|
12
|
+
end
|
13
|
+
|
14
|
+
def versions(sdk, apis, dm)
|
15
|
+
keys = ['google-appengine', 'appengine-apis', 'dm-appengine']
|
16
|
+
hash = {}
|
17
|
+
keys.zip([sdk, apis, dm]) do |key, value|
|
18
|
+
hash[key] = value && Gem::Version.new(value)
|
19
|
+
end
|
20
|
+
hash
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should expand path' do
|
24
|
+
File.should_receive(:expand_path).with(
|
25
|
+
AppEngine::Admin::UpdateCheck::NAG_FILE)
|
26
|
+
uc = AppEngine::Admin::UpdateCheck.new('/')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should support local_versions' do
|
30
|
+
expected = versions('3.2.1', '1.2.3', nil)
|
31
|
+
@update_check.should_receive(:find_gem_version).twice.and_return(
|
32
|
+
Gem::Version.new("1.2.3"), nil)
|
33
|
+
@update_check.local_versions.should == expected
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should support find_gem_version' do
|
37
|
+
Dir.should_receive(:glob).and_return(
|
38
|
+
['appengine-apis-0.0.20.gemspec','appengine-apis-0.0.3.gemspec'])
|
39
|
+
@update_check.find_gem_version("appengine-apis").should ==
|
40
|
+
Gem::Version.new('0.0.20')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should support remote_versions' do
|
44
|
+
expected = versions('3.2.2', '1.2.4', '2.3.4')
|
45
|
+
io = StringIO.new(YAML.dump(expected))
|
46
|
+
@update_check.stub!(:open)
|
47
|
+
@update_check.should_receive(:open).with("url").and_return(io)
|
48
|
+
@update_check.remote_versions.should == expected
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'check_for_updates' do
|
52
|
+
before :each do
|
53
|
+
@update_check.stub!(:puts)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def expect_puts(*messages)
|
58
|
+
length = messages.length
|
59
|
+
@update_check.should_receive(:puts) do |arg|
|
60
|
+
arg.should == messages.shift
|
61
|
+
end.exactly(length).times
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should skip check if no sdk version' do
|
65
|
+
@update_check.should_receive(:local_versions).and_return(
|
66
|
+
versions(nil, nil, nil))
|
67
|
+
expect_puts("Skipping update check")
|
68
|
+
@update_check.check_for_updates
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should warn about old sdk' do
|
72
|
+
@update_check.should_receive(:local_versions).and_return(
|
73
|
+
versions('0.0.1', nil, nil))
|
74
|
+
@update_check.should_receive(:remote_versions).and_return(
|
75
|
+
versions('0.0.2', '0.0.2', '0.0.2'))
|
76
|
+
expect_puts(
|
77
|
+
"There is a new version of google-appengine: 0.0.2 (You have 0.0.1)",
|
78
|
+
"Please run sudo gem update google-appengine."
|
79
|
+
)
|
80
|
+
@update_check.check_for_updates
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should warn about old apis' do
|
84
|
+
@update_check.should_receive(:local_versions).and_return(
|
85
|
+
versions('0.0.2', '0.0.2', '0.0.2'))
|
86
|
+
@update_check.should_receive(:remote_versions).and_return(
|
87
|
+
versions('0.0.2', '0.0.4', '0.0.2'))
|
88
|
+
expect_puts(
|
89
|
+
"There is a new version of appengine-apis: 0.0.4 (You have 0.0.2)",
|
90
|
+
"Please run appcfg.rb gem update appengine-apis."
|
91
|
+
)
|
92
|
+
@update_check.check_for_updates
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should warn about old dm adapter' do
|
96
|
+
@update_check.should_receive(:local_versions).and_return(
|
97
|
+
versions('0.0.2', '0.0.2', '0.0.2'))
|
98
|
+
@update_check.should_receive(:remote_versions).and_return(
|
99
|
+
versions('0.0.2', '0.0.2', '0.0.3'))
|
100
|
+
expect_puts(
|
101
|
+
"There is a new version of dm-appengine: 0.0.3 (You have 0.0.2)",
|
102
|
+
"Please run appcfg.rb gem update dm-appengine."
|
103
|
+
)
|
104
|
+
@update_check.check_for_updates
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
it 'should warn about multiple old gems' do
|
109
|
+
@update_check.should_receive(:local_versions).and_return(
|
110
|
+
versions('0.0.1', '0.0.3', '0.0.5'))
|
111
|
+
@update_check.should_receive(:remote_versions).and_return(
|
112
|
+
versions('0.0.2', '0.0.4', '0.0.5'))
|
113
|
+
expect_puts(
|
114
|
+
"There is a new version of google-appengine: 0.0.2 (You have 0.0.1)",
|
115
|
+
"Please run sudo gem update google-appengine.",
|
116
|
+
"There is a new version of appengine-apis: 0.0.4 (You have 0.0.3)",
|
117
|
+
"Please run appcfg.rb gem update appengine-apis."
|
118
|
+
)
|
119
|
+
@update_check.check_for_updates
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'parse_nag_file' do
|
124
|
+
it 'should support missing file' do
|
125
|
+
File.should_receive(:exist?).with('nag').and_return(false)
|
126
|
+
@update_check.parse_nag_file.should == {
|
127
|
+
'opt_in' => true,
|
128
|
+
'timestamp' => 0
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should parse existing file' do
|
133
|
+
File.should_receive(:exist?).with('nag').and_return(true)
|
134
|
+
YAML.should_receive(:load_file).with('nag').and_return("parsed_yaml")
|
135
|
+
@update_check.parse_nag_file.should == "parsed_yaml"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'nag' do
|
140
|
+
it 'should nag if old timestamp' do
|
141
|
+
@update_check.should_receive(:parse_nag_file).and_return({
|
142
|
+
'opt_in' => true,
|
143
|
+
'timestamp' => Time.now.to_i - (60 * 60 * 24 * 8)
|
144
|
+
})
|
145
|
+
@update_check.should_receive(:check_for_updates)
|
146
|
+
@update_check.should_receive(:write_nag_file)
|
147
|
+
@update_check.nag
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should not nag if new timestamp' do
|
151
|
+
@update_check.should_receive(:parse_nag_file).and_return({
|
152
|
+
'opt_in' => true,
|
153
|
+
'timestamp' => Time.now.to_i - (60 * 60 * 24 * 6)
|
154
|
+
})
|
155
|
+
@update_check.nag
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe 'can_nag?' do
|
160
|
+
it 'should return true of opted in' do
|
161
|
+
@update_check.should_receive(:parse_nag_file).and_return({
|
162
|
+
'opt_in' => true,
|
163
|
+
'timestamp' => Time.now.to_i
|
164
|
+
})
|
165
|
+
@update_check.can_nag?.should == true
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should return false of opted out' do
|
169
|
+
@update_check.should_receive(:parse_nag_file).and_return({
|
170
|
+
'opt_in' => false,
|
171
|
+
'timestamp' => Time.now.to_i
|
172
|
+
})
|
173
|
+
@update_check.can_nag?.should == false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appengine-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Brown
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-09-03 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -60,6 +60,7 @@ files:
|
|
60
60
|
- lib/appengine-tools/boot.rb
|
61
61
|
- lib/appengine-tools/bundler.rb
|
62
62
|
- lib/appengine-tools/dev_appserver.rb
|
63
|
+
- lib/appengine-tools/update_check.rb
|
63
64
|
- lib/appengine-tools/web-xml.rb
|
64
65
|
- lib/appengine-tools/xml-formatter.rb
|
65
66
|
- spec/appengine-web.xml
|
@@ -67,6 +68,7 @@ files:
|
|
67
68
|
- spec/rack_spec.rb
|
68
69
|
- spec/spec.opts
|
69
70
|
- spec/spec_helper.rb
|
71
|
+
- spec/update_check_spec.rb
|
70
72
|
- spec/web-xml_spec.rb
|
71
73
|
- spec/web.xml
|
72
74
|
has_rdoc: true
|