codefumes 0.1.10 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. data/Gemfile +19 -0
  2. data/Gemfile.lock +135 -0
  3. data/History.txt +12 -0
  4. data/LICENSE +20 -0
  5. data/Manifest.txt +40 -19
  6. data/README.txt +11 -29
  7. data/Rakefile +15 -10
  8. data/bin/fumes +214 -0
  9. data/config/website.yml +2 -0
  10. data/cucumber.yml +2 -0
  11. data/features/claiming_a_project.feature +46 -0
  12. data/features/deleting_a_project.feature +32 -0
  13. data/features/releasing_a_project.feature +50 -0
  14. data/features/step_definitions/cli_steps.rb +98 -0
  15. data/features/step_definitions/common_steps.rb +168 -0
  16. data/features/step_definitions/filesystem_steps.rb +19 -0
  17. data/features/storing_user_api_key.feature +41 -0
  18. data/features/support/common.rb +29 -0
  19. data/features/support/env.rb +24 -0
  20. data/features/support/matchers.rb +11 -0
  21. data/features/synchronizing_repository_with_project.feature +33 -0
  22. data/lib/codefumes.rb +10 -8
  23. data/lib/codefumes/api.rb +20 -11
  24. data/lib/codefumes/api/build.rb +139 -0
  25. data/lib/codefumes/api/claim.rb +74 -0
  26. data/lib/codefumes/api/commit.rb +150 -0
  27. data/lib/codefumes/api/payload.rb +93 -0
  28. data/lib/codefumes/api/project.rb +158 -0
  29. data/lib/codefumes/cli_helpers.rb +54 -0
  30. data/lib/codefumes/config_file.rb +3 -2
  31. data/lib/codefumes/errors.rb +21 -0
  32. data/lib/codefumes/exit_codes.rb +10 -0
  33. data/lib/codefumes/harvester.rb +113 -0
  34. data/lib/codefumes/quick_build.rb +43 -0
  35. data/lib/codefumes/quick_metric.rb +20 -0
  36. data/lib/codefumes/source_control.rb +137 -0
  37. data/lib/integrity_notifier/codefumes.haml +11 -0
  38. data/lib/integrity_notifier/codefumes.rb +62 -0
  39. data/spec/codefumes/{build_spec.rb → api/build_spec.rb} +14 -24
  40. data/spec/codefumes/{claim_spec.rb → api/claim_spec.rb} +42 -3
  41. data/spec/codefumes/{commit_spec.rb → api/commit_spec.rb} +34 -24
  42. data/spec/codefumes/api/payload_spec.rb +148 -0
  43. data/spec/codefumes/api/project_spec.rb +286 -0
  44. data/spec/codefumes/api_spec.rb +38 -15
  45. data/spec/codefumes/config_file_spec.rb +69 -13
  46. data/spec/codefumes/harvester_spec.rb +118 -0
  47. data/spec/codefumes/source_control_spec.rb +199 -0
  48. data/spec/codefumes_service_helpers.rb +23 -19
  49. data/spec/fixtures/sample_project_dirs/no_scm/description +4 -0
  50. data/spec/spec_helper.rb +1 -0
  51. data/tasks/cucumber.rake +11 -0
  52. metadata +145 -60
  53. data/bin/cf_claim_project +0 -9
  54. data/bin/cf_release_project +0 -10
  55. data/bin/cf_store_credentials +0 -10
  56. data/lib/cf_claim_project/cli.rb +0 -95
  57. data/lib/cf_release_project/cli.rb +0 -76
  58. data/lib/cf_store_credentials/cli.rb +0 -50
  59. data/lib/codefumes/build.rb +0 -131
  60. data/lib/codefumes/claim.rb +0 -57
  61. data/lib/codefumes/commit.rb +0 -144
  62. data/lib/codefumes/payload.rb +0 -103
  63. data/lib/codefumes/project.rb +0 -129
  64. data/spec/cf_claim_project/cli_spec.rb +0 -17
  65. data/spec/cf_release_project/cli_spec.rb +0 -41
  66. data/spec/cf_store_credentials/cli_spec.rb +0 -28
  67. data/spec/codefumes/payload_spec.rb +0 -155
  68. data/spec/codefumes/project_spec.rb +0 -274
@@ -14,7 +14,7 @@ end
14
14
 
15
15
  describe "ConfigFile" do
16
16
  before(:each) do
17
- @project = Project.new(:public_key => 'public_key_value', :private_key => 'private_key_value')
17
+ @project = Project.new('public_key_value', :private_key => 'private_key_value')
18
18
  end
19
19
 
20
20
  after(:all) do
@@ -53,7 +53,7 @@ describe "ConfigFile" do
53
53
  context "when passed an existing project" do
54
54
  before(:each) do
55
55
  ConfigFile.save_project(@project)
56
- @updated_project = Project.new(:public_key => @project.public_key, :private_key => "updated_private_key")
56
+ @updated_project = Project.new(@project.public_key, :private_key => "updated_private_key")
57
57
  end
58
58
 
59
59
  it "does not create a duplicate entry in the list of projects" do
@@ -67,9 +67,9 @@ describe "ConfigFile" do
67
67
 
68
68
  context "when several projects exist" do
69
69
  before(:each) do
70
- @project1 = Project.new(:public_key => :p1_pub_value, :private_key => "p1_private_key")
71
- @project2 = Project.new(:public_key => :p2_pub_value, :private_key => "p2_private_key")
72
- @project3 = Project.new(:public_key => :p3_pub_value, :private_key => "p3_private_key")
70
+ @project1 = Project.new(:p1_pub_value, :private_key => "p1_private_key")
71
+ @project2 = Project.new(:p2_pub_value, :private_key => "p2_private_key")
72
+ @project3 = Project.new(:p3_pub_value, :private_key => "p3_private_key")
73
73
  ConfigFile.save_project(@project1)
74
74
  ConfigFile.save_project(@project2)
75
75
  ConfigFile.save_project(@project3)
@@ -86,9 +86,9 @@ describe "ConfigFile" do
86
86
  describe "calling 'delete_project'" do
87
87
  context "when the project entry exists in the file" do
88
88
  before(:each) do
89
- @project1 = Project.new(:public_key => "p1_pub_value", :private_key => "p1_private_key")
90
- @project2 = Project.new(:public_key => "p2_pub_value", :private_key => "p2_private_key")
91
- @project3 = Project.new(:public_key => "p3_pub_value", :private_key => "p3_private_key")
89
+ @project1 = Project.new("p1_pub_value", :private_key => "p1_private_key")
90
+ @project2 = Project.new("p2_pub_value", :private_key => "p2_private_key")
91
+ @project3 = Project.new("p3_pub_value", :private_key => "p3_private_key")
92
92
  ConfigFile.save_project(@project1)
93
93
  ConfigFile.save_project(@project2)
94
94
  ConfigFile.save_project(@project3)
@@ -107,7 +107,7 @@ describe "ConfigFile" do
107
107
 
108
108
  context "when the project entry does not exist in the file" do
109
109
  before(:each) do
110
- @project = Project.new(:public_key => "p1_pub_nonexist_value", :private_key => "p1_private_key")
110
+ @project = Project.new("p1_pub_nonexist_value", :private_key => "p1_private_key")
111
111
  end
112
112
 
113
113
  it "does not raise an error" do
@@ -144,7 +144,7 @@ describe "ConfigFile" do
144
144
  end
145
145
  end
146
146
  end
147
-
147
+
148
148
  describe "calling 'save_credentials'" do
149
149
  before(:each) do
150
150
  @api_key = "API_KEY"
@@ -182,7 +182,7 @@ describe "ConfigFile" do
182
182
 
183
183
  it "does not affect existing project content" do
184
184
  public_key = "pub_value"
185
- project = Project.new(:public_key => public_key, :private_key => "private_key")
185
+ project = Project.new(public_key, :private_key => "private_key")
186
186
  ConfigFile.save_project(project)
187
187
  ConfigFile.save_credentials(@api_key)
188
188
  ConfigFile.serialized[:projects][public_key.to_sym].should_not be_nil
@@ -215,7 +215,7 @@ describe "ConfigFile" do
215
215
 
216
216
  context "when projects exist in the file" do
217
217
  before(:each) do
218
- create_uniq_project = lambda {|index| Project.new(:public_key => "pub_key_#{index}", :private_key => 'pk')}
218
+ create_uniq_project = lambda {|index| Project.new("pub_key_#{index}", :private_key => 'pk')}
219
219
  @projects = 5.times.map {|i| create_uniq_project.call(i)}
220
220
  @projects.each {|project| ConfigFile.save_project(project)}
221
221
  end
@@ -237,9 +237,65 @@ describe "ConfigFile" do
237
237
  end
238
238
 
239
239
  context "when no credentials exist in the file" do
240
- it "returns an empty Hash" do
240
+ it "returns nil" do
241
241
  ConfigFile.api_key.should == nil
242
242
  end
243
243
  end
244
244
  end
245
+
246
+ describe "#options_for_project" do
247
+ let(:private_key) {'private_key'}
248
+ let(:public_key) {'public_key'}
249
+ let(:project_instance) {Project.new(public_key, private_key)}
250
+
251
+ context "when passing in an Project instance" do
252
+ let(:param) {project_instance}
253
+
254
+ context "and no projects have been saved before" do
255
+ it "returns an empty Hash" do
256
+ delete_config_file
257
+ ConfigFile.options_for_project(param).should == {}
258
+ end
259
+ end
260
+
261
+ context "and the specified project has not been saved before" do
262
+ it "returns an empty Hash" do
263
+ ConfigFile.save_project(Project.new('public_key1'))
264
+ ConfigFile.options_for_project(param).should == {}
265
+ end
266
+ end
267
+
268
+ context "and the specified project has been saved before" do
269
+ it "returns a Hash of the project information" do
270
+ ConfigFile.save_project(project_instance)
271
+ ConfigFile.options_for_project(param).should include(:private_key => private_key)
272
+ end
273
+ end
274
+ end
275
+
276
+ context "when passing in the public key" do
277
+ let(:param) {public_key}
278
+
279
+ context "and no projects have been saved before" do
280
+ it "returns an empty Hash" do
281
+ delete_config_file
282
+ ConfigFile.options_for_project(param).should == {}
283
+ end
284
+ end
285
+
286
+ context "and the specified project has not been saved before" do
287
+ it "returns an empty Hash" do
288
+ ConfigFile.save_project(Project.new('public_key1'))
289
+ ConfigFile.options_for_project(param).should == {}
290
+ end
291
+ end
292
+
293
+ context "and the specified project has been saved before" do
294
+ it "returns a Hash of the project information" do
295
+ ConfigFile.save_project(project_instance)
296
+ ConfigFile.options_for_project(param).should include(:private_key => private_key)
297
+ end
298
+ end
299
+ end
300
+ end
245
301
  end
@@ -0,0 +1,118 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ #TODO: clean this up
4
+ include CodeFumesServiceHelpers::Shared
5
+ include CodeFumesServiceHelpers::ProjectHelpers
6
+
7
+ def raise_if_users_config_file
8
+ if ConfigFile.path == File.expand_path('~/.codefumes_config')
9
+ raise "Set a custom config file path"
10
+ end
11
+ end
12
+
13
+ def delete_config_file
14
+ unless ConfigFile.path == File.expand_path('~/.codefumes_config')
15
+ File.delete(ConfigFile.path) if File.exist?(ConfigFile.path)
16
+ end
17
+ end
18
+
19
+ describe Harvester do
20
+ after(:all) do
21
+ delete_config_file
22
+ FakeWeb.allow_net_connect = false
23
+ FakeWeb.clean_registry
24
+ end
25
+
26
+
27
+ let(:no_scm_path) {"#{File.dirname(__FILE__)}/../fixtures/sample_project_dirs/no_scm"}
28
+ let(:git_scm_path) {"#{File.dirname(__FILE__)}/../fixtures/sample_project_dirs/git_repository"}
29
+
30
+
31
+ before(:each) do
32
+ raise_if_users_config_file
33
+ setup_fixture_base
34
+ register_create_uri #For project creation
35
+ @repository = Grit::Repo.new(git_scm_path)
36
+ end
37
+
38
+ describe "#new" do
39
+ context "with a path to a directory that is using git" do
40
+ before(:each) do
41
+ Grit::Repo.stub!(:new).with(File.expand_path(git_scm_path)).and_return(@repository)
42
+ end
43
+
44
+ it "initializes a new Grit::Repo instance with the specified directory path" do
45
+ Grit::Repo.should_receive(:new).with(File.expand_path(git_scm_path)).and_return(@repository)
46
+ Harvester.new(:path => git_scm_path)
47
+ end
48
+ end
49
+
50
+ context "with a path to a directory that is not using a supported SCM" do
51
+ it "raises an UnsupportedScmToolError" do
52
+ lambda {
53
+ Harvester.new(:path => no_scm_path)
54
+ }.should raise_error(Errors::UnsupportedScmToolError)
55
+ end
56
+ end
57
+
58
+ context "when specifying the public/private keys" do
59
+ let(:public_key) {@pub_key}
60
+ let(:private_key) {@priv_key}
61
+
62
+ it "ignores the configuration saved in the underlying repository" do
63
+ register_show_uri
64
+ @repository.should_not_receive(:public_key)
65
+ @repository.should_not_receive(:private__key)
66
+ lambda {Harvester.new(:public_key => public_key, :private_key => private_key)}
67
+ end
68
+
69
+ context "if the specified project is not found on the server" do
70
+ before(:each) {register_show_uri(["404", "Not found"])}
71
+
72
+ it "raises an UnknownProjectError" do
73
+ lambda {
74
+ Harvester.new(:public_key => public_key, :private_key => private_key)
75
+ }.should raise_error(Errors::UnknownProjectError)
76
+ end
77
+
78
+ it "does not attempt to create a new project on the CodeFumes site" do
79
+ Project.should_not_receive(:create)
80
+ lambda {Harvester.new(:public_key => public_key, :private_key => private_key)}
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#path" do
87
+ it "returns the full path to a project" do
88
+ harvester = Harvester.new(:path => git_scm_path)
89
+ harvester.path.should == File.expand_path(git_scm_path)
90
+ end
91
+ end
92
+
93
+ describe "#publish_data!" do
94
+ include CodeFumesServiceHelpers::CommitHelpers
95
+ include CodeFumesServiceHelpers::ProjectHelpers
96
+
97
+ before(:each) do
98
+ delete_config_file
99
+ register_latest_uri
100
+ register_update_uri
101
+ @harvester = Harvester.new(:path => git_scm_path)
102
+ @payload = mock("Payload instance", :save => true)
103
+ Payload.stub!(:new).and_return(@payload)
104
+ end
105
+
106
+ it "saves the Payload to the website" do
107
+ pending "Need to figure out a better way to test this..."
108
+ @payload.should_receive(:save).and_return(true)
109
+ @harvester.publish_data!
110
+ end
111
+
112
+ it "updates the config file's project information" do
113
+ pending "Need to figure out a better way to test this..."
114
+ ConfigFile.should_receive(:save_project)
115
+ @harvester.publish_data!
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,199 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe SourceControl do
4
+ after(:each) do
5
+ SourceControl.new(GIT_FIXTURE_REPO_PATH).unlink_from_codefumes!
6
+ unless ConfigFile.path == File.expand_path('~/.codefumes_config')
7
+ File.delete(ConfigFile.path) if File.exists?(ConfigFile.path)
8
+ end
9
+ end
10
+
11
+ describe "creation" do
12
+ context "with a path to a directory that is not a git repository" do
13
+ it "raises an error" do
14
+ invalid_path = File.dirname(__FILE__)
15
+ lambda {SourceControl.new(invalid_path)}.should raise_error(Errors::UnsupportedScmToolError)
16
+ end
17
+ end
18
+ end
19
+
20
+ describe "#supported_systems" do
21
+ it "includes :git" do
22
+ SourceControl.supported_systems.should include(:git)
23
+ end
24
+ end
25
+
26
+ describe "#supported_system?" do
27
+ SourceControl.supported_systems.each do |scm_tool|
28
+ it "returns true for the string '#{scm_tool.to_s}'" do
29
+ SourceControl.supported_system?(scm_tool.to_s)
30
+ end
31
+
32
+ it "returns true for the symbol ':#{scm_tool.to_s}'" do
33
+ SourceControl.supported_system?(scm_tool.to_sym)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#initial_commit_identifier" do
39
+ before(:each) do
40
+ git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../fixtures/sample_project_dirs/git_repository')
41
+ @repository = SourceControl.new(git_repo_path)
42
+ end
43
+
44
+ it "returns the identifer (sha/commit #) of the first commit" do
45
+ @repository.initial_commit_identifier.should == "a8b3e73fc5e4bc46bbdf5c1cab38cb2ce47ba2d0"
46
+ end
47
+ end
48
+
49
+ describe "#payload_between" do
50
+ before(:each) do
51
+ git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../fixtures/sample_project_dirs/git_repository')
52
+ @repository = SourceControl.new(git_repo_path)
53
+ end
54
+
55
+ it "returns a Hash containing the key ':commits'" do
56
+ @repository.payload_between.keys.should include(:commits)
57
+ end
58
+
59
+ it "is aliased to 'payload'" do
60
+ @repository.payload_between.keys.should == @repository.payload.keys
61
+ # FIXME: Improve this...not a complete test. Object ids getting in the way
62
+ @repository.payload_between[:commits].each_with_index do |commit, index|
63
+ commit[:identifier].should == @repository.payload[:commits][index][:identifier]
64
+ end
65
+ end
66
+
67
+ it "returns the commits in the same order a standard 'log view' of the repository would" do
68
+ first_commit = @repository.payload_between[:commits].last
69
+ first_commit[:identifier].should == "a8b3e73fc5e4bc46bbdf5c1cab38cb2ce47ba2d0"
70
+ end
71
+
72
+ context "the ':commits' key returned" do
73
+ context "contains a list of data for each commit" do
74
+ before(:each) do
75
+ @first_commit = @repository.payload_between[:commits].last
76
+ end
77
+
78
+ it "contains 'id', which is the 'sha' of the commit" do
79
+ @first_commit[:identifier].should == "a8b3e73fc5e4bc46bbdf5c1cab38cb2ce47ba2d0"
80
+ end
81
+
82
+ it "contains 'author', which points to the 'email' & the 'name' of the commit (in a Hash)" do
83
+ @first_commit[:author_email].should == "tkersten@obtiva.com"
84
+ @first_commit[:author_name].should == "Tom Kersten"
85
+ end
86
+
87
+ it "contains 'committer', which points to the 'email' & the 'name' of the commit (in a Hash)" do
88
+ @first_commit[:committer_email].should == "tkersten@obtiva.com"
89
+ @first_commit[:committer_name].should == "Tom Kersten"
90
+ end
91
+
92
+ it "contains 'message', which holds the full message of the commit" do
93
+ @first_commit[:message].should == "Initial commit with description of directory"
94
+ end
95
+
96
+ it "contains 'short_message', which holds the first line of the message of the commit" do
97
+ @first_commit[:short_message].should == "Initial commit with description of directory"
98
+ end
99
+
100
+ it "contains 'committed_date' of the commit" do
101
+ @first_commit[:committed_at].should == Chronic.parse("Sat May 09 08:52:14 -0500 2009")
102
+ end
103
+
104
+ it "contains 'authored_date' of the commit" do
105
+ @first_commit[:authored_at].should == Chronic.parse("Sat May 09 08:52:14 -0500 2009")
106
+ end
107
+
108
+ it "contains 'parent_commits' of the commit" do
109
+ @first_commit[:parent_identifiers].should_not be_nil
110
+ end
111
+
112
+ it "contains a 'line_additions' key" do
113
+ @first_commit[:line_additions].should_not be_nil
114
+ end
115
+
116
+ it "contains an 'line_deletions' key" do
117
+ @first_commit[:line_deletions].should_not be_nil
118
+ end
119
+
120
+ it "contains an 'line_total' key" do
121
+ @first_commit[:line_total].should_not be_nil
122
+ end
123
+
124
+ it "contains a 'affected_file_count' key" do
125
+ @first_commit[:affected_file_count].should_not be_nil
126
+ end
127
+ end
128
+ end
129
+
130
+ context "when 'from' is specified but the value is nil" do
131
+ it "defaults to the initial commit identifier of the repository" do
132
+ @repository.should_receive(:commits_between).with("a8b3e73fc5e4bc46bbdf5c1cab38cb2ce47ba2d0", anything()).and_return([])
133
+ @repository.payload_between(nil, "something")
134
+ end
135
+ end
136
+
137
+ context "when 'to' is specified but the value is nil" do
138
+ it "defaults to the 'HEAD' commit identifier of the repository" do
139
+ @repository.should_receive(:commits_between).with(anything(), "HEAD").and_return([])
140
+ @repository.payload_between("something", nil)
141
+ end
142
+ end
143
+
144
+ context "when there is no new information to send up" do
145
+ it "returns an empty object" do
146
+ @repository.payload_between("HEAD", "HEAD").should be_empty
147
+ end
148
+ end
149
+ end
150
+
151
+ describe "#public_key=" do
152
+ before(:each) do
153
+ git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../fixtures/sample_project_dirs/git_repository')
154
+ @public_key = "public_key_specified"
155
+ @repository = SourceControl.new(git_repo_path)
156
+ @project = Project.new(:public_key => @public_key)
157
+ end
158
+
159
+ it "stores the supplied public key in the SCM tool's repository-specific configuration" do
160
+ @repository.store_public_key(@public_key)
161
+ @repository.public_key.should == @public_key
162
+ end
163
+ end
164
+
165
+ describe "#public_key" do
166
+ before(:each) do
167
+ @public_key = "original_value"
168
+ git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../fixtures/sample_project_dirs/git_repository')
169
+ @repository = SourceControl.new(git_repo_path)
170
+ @repository.store_public_key(@public_key)
171
+ end
172
+
173
+ it "returns the current value of 'codometer.public_key' from the repository-specific configuration" do
174
+ @repository.public_key.should == @public_key
175
+ end
176
+ end
177
+
178
+ describe "#local_commit_identifier" do
179
+ before(:each) do
180
+ git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../fixtures/sample_project_dirs/git_repository')
181
+ @repository = SourceControl.new(git_repo_path)
182
+ end
183
+
184
+ it "returns the current commit identifier for the local repository" do
185
+ @repository.local_commit_identifier.should == "7dc0e73fea4625204b7c1e6a48e9a57025be4d7e"
186
+ end
187
+ end
188
+
189
+ describe "#path" do
190
+ before(:each) do
191
+ @git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../fixtures/sample_project_dirs/git_repository')
192
+ @repository = SourceControl.new(@git_repo_path)
193
+ end
194
+
195
+ it "returns the full path to the .git directory of the repository" do
196
+ @repository.path.should == @git_repo_path + '/.git'
197
+ end
198
+ end
199
+ end