mortar 0.7.8 → 0.7.9
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mortar/command/base.rb +75 -0
- data/lib/mortar/command/describe.rb +1 -2
- data/lib/mortar/command/illustrate.rb +1 -2
- data/lib/mortar/command/jobs.rb +2 -2
- data/lib/mortar/command/projects.rb +44 -58
- data/lib/mortar/command/validate.rb +1 -2
- data/lib/mortar/git.rb +126 -33
- data/lib/mortar/project.rb +4 -0
- data/lib/mortar/version.rb +1 -1
- data/spec/mortar/command/describe_spec.rb +23 -0
- data/spec/mortar/command/illustrate_spec.rb +26 -0
- data/spec/mortar/command/jobs_spec.rb +20 -0
- data/spec/mortar/command/projects_spec.rb +53 -1
- data/spec/mortar/command/validate_spec.rb +19 -0
- data/spec/mortar/git_spec.rb +67 -2
- data/spec/spec_helper.rb +10 -0
- metadata +2 -2
data/lib/mortar/command/base.rb
CHANGED
@@ -107,6 +107,65 @@ class Mortar::Command::Base
|
|
107
107
|
end
|
108
108
|
return ""
|
109
109
|
end
|
110
|
+
|
111
|
+
def validate_project_name(name)
|
112
|
+
project_names = api.get_projects().body["projects"].collect{|p| p['name']}
|
113
|
+
if project_names.include? name
|
114
|
+
error("Your account already contains a project named #{name}.\nPlease choose a different name for your new project, or clone the existing #{name} code using:\n\nmortar projects:clone #{name}")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def validate_project_structure()
|
119
|
+
present_dirs = Dir.glob("*").select { |path| File.directory? path }
|
120
|
+
required_dirs = ["controlscripts", "pigscripts", "macros", "udfs", "fixtures"]
|
121
|
+
missing_dirs = required_dirs - present_dirs
|
122
|
+
|
123
|
+
if missing_dirs.length > 0
|
124
|
+
error("Project missing required directories: #{missing_dirs.to_s}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def register_project(name)
|
129
|
+
project_id = nil
|
130
|
+
action("Sending request to register project: #{name}") do
|
131
|
+
project_id = api.post_project(name).body["project_id"]
|
132
|
+
end
|
133
|
+
|
134
|
+
project_result = nil
|
135
|
+
project_status = nil
|
136
|
+
display
|
137
|
+
ticking(polling_interval) do |ticks|
|
138
|
+
project_result = api.get_project(project_id).body
|
139
|
+
project_status = project_result.fetch("status_code", project_result["status"])
|
140
|
+
project_description = project_result.fetch("status_description", project_status)
|
141
|
+
is_finished = Mortar::API::Projects::STATUSES_COMPLETE.include?(project_status)
|
142
|
+
|
143
|
+
redisplay("Status: %s %s" % [
|
144
|
+
project_description + (is_finished ? "" : "..."),
|
145
|
+
is_finished ? " " : spinner(ticks)],
|
146
|
+
is_finished) # only display newline on last message
|
147
|
+
if is_finished
|
148
|
+
display
|
149
|
+
break
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
case project_status
|
154
|
+
when Mortar::API::Projects::STATUS_FAILED
|
155
|
+
error("Project registration failed.\nError message: #{project_result['error_message']}")
|
156
|
+
when Mortar::API::Projects::STATUS_ACTIVE
|
157
|
+
yield project_result
|
158
|
+
else
|
159
|
+
raise RuntimeError, "Unknown project status: #{project_status} for project_id: #{project_id}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def initialize_gitless_project(api_registration_result)
|
164
|
+
File.open(".mortar-project-remote", "w") do |f|
|
165
|
+
f.puts api_registration_result["git_url"]
|
166
|
+
end
|
167
|
+
git.sync_gitless_project(project)
|
168
|
+
end
|
110
169
|
|
111
170
|
protected
|
112
171
|
|
@@ -244,6 +303,12 @@ protected
|
|
244
303
|
end
|
245
304
|
end
|
246
305
|
|
306
|
+
def validate_gitless_project!
|
307
|
+
unless project.root_path
|
308
|
+
error("#{current_command[:command]} must be run from the project root directory")
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
247
312
|
def validate_script!(script_name)
|
248
313
|
pigscript = project.pigscripts[script_name]
|
249
314
|
controlscript = project.controlscripts[script_name]
|
@@ -330,6 +395,16 @@ protected
|
|
330
395
|
(options[:no_browser])
|
331
396
|
end
|
332
397
|
|
398
|
+
def sync_code_with_cloud
|
399
|
+
# returns git_ref
|
400
|
+
if project.gitless_project?
|
401
|
+
return git.sync_gitless_project(project)
|
402
|
+
else
|
403
|
+
validate_git_based_project!
|
404
|
+
return git.create_and_push_snapshot_branch(project)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
333
408
|
end
|
334
409
|
|
335
410
|
module Mortar::Command
|
@@ -47,8 +47,7 @@ class Mortar::Command::Describe < Mortar::Command::Base
|
|
47
47
|
error "Currently Mortar does not support describing control scripts"
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
git_ref = git.create_and_push_snapshot_branch(project)
|
50
|
+
git_ref = sync_code_with_cloud()
|
52
51
|
|
53
52
|
describe_id = nil
|
54
53
|
action("Starting describe") do
|
@@ -51,8 +51,7 @@ class Mortar::Command::Illustrate < Mortar::Command::Base
|
|
51
51
|
error "Currently Mortar does not support illustrating control scripts"
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
git_ref = git.create_and_push_snapshot_branch(project)
|
54
|
+
git_ref = sync_code_with_cloud()
|
56
55
|
|
57
56
|
illustrate_id = nil
|
58
57
|
action("Starting illustrate") do
|
data/lib/mortar/command/jobs.rb
CHANGED
@@ -114,8 +114,8 @@ class Mortar::Command::Jobs < Mortar::Command::Base
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
|
118
|
-
|
117
|
+
git_ref = sync_code_with_cloud()
|
118
|
+
|
119
119
|
notify_on_job_finish = ! options[:donotnotify]
|
120
120
|
|
121
121
|
# post job to API
|
@@ -59,6 +59,9 @@ class Mortar::Command::Projects < Mortar::Command::Base
|
|
59
59
|
# projects:create PROJECTNAME
|
60
60
|
#
|
61
61
|
# Used when you want to start a new Mortar project using Mortar generated code.
|
62
|
+
#
|
63
|
+
# --withoutgit # Create a Mortar project that is not its own git repo. Your code will still be synced with a git repo in the cloud.
|
64
|
+
#
|
62
65
|
def create
|
63
66
|
name = shift_argument
|
64
67
|
unless name
|
@@ -66,82 +69,65 @@ class Mortar::Command::Projects < Mortar::Command::Base
|
|
66
69
|
end
|
67
70
|
|
68
71
|
Mortar::Command::run("generate:project", [name])
|
72
|
+
|
69
73
|
FileUtils.cd(name)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
+
if options[:withoutgit]
|
75
|
+
Mortar::Command::run("projects:register", [name, "--withoutgit"])
|
76
|
+
else
|
77
|
+
git.git_init
|
78
|
+
git.git("add .")
|
79
|
+
git.git("commit -m \"Mortar project scaffolding\"")
|
80
|
+
Mortar::Command::run("projects:register", [name])
|
81
|
+
end
|
74
82
|
end
|
75
83
|
alias_command "new", "projects:create"
|
76
84
|
|
77
85
|
# projects:register PROJECTNAME
|
78
86
|
#
|
79
87
|
# Used when you want to start a new Mortar project using your existing code in the current directory.
|
88
|
+
#
|
89
|
+
# --withoutgit # Register code that is not its own git repo as a Mortar project. Your code will still be synced with a git repo in the cloud.
|
90
|
+
#
|
80
91
|
def register
|
81
92
|
name = shift_argument
|
82
93
|
unless name
|
83
94
|
error("Usage: mortar projects:register PROJECT\nMust specify PROJECT.")
|
84
95
|
end
|
85
96
|
validate_arguments!
|
86
|
-
|
87
|
-
|
97
|
+
|
98
|
+
if options[:withoutgit]
|
99
|
+
validate_project_name(name)
|
100
|
+
validate_project_structure()
|
101
|
+
|
102
|
+
register_project(name) do |project_result|
|
103
|
+
initialize_gitless_project(project_result)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
unless git.has_dot_git?
|
88
107
|
# check if we're in the parent directory
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
108
|
+
if File.exists? name
|
109
|
+
error("mortar projects:register must be run from within the project directory.\nPlease \"cd #{name}\" and rerun this command.")
|
110
|
+
else
|
111
|
+
error("No git repository found in the current directory.\nTo register a project that is not its own git repository, use the --withoutgit option.\nIf you do want this project to be its own git repository, please initialize git in this directory, and then rerun the register command.\nTo initialize your project in git, use:\n\ngit init\ngit add .\ngit commit -a -m \"first commit\"")
|
112
|
+
end
|
93
113
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
begin
|
104
|
-
error("Currently in project: #{project.name}. You can not register a new project inside of an existing mortar project.")
|
105
|
-
rescue Mortar::Command::CommandFailed => cf
|
106
|
-
error("Currently in an existing Mortar project. You can not register a new project inside of an existing mortar project.")
|
114
|
+
|
115
|
+
validate_project_name(name)
|
116
|
+
|
117
|
+
unless git.remotes(git_organization).empty?
|
118
|
+
begin
|
119
|
+
error("Currently in project: #{project.name}. You can not register a new project inside of an existing mortar project.")
|
120
|
+
rescue Mortar::Command::CommandFailed => cf
|
121
|
+
error("Currently in an existing Mortar project. You can not register a new project inside of an existing mortar project.")
|
122
|
+
end
|
107
123
|
end
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
project_result = nil
|
116
|
-
project_status = nil
|
117
|
-
display
|
118
|
-
ticking(polling_interval) do |ticks|
|
119
|
-
project_result = api.get_project(project_id).body
|
120
|
-
project_status = project_result.fetch("status_code", project_result["status"])
|
121
|
-
project_description = project_result.fetch("status_description", project_status)
|
122
|
-
is_finished = Mortar::API::Projects::STATUSES_COMPLETE.include?(project_status)
|
123
|
-
|
124
|
-
redisplay("Status: %s %s" % [
|
125
|
-
project_description + (is_finished ? "" : "..."),
|
126
|
-
is_finished ? " " : spinner(ticks)],
|
127
|
-
is_finished) # only display newline on last message
|
128
|
-
if is_finished
|
129
|
-
display
|
130
|
-
break
|
124
|
+
|
125
|
+
register_project(name) do |project_result|
|
126
|
+
git.remote_add("mortar", project_result['git_url'])
|
127
|
+
git.push_master
|
128
|
+
display "Your project is ready for use. Type 'mortar help' to see the commands you can perform on the project.\n\n"
|
131
129
|
end
|
132
130
|
end
|
133
|
-
|
134
|
-
case project_status
|
135
|
-
when Mortar::API::Projects::STATUS_FAILED
|
136
|
-
error("Project registration failed.\nError message: #{project_result['error_message']}")
|
137
|
-
when Mortar::API::Projects::STATUS_ACTIVE
|
138
|
-
git.remote_add("mortar", project_result['git_url'])
|
139
|
-
git.push_master
|
140
|
-
display "Your project is ready for use. Type 'mortar help' to see the commands you can perform on the project.\n\n"
|
141
|
-
else
|
142
|
-
raise RuntimeError, "Unknown project status: #{project_status} for project_id: #{project_id}"
|
143
|
-
end
|
144
|
-
|
145
131
|
end
|
146
132
|
alias_command "register", "projects:register"
|
147
133
|
|
@@ -42,8 +42,7 @@ class Mortar::Command::Validate < Mortar::Command::Base
|
|
42
42
|
error "Currently Mortar does not support validating control scripts"
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
git_ref = git.create_and_push_snapshot_branch(project)
|
45
|
+
git_ref = sync_code_with_cloud()
|
47
46
|
|
48
47
|
validate_id = nil
|
49
48
|
action("Starting validate") do
|
data/lib/mortar/git.rb
CHANGED
@@ -87,7 +87,7 @@ module Mortar
|
|
87
87
|
raise GitError, "No commits found in repository. You must do an initial commit to initialize the repository."
|
88
88
|
end
|
89
89
|
|
90
|
-
safe_copy(
|
90
|
+
safe_copy(mortar_manifest_pathlist) do
|
91
91
|
did_stash_changes = stash_working_dir("Stash for push to master")
|
92
92
|
git('push mortar master')
|
93
93
|
end
|
@@ -118,27 +118,29 @@ module Mortar
|
|
118
118
|
# Only snapshot filesystem paths that are in a whitelist
|
119
119
|
#
|
120
120
|
|
121
|
-
def
|
121
|
+
def mortar_manifest_pathlist(include_dot_git = true)
|
122
122
|
ensure_valid_mortar_project_manifest()
|
123
123
|
|
124
|
-
|
125
|
-
|
124
|
+
manifest_pathlist = File.read(".mortar-project-manifest").split("\n")
|
125
|
+
if include_dot_git
|
126
|
+
manifest_pathlist << ".git"
|
127
|
+
end
|
126
128
|
|
127
|
-
|
129
|
+
manifest_pathlist.each do |path|
|
128
130
|
unless File.exists? path
|
129
131
|
Helpers.error(".mortar-project-manifest includes file/dir \"#{path}\" that is not in the mortar project directory.")
|
130
132
|
end
|
131
133
|
end
|
132
134
|
|
133
|
-
|
135
|
+
manifest_pathlist
|
134
136
|
end
|
135
137
|
|
136
138
|
#
|
137
139
|
# Create a snapshot whitelist file if it doesn't already exist
|
138
140
|
#
|
139
141
|
def ensure_valid_mortar_project_manifest()
|
140
|
-
if File.exists?
|
141
|
-
File.open(
|
142
|
+
if File.exists? ".mortar-project-manifest"
|
143
|
+
File.open(".mortar-project-manifest", "r+") do |manifest|
|
142
144
|
contents = manifest.read()
|
143
145
|
manifest.seek(0, IO::SEEK_END)
|
144
146
|
|
@@ -187,7 +189,7 @@ module Mortar
|
|
187
189
|
|
188
190
|
# Copy code into a temp directory so we don't confuse editors while snapshotting
|
189
191
|
curdir = Dir.pwd
|
190
|
-
tmpdir = safe_copy(
|
192
|
+
tmpdir = safe_copy(mortar_manifest_pathlist)
|
191
193
|
|
192
194
|
starting_branch = current_branch
|
193
195
|
snapshot_branch = "mortar-snapshot-#{Mortar::UUID.create_random.to_s}"
|
@@ -195,11 +197,13 @@ module Mortar
|
|
195
197
|
# checkout a new branch
|
196
198
|
git("checkout -b #{snapshot_branch}")
|
197
199
|
|
198
|
-
|
200
|
+
# stage all changes (including deletes)
|
201
|
+
git("add .")
|
202
|
+
git("add -u .")
|
199
203
|
|
200
204
|
# commit the changes if there are any
|
201
205
|
if ! is_clean_working_directory?
|
202
|
-
git("commit -
|
206
|
+
git("commit -m \"mortar development snapshot commit\"")
|
203
207
|
end
|
204
208
|
|
205
209
|
Dir.chdir(curdir)
|
@@ -215,21 +219,7 @@ module Mortar
|
|
215
219
|
end
|
216
220
|
|
217
221
|
Dir.chdir(snapshot_dir)
|
218
|
-
|
219
|
-
git_ref = Helpers.action("Sending code snapshot to Mortar") do
|
220
|
-
# push the code
|
221
|
-
begin
|
222
|
-
push(project.remote, snapshot_branch)
|
223
|
-
rescue
|
224
|
-
retry if retry_snapshot_push?
|
225
|
-
Helpers.error("Could not connect to github remote. Tried #{@snapshot_push_attempts.to_s} times.")
|
226
|
-
end
|
227
|
-
|
228
|
-
# grab the commit hash
|
229
|
-
ref = git_ref(snapshot_branch)
|
230
|
-
ref
|
231
|
-
end
|
232
|
-
|
222
|
+
git_ref = push_with_retry(project.remote, snapshot_branch, "Sending code snapshot to Mortar")
|
233
223
|
FileUtils.remove_entry_secure(snapshot_dir)
|
234
224
|
Dir.chdir(curdir)
|
235
225
|
return git_ref
|
@@ -246,7 +236,82 @@ module Mortar
|
|
246
236
|
@snapshot_push_attempts ||= 0
|
247
237
|
@snapshot_push_attempts += 1
|
248
238
|
@snapshot_push_attempts < 10
|
249
|
-
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def mortar_mirrors_dir()
|
242
|
+
"/tmp/mortar-git-mirrors"
|
243
|
+
end
|
244
|
+
|
245
|
+
def sync_gitless_project(project)
|
246
|
+
# the project is not a git repo, so we manage a mirror directory that is a git repo
|
247
|
+
|
248
|
+
project_dir = project.root_path
|
249
|
+
mirror_dir = "#{mortar_mirrors_dir}/#{project.name}"
|
250
|
+
|
251
|
+
ensure_gitless_project_mirror_exists(project_dir, mirror_dir)
|
252
|
+
sync_gitless_project_with_mirror(project_dir, mirror_dir)
|
253
|
+
git_ref = sync_gitless_project_mirror_with_cloud(project_dir, mirror_dir)
|
254
|
+
|
255
|
+
Dir.chdir(project_dir)
|
256
|
+
return git_ref
|
257
|
+
end
|
258
|
+
|
259
|
+
def ensure_gitless_project_mirror_exists(project_dir, mirror_dir)
|
260
|
+
# create and initialize mirror git repo if it doesn't already exist
|
261
|
+
unless File.directory? mirror_dir
|
262
|
+
unless File.directory? mortar_mirrors_dir
|
263
|
+
FileUtils.mkdir_p mortar_mirrors_dir
|
264
|
+
end
|
265
|
+
|
266
|
+
# clone mortar-code repo
|
267
|
+
remote_path = File.open(".mortar-project-remote").read.strip
|
268
|
+
clone(remote_path, mirror_dir)
|
269
|
+
|
270
|
+
# make an initial commit to master
|
271
|
+
Dir.chdir(mirror_dir)
|
272
|
+
File.open(".gitkeep", "w").close()
|
273
|
+
git("add .")
|
274
|
+
git("commit -m \"mortar development initial commit\"")
|
275
|
+
git("remote add mortar #{remote_path}")
|
276
|
+
push_with_retry("mortar", "master", "Setting up gitless Mortar project")
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def sync_gitless_project_with_mirror(project_dir, mirror_dir)
|
281
|
+
# pull from master and overwrite everything
|
282
|
+
Dir.chdir(mirror_dir)
|
283
|
+
git("fetch --all")
|
284
|
+
git("reset --hard mortar/master")
|
285
|
+
|
286
|
+
# wipe mirror dir and copy project files into it
|
287
|
+
# since we fetched mortar/master earlier, the git diff will now be b/tw master and the current state
|
288
|
+
# mortar_manifest_pathlist(false) means don't copy .git
|
289
|
+
FileUtils.rm_rf(Dir.glob("#{mirror_dir}/*"))
|
290
|
+
Dir.chdir(project_dir)
|
291
|
+
FileUtils.cp_r(mortar_manifest_pathlist(false), mirror_dir)
|
292
|
+
|
293
|
+
# update master
|
294
|
+
Dir.chdir(mirror_dir)
|
295
|
+
unless is_clean_working_directory?
|
296
|
+
git("add .")
|
297
|
+
git("add -u .") # this gets deletes
|
298
|
+
git("commit -m \"mortar development snapshot commit\"")
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def sync_gitless_project_mirror_with_cloud(project_dir, mirror_dir)
|
303
|
+
# checkout snapshot branch.
|
304
|
+
# it will permenantly keep the code in this state (as opposed to master, which will be updated)
|
305
|
+
Dir.chdir(mirror_dir)
|
306
|
+
snapshot_branch = "mortar-snapshot-#{Mortar::UUID.create_random.to_s}"
|
307
|
+
git("checkout -b #{snapshot_branch}")
|
308
|
+
|
309
|
+
# push everything (master updates and snapshot branch)
|
310
|
+
git_ref = push_with_retry("mortar", snapshot_branch, "Sending code snapshot to Mortar", true)
|
311
|
+
|
312
|
+
git("checkout master")
|
313
|
+
return git_ref
|
314
|
+
end
|
250
315
|
|
251
316
|
#
|
252
317
|
# add
|
@@ -255,12 +320,6 @@ module Mortar
|
|
255
320
|
def add(path)
|
256
321
|
git("add #{path}")
|
257
322
|
end
|
258
|
-
|
259
|
-
def add_untracked_files
|
260
|
-
untracked_files.each do |untracked_file|
|
261
|
-
add untracked_file
|
262
|
-
end
|
263
|
-
end
|
264
323
|
|
265
324
|
#
|
266
325
|
# branch
|
@@ -295,6 +354,40 @@ module Mortar
|
|
295
354
|
git("push #{remote_name} #{ref}")
|
296
355
|
end
|
297
356
|
|
357
|
+
def push_all(remote_name)
|
358
|
+
git("push #{remote_name} --all")
|
359
|
+
end
|
360
|
+
|
361
|
+
def push_with_retry(remote_name, branch_name, action_msg, push_all_branches = false)
|
362
|
+
git_ref = Helpers.action(action_msg) do
|
363
|
+
# push the code
|
364
|
+
begin
|
365
|
+
if push_all_branches
|
366
|
+
push_all(remote_name)
|
367
|
+
else
|
368
|
+
push(remote_name, branch_name)
|
369
|
+
end
|
370
|
+
rescue
|
371
|
+
retry if retry_snapshot_push?
|
372
|
+
Helpers.error("Could not connect to github remote. Tried #{@snapshot_push_attempts.to_s} times.")
|
373
|
+
end
|
374
|
+
|
375
|
+
# grab the commit hash
|
376
|
+
ref = git_ref(branch_name)
|
377
|
+
ref
|
378
|
+
end
|
379
|
+
|
380
|
+
return git_ref
|
381
|
+
end
|
382
|
+
|
383
|
+
#
|
384
|
+
# pull
|
385
|
+
#
|
386
|
+
|
387
|
+
def pull(remote_name, ref)
|
388
|
+
git("pull #{remote_name} #{ref}")
|
389
|
+
end
|
390
|
+
|
298
391
|
|
299
392
|
#
|
300
393
|
# remotes
|
data/lib/mortar/project.rb
CHANGED
data/lib/mortar/version.rb
CHANGED
@@ -144,5 +144,28 @@ STDERR
|
|
144
144
|
end
|
145
145
|
end
|
146
146
|
end
|
147
|
+
|
148
|
+
it "requests and reports a describe for a gitless project" do
|
149
|
+
with_gitless_project do |p|
|
150
|
+
# stub api requests
|
151
|
+
describe_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
|
152
|
+
describe_url = "https://api.mortardata.com/describe/#{describe_id}"
|
153
|
+
parameters = ["name"=>"key", "value"=>"value" ]
|
154
|
+
|
155
|
+
mock(Mortar::Auth.api).post_describe("myproject", "my_script", "my_alias", is_a(String), :parameters => parameters) {Excon::Response.new(:body => {"describe_id" => describe_id})}
|
156
|
+
mock(Mortar::Auth.api).get_describe(describe_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Describe::STATUS_QUEUED, "status_description" => "Pending"})).ordered
|
157
|
+
mock(Mortar::Auth.api).get_describe(describe_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Describe::STATUS_GATEWAY_STARTING, "status_description" => "Gateway starting"})).ordered
|
158
|
+
mock(Mortar::Auth.api).get_describe(describe_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Describe::STATUS_PROGRESS, "status_description" => "Starting pig"})).ordered
|
159
|
+
mock(Mortar::Auth.api).get_describe(describe_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Describe::STATUS_SUCCESS, "status_description" => "Success", "web_result_url" => describe_url})).ordered
|
160
|
+
|
161
|
+
mock(@git).sync_gitless_project.with_any_args.times(1) { "somewhere_over_the_rainbow" }
|
162
|
+
|
163
|
+
# stub launchy
|
164
|
+
mock(Launchy).open(describe_url) {Thread.new {}}
|
165
|
+
|
166
|
+
write_file(File.join(p.pigscripts_path, "my_script.pig"))
|
167
|
+
stderr, stdout = execute("describe my_script my_alias --polling_interval 0.05 -p key=value", p, @git)
|
168
|
+
end
|
169
|
+
end
|
147
170
|
end
|
148
171
|
end
|
@@ -223,6 +223,32 @@ STDOUT
|
|
223
223
|
STDERR
|
224
224
|
end
|
225
225
|
end
|
226
|
+
|
227
|
+
it "requests and reports an illustrate for a gitless project" do
|
228
|
+
with_gitless_project do |p|
|
229
|
+
# stub api requests
|
230
|
+
illustrate_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
|
231
|
+
illustrate_url = "https://api.mortardata.com/illustrates/#{illustrate_id}"
|
232
|
+
parameters = ["name"=>"key", "value"=>"value" ]
|
233
|
+
|
234
|
+
# These don't test the validity of the error message, it only tests that the CLI can handle a message returned from the server
|
235
|
+
mock(Mortar::Auth.api).post_illustrate("myproject", "my_script", nil, false, is_a(String), :parameters => parameters) {Excon::Response.new(:body => {"illustrate_id" => illustrate_id})}
|
236
|
+
mock(Mortar::Auth.api).get_illustrate(illustrate_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Illustrate::STATUS_QUEUED, "status_description" => "Pending"})).ordered
|
237
|
+
mock(Mortar::Auth.api).get_illustrate(illustrate_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Illustrate::STATUS_GATEWAY_STARTING, "status_description" => "GATEWAY_STARTING"})).ordered
|
238
|
+
mock(Mortar::Auth.api).get_illustrate(illustrate_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Illustrate::STATUS_PROGRESS, "status_description" => "In progress"})).ordered
|
239
|
+
mock(Mortar::Auth.api).get_illustrate(illustrate_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Illustrate::STATUS_READING_DATA, "status_description" => "Reading data"})).ordered
|
240
|
+
mock(Mortar::Auth.api).get_illustrate(illustrate_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Illustrate::STATUS_PRUNING_DATA, "status_description" => "Pruning data"})).ordered
|
241
|
+
mock(Mortar::Auth.api).get_illustrate(illustrate_id, :exclude_result => true).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Illustrate::STATUS_SUCCESS, "status_description" => "Succeeded", "web_result_url" => illustrate_url})).ordered
|
242
|
+
|
243
|
+
mock(@git).sync_gitless_project.with_any_args.times(1) { "somewhere_over_the_rainbow" }
|
244
|
+
|
245
|
+
# stub launchy
|
246
|
+
mock(Launchy).open(illustrate_url) {Thread.new {}}
|
247
|
+
|
248
|
+
write_file(File.join(p.pigscripts_path, "my_script.pig"))
|
249
|
+
stderr, stdout = execute("illustrate my_script --polling_interval 0.05 -p key=value", p, @git)
|
250
|
+
end
|
251
|
+
end
|
226
252
|
end
|
227
253
|
end
|
228
254
|
end
|
@@ -388,6 +388,26 @@ PARAMS
|
|
388
388
|
STDERR
|
389
389
|
end
|
390
390
|
end
|
391
|
+
|
392
|
+
it "runs a job for a gitless project" do
|
393
|
+
with_gitless_project do |p|
|
394
|
+
# stub api requests
|
395
|
+
job_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
|
396
|
+
job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
|
397
|
+
cluster_size = 5
|
398
|
+
|
399
|
+
mock(@git).sync_gitless_project.with_any_args.times(1) { "somewhere_over_the_rainbow" }
|
400
|
+
|
401
|
+
mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
|
402
|
+
:parameters => match_array([{"name" => "FIRST_PARAM", "value" => "FOO"}, {"name" => "SECOND_PARAM", "value" => "BAR"}]),
|
403
|
+
:cluster_type => Jobs::CLUSTER_TYPE__PERSISTENT,
|
404
|
+
:notify_on_job_finish => true,
|
405
|
+
:is_control_script=>false) {Excon::Response.new(:body => {"job_id" => job_id, "web_job_url" => job_url})}
|
406
|
+
|
407
|
+
write_file(File.join(p.pigscripts_path, "my_script.pig"))
|
408
|
+
stderr, stdout = execute("jobs:run my_script --clustersize 5 -p FIRST_PARAM=FOO -p SECOND_PARAM=BAR", p, @git)
|
409
|
+
end
|
410
|
+
end
|
391
411
|
end
|
392
412
|
|
393
413
|
context("status") do
|
@@ -140,6 +140,35 @@ Sending request to register project: some_new_project... done\n\n\r\e[0KStatus:
|
|
140
140
|
STDOUT
|
141
141
|
end
|
142
142
|
|
143
|
+
it "generates and registers a gitless project" do
|
144
|
+
mock(Mortar::Auth.api).get_projects().returns(Excon::Response.new(:body => {"projects" => [project1, project2]}))
|
145
|
+
project_id = "1234abcd1234abcd1234"
|
146
|
+
project_name = "some_new_project"
|
147
|
+
project_git_url = "git@github.com:mortarcode-dev/#{project_name}"
|
148
|
+
mock(Mortar::Auth.api).post_project("some_new_project") {Excon::Response.new(:body => {"project_id" => project_id})}
|
149
|
+
mock(Mortar::Auth.api).get_project(project_id).returns(Excon::Response.new(:body => {"status" => Mortar::API::Projects::STATUS_ACTIVE,
|
150
|
+
"git_url" => project_git_url})).ordered
|
151
|
+
|
152
|
+
# test that sync_gitless_project is called. the method itself is tested in git_spec.
|
153
|
+
mock(@git).sync_gitless_project.with_any_args.times(1) { true }
|
154
|
+
|
155
|
+
stderr, stdout = execute("projects:create #{project_name} --withoutgit", nil, @git)
|
156
|
+
Dir.pwd.end_with?("some_new_project").should be_true
|
157
|
+
File.exists?(".mortar-project-remote").should be_true
|
158
|
+
File.exists?("macros").should be_true
|
159
|
+
File.exists?("fixtures").should be_true
|
160
|
+
File.exists?("pigscripts").should be_true
|
161
|
+
File.exists?("udfs").should be_true
|
162
|
+
File.exists?("README.md").should be_true
|
163
|
+
File.exists?("Gemfile").should be_false
|
164
|
+
File.exists?("macros/.gitkeep").should be_true
|
165
|
+
File.exists?("fixtures/.gitkeep").should be_true
|
166
|
+
File.exists?("pigscripts/some_new_project.pig").should be_true
|
167
|
+
File.exists?("udfs/python/some_new_project.py").should be_true
|
168
|
+
|
169
|
+
File.read("pigscripts/some_new_project.pig").each_line { |line| line.match(/<%.*%>/).should be_nil }
|
170
|
+
end
|
171
|
+
|
143
172
|
end
|
144
173
|
|
145
174
|
context("register") do
|
@@ -170,7 +199,8 @@ STDERR
|
|
170
199
|
stderr, stdout = execute("projects:register some_new_project")
|
171
200
|
stderr.should == <<-STDERR
|
172
201
|
! No git repository found in the current directory.
|
173
|
-
!
|
202
|
+
! To register a project that is not its own git repository, use the --withoutgit option.
|
203
|
+
! If you do want this project to be its own git repository, please initialize git in this directory, and then rerun the register command.
|
174
204
|
! To initialize your project in git, use:
|
175
205
|
!
|
176
206
|
! git init
|
@@ -246,6 +276,28 @@ STDOUT
|
|
246
276
|
Sending request to register project: some_new_project... done\n\n\r\e[0KStatus: Pending... /\r\e[0KStatus: Creating... -\r\e[0KStatus: Active \n\nYour project is ready for use. Type 'mortar help' to see the commands you can perform on the project.\n
|
247
277
|
STDOUT
|
248
278
|
end
|
279
|
+
|
280
|
+
it "registers a gitless project" do
|
281
|
+
mock(Mortar::Auth.api).get_projects().returns(Excon::Response.new(:body => {"projects" => [project1, project2]}))
|
282
|
+
project_id = "1234abcd1234abcd1234"
|
283
|
+
project_name = "some_new_project"
|
284
|
+
project_git_url = "git@github.com:mortarcode-dev/#{project_name}"
|
285
|
+
mock(Mortar::Auth.api).post_project("some_new_project") {Excon::Response.new(:body => {"project_id" => project_id})}
|
286
|
+
mock(Mortar::Auth.api).get_project(project_id).returns(Excon::Response.new(:body => {"status_description" => "Pending", "status_code" => Mortar::API::Projects::STATUS_PENDING})).ordered
|
287
|
+
mock(Mortar::Auth.api).get_project(project_id).returns(Excon::Response.new(:body => {"status_description" => "Creating", "status_code" => Mortar::API::Projects::STATUS_CREATING})).ordered
|
288
|
+
mock(Mortar::Auth.api).get_project(project_id).returns(Excon::Response.new(:body => {"status_description" => "Active", "status_code" => Mortar::API::Projects::STATUS_ACTIVE,
|
289
|
+
"git_url" => project_git_url})).ordered
|
290
|
+
|
291
|
+
any_instance_of(Mortar::Command::Projects) do |obj|
|
292
|
+
mock(obj).project.returns(nil)
|
293
|
+
mock(obj).validate_project_structure.returns(true)
|
294
|
+
end
|
295
|
+
|
296
|
+
# test that sync_gitless_project is called. the method itself is tested in git_spec.
|
297
|
+
mock(@git).sync_gitless_project.with_any_args.times(1) { true }
|
298
|
+
|
299
|
+
stderr, stdout = execute("projects:register some_new_project --withoutgit --polling_interval 0.05", nil, @git)
|
300
|
+
end
|
249
301
|
|
250
302
|
end
|
251
303
|
|
@@ -128,5 +128,24 @@ STDERR
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
131
|
+
|
132
|
+
it "requests and reports a validate for a gitless project" do
|
133
|
+
with_gitless_project do |p|
|
134
|
+
# stub api requests
|
135
|
+
validate_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
|
136
|
+
parameters = ["name"=>"key", "value"=>"value" ]
|
137
|
+
|
138
|
+
mock(Mortar::Auth.api).post_validate("myproject", "my_script", is_a(String), :parameters => parameters) {Excon::Response.new(:body => {"validate_id" => validate_id})}
|
139
|
+
mock(Mortar::Auth.api).get_validate(validate_id).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Validate::STATUS_QUEUED, "status_description" => "Pending"})).ordered
|
140
|
+
mock(Mortar::Auth.api).get_validate(validate_id).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Validate::STATUS_GATEWAY_STARTING, "status_description" => "GATEWAY_STARTING"})).ordered
|
141
|
+
mock(Mortar::Auth.api).get_validate(validate_id).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Validate::STATUS_PROGRESS, "status_description" => "Starting"})).ordered
|
142
|
+
mock(Mortar::Auth.api).get_validate(validate_id).returns(Excon::Response.new(:body => {"status_code" => Mortar::API::Validate::STATUS_SUCCESS, "status_description" => "Success"})).ordered
|
143
|
+
|
144
|
+
mock(@git).sync_gitless_project.with_any_args.times(1) { "somewhere_over_the_rainbow" }
|
145
|
+
|
146
|
+
write_file(File.join(p.pigscripts_path, "my_script.pig"))
|
147
|
+
stderr, stdout = execute("validate my_script --polling_interval 0.05 -p key=value", p, @git)
|
148
|
+
end
|
149
|
+
end
|
131
150
|
end
|
132
151
|
end
|
data/spec/mortar/git_spec.rb
CHANGED
@@ -25,7 +25,7 @@ module Mortar
|
|
25
25
|
before do
|
26
26
|
@git = Mortar::Git::Git.new
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
context "has_git?" do
|
30
30
|
it "returns false with no git installed" do
|
31
31
|
mock(@git).run_cmd("git --version").returns(["-bash: git: command not found",-1])
|
@@ -248,7 +248,7 @@ STASH
|
|
248
248
|
end
|
249
249
|
end
|
250
250
|
|
251
|
-
context "snapshot" do
|
251
|
+
context "snapshot with git project" do
|
252
252
|
it "raises when no commits are found in the repo" do
|
253
253
|
with_blank_project do |p|
|
254
254
|
lambda { @git.create_snapshot_branch }.should raise_error(Mortar::Git::GitError)
|
@@ -301,6 +301,71 @@ STASH
|
|
301
301
|
end
|
302
302
|
end
|
303
303
|
end
|
304
|
+
|
305
|
+
# we manually create and destroy "mirror_dir" instead of using FakeFS
|
306
|
+
# because FakeFS doesn't clean up properly when you use Dir.chdir inside of it
|
307
|
+
context "snapshot with gitless project" do
|
308
|
+
|
309
|
+
it "creates a mirror directory for the project when one does not already exist" do
|
310
|
+
with_gitless_project do |p|
|
311
|
+
mirror_dir = File.join(Dir.tmpdir, "mortar", "test-git-mirror")
|
312
|
+
mock(@git).mortar_mirrors_dir.any_times { mirror_dir }
|
313
|
+
|
314
|
+
mock(@git).git.with_any_args.any_times { true }
|
315
|
+
mock(@git).clone.with_any_args.times(1) { FileUtils.mkdir("#{mirror_dir}/#{p.name}") }
|
316
|
+
mock(@git).push_with_retry.with_any_args.times(2) { true }
|
317
|
+
mock(@git).is_clean_working_directory? { false }
|
318
|
+
|
319
|
+
@git.sync_gitless_project(p)
|
320
|
+
|
321
|
+
File.directory?(mirror_dir).should be_true
|
322
|
+
FileUtils.rm_rf(mirror_dir)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
it "syncs files to the project mirror" do
|
327
|
+
with_gitless_project do |p|
|
328
|
+
mirror_dir = File.join(Dir.tmpdir, "mortar", "test-git-mirror")
|
329
|
+
mock(@git).mortar_mirrors_dir.any_times { mirror_dir }
|
330
|
+
|
331
|
+
project_mirror_dir = File.join(mirror_dir, p.name)
|
332
|
+
FileUtils.mkdir_p(project_mirror_dir)
|
333
|
+
FileUtils.touch("#{p.root_path}/pigscripts/calydonian_boar.pig")
|
334
|
+
|
335
|
+
mock(@git).git.with_any_args.any_times { true }
|
336
|
+
mock(@git).clone.with_any_args.never
|
337
|
+
mock(@git).push_with_retry.with_any_args.times(1) { true }
|
338
|
+
mock(@git).is_clean_working_directory? { false }
|
339
|
+
|
340
|
+
@git.sync_gitless_project(p)
|
341
|
+
|
342
|
+
File.exists?("#{project_mirror_dir}/pigscripts/calydonian_boar.pig").should be_true
|
343
|
+
FileUtils.rm_rf(mirror_dir)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
it "syncs deleted files to the project mirror" do
|
348
|
+
with_gitless_project do |p|
|
349
|
+
mirror_dir = File.join(Dir.tmpdir, "mortar", "test-git-mirror")
|
350
|
+
mock(@git).mortar_mirrors_dir.any_times { mirror_dir }
|
351
|
+
|
352
|
+
project_mirror_dir = File.join(mirror_dir, p.name)
|
353
|
+
FileUtils.mkdir_p(project_mirror_dir)
|
354
|
+
FileUtils.cp_r(Dir.glob("#{p.root_path}/*"), project_mirror_dir)
|
355
|
+
FileUtils.touch("#{project_mirror_dir}/pigscripts/calydonian_boar.pig")
|
356
|
+
|
357
|
+
mock(@git).git.with_any_args.any_times { true }
|
358
|
+
mock(@git).clone.with_any_args.never
|
359
|
+
mock(@git).push_with_retry.with_any_args.times(1) { true }
|
360
|
+
mock(@git).is_clean_working_directory? { false }
|
361
|
+
|
362
|
+
@git.sync_gitless_project(p)
|
363
|
+
|
364
|
+
File.exists?("#{project_mirror_dir}/pigscripts/calydonian_boar.pig").should be_false
|
365
|
+
FileUtils.rm_rf(mirror_dir)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
304
369
|
|
305
370
|
=begin
|
306
371
|
#TODO: Fix this.
|
data/spec/spec_helper.rb
CHANGED
@@ -168,6 +168,7 @@ def with_blank_project(&block)
|
|
168
168
|
FileUtils.mkdir_p(File.join(project_path, "udfs"))
|
169
169
|
FileUtils.mkdir_p(File.join(project_path, "udfs/python"))
|
170
170
|
FileUtils.mkdir_p(File.join(project_path, "udfs/jython"))
|
171
|
+
FileUtils.mkdir_p(File.join(project_path, "fixtures"))
|
171
172
|
|
172
173
|
Dir.chdir(project_path)
|
173
174
|
|
@@ -207,6 +208,15 @@ def with_git_initialized_project(&block)
|
|
207
208
|
with_blank_project(&commit_proc)
|
208
209
|
end
|
209
210
|
|
211
|
+
def with_gitless_project(&block)
|
212
|
+
with_blank_project do |project|
|
213
|
+
File.open(File.join(project.root_path, ".mortar-project-remote"), "w") do |f|
|
214
|
+
f.puts "git@github.com:mortarcode-dev/4dbbd83cae8d5bf8a4000000_#{project.name}.git"
|
215
|
+
end
|
216
|
+
block.call(project)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
210
220
|
def write_file(path, contents="")
|
211
221
|
FileUtils.mkdir_p File.dirname(path)
|
212
222
|
File.open(path, 'w') {|f| f.write(contents)}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mortar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mortar-api-ruby
|