mortar 0.7.8 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- validate_git_based_project!
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
- validate_git_based_project!
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
@@ -114,8 +114,8 @@ class Mortar::Command::Jobs < Mortar::Command::Base
114
114
  end
115
115
  end
116
116
 
117
- validate_git_based_project!
118
- git_ref = git.create_and_push_snapshot_branch(project)
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
- git.git_init
71
- git.git("add .")
72
- git.git("commit -m \"Mortar project scaffolding\"")
73
- Mortar::Command::run("projects:register", [name])
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
- unless git.has_dot_git?
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
- if File.exists? name
90
- error("mortar projects:register must be run from within the project directory.\nPlease \"cd #{name}\" and rerun this command.")
91
- else
92
- error("No git repository found in the current directory.\nPlease initialize a git repository for this project, and then rerun the register command.\nTo initialize your project in git, use:\n\ngit init\ngit add .\ngit commit -a -m \"first commit\"")
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
- end
95
-
96
- # ensure the project name does not already exist
97
- project_names = api.get_projects().body["projects"].collect{|p| p['name']}
98
- if project_names.include? name
99
- 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}")
100
- end
101
-
102
- unless git.remotes(git_organization).empty?
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
- end
109
-
110
- project_id = nil
111
- action("Sending request to register project: #{name}") do
112
- project_id = api.post_project(name).body["project_id"]
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
- validate_git_based_project!
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(mortar_snapshot_pathlist) do
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 mortar_snapshot_pathlist()
121
+ def mortar_manifest_pathlist(include_dot_git = true)
122
122
  ensure_valid_mortar_project_manifest()
123
123
 
124
- snapshot_pathlist = File.read('.mortar-project-manifest').split("\n")
125
- snapshot_pathlist << ".git"
124
+ manifest_pathlist = File.read(".mortar-project-manifest").split("\n")
125
+ if include_dot_git
126
+ manifest_pathlist << ".git"
127
+ end
126
128
 
127
- snapshot_pathlist.each do |path|
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
- snapshot_pathlist
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? '.mortar-project-manifest'
141
- File.open('.mortar-project-manifest', 'r+') do |manifest|
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(mortar_snapshot_pathlist)
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
- add_untracked_files()
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 -a -m \"mortar development snapshot 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
@@ -82,6 +82,10 @@ module Mortar
82
82
  def fixtures_path
83
83
  path = File.join(@root_path, "fixtures")
84
84
  end
85
+
86
+ def gitless_project?()
87
+ File.exists?(File.join(@root_path, ".mortar-project-remote"))
88
+ end
85
89
  end
86
90
 
87
91
  class ProjectEntity
@@ -16,5 +16,5 @@
16
16
 
17
17
  module Mortar
18
18
  # see http://semver.org/
19
- VERSION = "0.7.8"
19
+ VERSION = "0.7.9"
20
20
  end
@@ -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
- ! Please initialize a git repository for this project, and then rerun the register command.
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
@@ -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.8
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-07 00:00:00.000000000 Z
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