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
@@ -0,0 +1,2 @@
1
+ host: tomkersten@rubyforge.org
2
+ remote_dir: /var/www/gforge-projects/codefumes
data/cucumber.yml ADDED
@@ -0,0 +1,2 @@
1
+ default: --format pretty features
2
+ html_report: --format progress --format html --out=features_report.html features
@@ -0,0 +1,46 @@
1
+ Feature: Claiming a project
2
+ As the owner of a project, if I have decided I want to use the CodeFumes
3
+ service, I don't want to have to remember the public key for my project(s).
4
+ The gem must provide a simple method of "claiming" a project and associating
5
+ it with an account.
6
+
7
+
8
+ Scenario: Specified project does not exist on CodeFumes.com
9
+ Given valid user credentials have been stored in the CodeFumes config file
10
+ When I run "#{@bin_path}/fumes claim -p bad-public-key"
11
+ Then the output should contain "Not Found"
12
+ And the exit status should be 0
13
+
14
+ Scenario: Attempting to claim a project without having an API key entry in the CodeFumes config file
15
+ Given I have cloned and synchronized 1 project
16
+ When I claim the project
17
+ Then the output should contain instructions about storing your API key
18
+ And the exit status should be 3
19
+
20
+ Scenario: Attempting to claim a project with an invalid API key entry in the user's CodeFumes config file
21
+ Given invalid user credentials have been stored in the CodeFumes config file
22
+ And I have cloned and synchronized 1 project
23
+ When I claim the project
24
+ Then the output should contain "Denied"
25
+ And the exit status should be 0
26
+
27
+ Scenario: Claim a project using the key stored in a CodeFumes project directory
28
+ Given valid user credentials have been stored in the CodeFumes config file
29
+ And I have cloned and synchronized 1 project
30
+ When I claim the project
31
+ Then the output should contain "Success"
32
+ And the exit status should be 0
33
+
34
+ Scenario: Claiming one of multiple projects in your CodeFumes config file
35
+ Given valid user credentials have been stored in the CodeFumes config file
36
+ And I have cloned and synchronized 2 projects
37
+ When I claim the 1st project
38
+ Then the output should contain 1 successful claim message
39
+ And the exit status should be 0
40
+
41
+ Scenario: Claim all projects in your CodeFumes config file
42
+ Given valid user credentials have been stored in the CodeFumes config file
43
+ And I have cloned and synchronized 2 projects
44
+ And I run "#{@bin_path}/fumes claim -a"
45
+ Then the output should contain 2 successful claim messages
46
+ And the exit status should be 0
@@ -0,0 +1,32 @@
1
+ Feature: Deleting a project
2
+ The process of deleting a project must be both
3
+ available and simple in order to reduce the number
4
+ of barriers to testing out the service.
5
+
6
+
7
+ Scenario: The specified project does not exist on CodeFumes.com
8
+ When I run "#{@bin_path}/fumes delete -p bad-public-key"
9
+ Then the output should contain "Not Found"
10
+ And the exit status should be 0
11
+
12
+ Scenario: Attempting to delete a project without having an API key entry in the CodeFumes config file
13
+ Given I have cloned and synchronized 1 project
14
+ And I have claimed the project
15
+ When I delete the project
16
+ Then the output should contain 1 successful delete messages
17
+ And the exit status should be 0
18
+
19
+ Scenario: Deleting one of multiple projects in your CodeFumes config file
20
+ Given I have cloned and synchronized 2 projects
21
+ And I have claimed the 1st project
22
+ When I delete the 1st project
23
+ Then the output should contain 1 successful delete messages
24
+ And the exit status should be 0
25
+
26
+ Scenario: Releasing all projects in your CodeFumes config file
27
+ Given valid user credentials have been stored in the CodeFumes config file
28
+ And I have cloned and synchronized 2 projects
29
+ And I run "#{@bin_path}/fumes claim -a"
30
+ When I run "#{@bin_path}/fumes delete -a"
31
+ Then the output should contain 2 successful delete messages
32
+ And the exit status should be 0
@@ -0,0 +1,50 @@
1
+ Feature: Claiming a project
2
+ As the owner of a project, the process of relinquishing ownership
3
+ of a project must be both available and simple.
4
+
5
+
6
+ Scenario: Specified project does not exist on CodeFumes.com
7
+ Given valid user credentials have been stored in the CodeFumes config file
8
+ When I run "#{@bin_path}/fumes release -p bad-public-key"
9
+ Then the output should contain "Not Found"
10
+ And the exit status should be 0
11
+
12
+ Scenario: Attempting to claim a project without having an API key entry in the CodeFumes config file
13
+ And I have cloned and synchronized 1 project
14
+ And I have claimed the project
15
+ When I release the project
16
+ Then the output should contain instructions about storing your API key
17
+ And the exit status should be 3
18
+
19
+ Scenario: Attempting to release a project with an invalid API key entry in the user's CodeFumes config file
20
+ Given invalid user credentials have been stored in the CodeFumes config file
21
+ And I have cloned and synchronized 1 project
22
+ And I have claimed the project
23
+ And invalid user credentials have been stored in the CodeFumes config file
24
+ When I release the project
25
+ Then the output should contain "Denied"
26
+ And the exit status should be 0
27
+
28
+ Scenario: Releasing a project using the key stored in a CodeFumes project directory
29
+ Given valid user credentials have been stored in the CodeFumes config file
30
+ And I have cloned and synchronized 1 project
31
+ And I have claimed the project
32
+ When I release the project
33
+ Then the output should contain "Success"
34
+ And the exit status should be 0
35
+
36
+ Scenario: Releasing one of multiple projects in your CodeFumes config file
37
+ Given valid user credentials have been stored in the CodeFumes config file
38
+ And I have cloned and synchronized 2 projects
39
+ And I have claimed the 1st project
40
+ When I release the 1st project
41
+ Then the output should contain 1 successful release message
42
+ And the exit status should be 0
43
+
44
+ Scenario: Releasing all projects in your CodeFumes config file
45
+ Given valid user credentials have been stored in the CodeFumes config file
46
+ And I have cloned and synchronized 2 projects
47
+ And I run "#{@bin_path}/fumes claim -a"
48
+ When I run "#{@bin_path}/fumes release -a"
49
+ Then the output should contain 2 successful release messages
50
+ And the exit status should be 0
@@ -0,0 +1,98 @@
1
+ # still a super-hack...but at least it's not duplicated, right?
2
+ # ...
3
+ # ...right?
4
+ def output_message_qty(action)
5
+ combined_output.scan(/#{action}\.\.\.'.*': Success/).count
6
+ end
7
+
8
+ def clone_fixture_repo_into(dir_name)
9
+ Given "I run \"git clone git@github.com:cosyn/git_fixture_repository.git #{dir_name}\""
10
+ end
11
+
12
+ # TODO: Get a better way of testing this...output is horrible
13
+ Then /^the output should contain (\d+) successful claim message[s]?$/ do |count|
14
+ output_message_qty("Claiming").should == count.to_i
15
+ end
16
+
17
+ # TODO: Get a better way of testing this...output is horrible
18
+ Then /^the output should contain (\d+) successful release message[s]?$/ do |count|
19
+ output_message_qty("Releasing").should == count.to_i
20
+ end
21
+
22
+ # TODO: Get a better way of testing this...output is horrible
23
+ Then /^the output should contain (\d+) successful delete message[s]?$/ do |count|
24
+ output_message_qty("Deleting").should == count.to_i
25
+ end
26
+
27
+ Then /^the output should contain instructions about storing your API key$/ do
28
+ Then "the output should contain \"fumes setup\""
29
+ end
30
+
31
+ Given /^I have cloned and synchronized (\d+) project[s]?$/ do |qty|
32
+ (1..qty.to_i).each do |index|
33
+ dir_name = "project_#{index}"
34
+ clone_fixture_repo_into(dir_name)
35
+ And "I synchronize project #{index}"
36
+ end
37
+ end
38
+
39
+ Given /^I have cloned (\d+) project[s]?$/ do |qty|
40
+ (1..qty.to_i).each do |index|
41
+ dir_name = "project_#{index}"
42
+ clone_fixture_repo_into(dir_name)
43
+ end
44
+ end
45
+
46
+ When /^I (?:have )?synchronize[d]? project (\d+)$/ do |index|
47
+ When "I cd to \"project_#{index}/\""
48
+ And "I run \"#{@bin_path}/fumes sync\""
49
+ And "I cd to \"../\""
50
+ end
51
+
52
+ # convenience step...assumes only one project
53
+ When /^I (?:have)?synchronize[d]? the project$/ do
54
+ When "I synchronize project 1"
55
+ end
56
+
57
+ Given /^I (?:have )?claim(?:ed)? the (\d+)st project$/ do |index|
58
+ dir_name = "project_#{index}"
59
+ And "I cd to \"#{dir_name}/\""
60
+ And "I run \"#{@bin_path}/fumes claim\""
61
+ And "I cd to \"../\""
62
+ end
63
+
64
+ # convenience step...assumes only one project
65
+ Given /^I (?:have )?claim(?:ed)? the project$/ do
66
+ Given "I have claimed the 1st project"
67
+ end
68
+
69
+ Given /^I (?:have )?release[d]? the (\d+)(?:st|nd|rd|th) project$/ do |index|
70
+ dir_name = "project_#{index}"
71
+ And "I cd to \"#{dir_name}/\""
72
+ And "I run \"#{@bin_path}/fumes release\""
73
+ And "I cd to \"../\""
74
+ end
75
+
76
+ # TODO: Refactor w/ other actions
77
+ Given /^I (?:have )?delete[d]? the (\d+)(?:st|nd|rd|th) project$/ do |index|
78
+ dir_name = "project_#{index}"
79
+ And "I cd to \"#{dir_name}/\""
80
+ And "I run \"#{@bin_path}/fumes delete\""
81
+ And "I cd to \"../\""
82
+ end
83
+
84
+ # convenience step...assumes only one project
85
+ Given /^I (?:have )?delete(?:ed)? the project$/ do
86
+ Given "I have deleted the 1st project"
87
+ end
88
+
89
+
90
+ # convenience step...assumes only one project
91
+ Given /^I (?:have )?release[d]? the project$/ do
92
+ Given "I have released the 1st project"
93
+ end
94
+
95
+ Then /^the API key in the config file should be ("[^"]*"|cleared)$/ do |api_key_value|
96
+ expected_value = api_key_value == 'cleared' ? nil : api_key_value.gsub(/"/,'')
97
+ ConfigFile.api_key.should == expected_value
98
+ end
@@ -0,0 +1,168 @@
1
+ Given /^this project is active project folder/ do
2
+ @active_project_folder = File.expand_path(File.dirname(__FILE__) + "/../..")
3
+ end
4
+
5
+ Given /^env variable \$([\w_]+) set to "(.*)"/ do |env_var, value|
6
+ ENV[env_var] = value
7
+ end
8
+
9
+ Given /"(.*)" folder is deleted/ do |folder|
10
+ in_project_folder { FileUtils.rm_rf folder }
11
+ end
12
+
13
+ When /^I invoke "(.*)" generator with arguments "(.*)"$/ do |generator, arguments|
14
+ @stdout = StringIO.new
15
+ in_project_folder do
16
+ if Object.const_defined?("APP_ROOT")
17
+ APP_ROOT.replace(FileUtils.pwd)
18
+ else
19
+ APP_ROOT = FileUtils.pwd
20
+ end
21
+ run_generator(generator, arguments.split(' '), SOURCES, :stdout => @stdout)
22
+ end
23
+ File.open(File.join(@tmp_root, "generator.out"), "w") do |f|
24
+ @stdout.rewind
25
+ f << @stdout.read
26
+ end
27
+ end
28
+
29
+ When /^I run executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
30
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
31
+ in_project_folder do
32
+ system "#{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
33
+ end
34
+ end
35
+
36
+ When /^I run project executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
37
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
38
+ in_project_folder do
39
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
40
+ end
41
+ end
42
+
43
+ When /^I run local executable "(.*)" with arguments "(.*)"/ do |executable, arguments|
44
+ @stdout = File.expand_path(File.join(@tmp_root, "executable.out"))
45
+ executable = File.expand_path(File.join(File.dirname(__FILE__), "/../../bin", executable))
46
+ in_project_folder do
47
+ system "ruby #{executable} #{arguments} > #{@stdout} 2> #{@stdout}"
48
+ end
49
+ end
50
+
51
+ When /^I invoke task "rake (.*)"/ do |task|
52
+ @stdout = File.expand_path(File.join(@tmp_root, "tests.out"))
53
+ in_project_folder do
54
+ system "rake #{task} --trace > #{@stdout} 2> #{@stdout}"
55
+ end
56
+ end
57
+
58
+ Then /^folder "(.*)" (is|is not) created/ do |folder, is|
59
+ in_project_folder do
60
+ File.exists?(folder).should(is == 'is' ? be_true : be_false)
61
+ end
62
+ end
63
+
64
+ Then /^file "(.*)" (is|is not) created/ do |file, is|
65
+ in_project_folder do
66
+ File.exists?(file).should(is == 'is' ? be_true : be_false)
67
+ end
68
+ end
69
+
70
+ Then /^file with name matching "(.*)" is created/ do |pattern|
71
+ in_project_folder do
72
+ Dir[pattern].should_not be_empty
73
+ end
74
+ end
75
+
76
+ Then /^file "(.*)" contents (does|does not) match \/(.*)\// do |file, does, regex|
77
+ in_project_folder do
78
+ actual_output = File.read(file)
79
+ (does == 'does') ?
80
+ actual_output.should(match(/#{regex}/)) :
81
+ actual_output.should_not(match(/#{regex}/))
82
+ end
83
+ end
84
+
85
+ Then /gem file "(.*)" and generated file "(.*)" should be the same/ do |gem_file, project_file|
86
+ File.exists?(gem_file).should be_true
87
+ File.exists?(project_file).should be_true
88
+ gem_file_contents = File.read(File.dirname(__FILE__) + "/../../#{gem_file}")
89
+ project_file_contents = File.read(File.join(@active_project_folder, project_file))
90
+ project_file_contents.should == gem_file_contents
91
+ end
92
+
93
+ Then /^(does|does not) invoke generator "(.*)"$/ do |does_invoke, generator|
94
+ actual_output = File.read(@stdout)
95
+ does_invoke == "does" ?
96
+ actual_output.should(match(/dependency\s+#{generator}/)) :
97
+ actual_output.should_not(match(/dependency\s+#{generator}/))
98
+ end
99
+
100
+ Then /help options "(.*)" and "(.*)" are displayed/ do |opt1, opt2|
101
+ actual_output = File.read(@stdout)
102
+ actual_output.should match(/#{opt1}/)
103
+ actual_output.should match(/#{opt2}/)
104
+ end
105
+
106
+ Then /^I should see "([^\"]*)"$/ do |text|
107
+ actual_output = File.read(@stdout)
108
+ actual_output.should contain(text)
109
+ end
110
+
111
+ Then /^I should see$/ do |text|
112
+ actual_output = File.read(@stdout)
113
+ actual_output.should contain(text)
114
+ end
115
+
116
+ Then /^I should not see$/ do |text|
117
+ actual_output = File.read(@stdout)
118
+ actual_output.should_not contain(text)
119
+ end
120
+
121
+ Then /^I should see exactly$/ do |text|
122
+ actual_output = File.read(@stdout)
123
+ actual_output.should == text
124
+ end
125
+
126
+ Then /^I should see all (\d+) tests pass/ do |expected_test_count|
127
+ expected = %r{^#{expected_test_count} tests, \d+ assertions, 0 failures, 0 errors}
128
+ actual_output = File.read(@stdout)
129
+ actual_output.should match(expected)
130
+ end
131
+
132
+ Then /^I should see all (\d+) examples pass/ do |expected_test_count|
133
+ expected = %r{^#{expected_test_count} examples?, 0 failures}
134
+ actual_output = File.read(@stdout)
135
+ actual_output.should match(expected)
136
+ end
137
+
138
+ Then /^yaml file "(.*)" contains (\{.*\})/ do |file, yaml|
139
+ in_project_folder do
140
+ yaml = eval yaml
141
+ YAML.load(File.read(file)).should == yaml
142
+ end
143
+ end
144
+
145
+ Then /^Rakefile can display tasks successfully/ do
146
+ @stdout = File.expand_path(File.join(@tmp_root, "rakefile.out"))
147
+ in_project_folder do
148
+ system "rake -T > #{@stdout} 2> #{@stdout}"
149
+ end
150
+ actual_output = File.read(@stdout)
151
+ actual_output.should match(/^rake\s+\w+\s+#\s.*/)
152
+ end
153
+
154
+ Then /^task "rake (.*)" is executed successfully/ do |task|
155
+ @stdout.should_not be_nil
156
+ actual_output = File.read(@stdout)
157
+ actual_output.should_not match(/^Don't know how to build task '#{task}'/)
158
+ actual_output.should_not match(/Error/i)
159
+ end
160
+
161
+ Then /^gem spec key "(.*)" contains \/(.*)\// do |key, regex|
162
+ in_project_folder do
163
+ gem_file = Dir["pkg/*.gem"].first
164
+ gem_spec = Gem::Specification.from_yaml(`gem spec #{gem_file}`)
165
+ spec_value = gem_spec.send(key.to_sym)
166
+ spec_value.to_s.should match(/#{regex}/)
167
+ end
168
+ end
@@ -0,0 +1,19 @@
1
+ Given /^I am in the CodeFumes gem's root directory$/ do
2
+ cd("/" + File.expand_path(__FILE__) + "/../")
3
+ end
4
+
5
+ Given /^(in)?valid user credentials have been stored in the CodeFumes config file$/ do |invalid_text|
6
+ # Fragile, as this user is not set up to be re-added after wiping test db on CodeFumes.com
7
+ # TODO: update deployment for test site to reload this information:
8
+ # login: test-user
9
+ # password: password
10
+ # email: test.user@codefumes.com
11
+ # api_key: eX2Kue_6YoEIWAvV1VCx
12
+
13
+ unless ConfigFile.path.match(/#{File.expand_path(@tmp_root)}/)
14
+ raise "###NOTE### ConfigFile path not set to use temp file. Check CODEFUMES_CONFIG_FILE settings!"
15
+ end
16
+
17
+ api_key = invalid_text.blank? ? 'eX2Kue_6YoEIWAvV1VCx' : 'invalid-key-here'
18
+ ConfigFile.save_credentials(api_key)
19
+ end
@@ -0,0 +1,41 @@
1
+ Feature: Storing a User's API key
2
+ Adding an API key to a user's CodeFumes config file allows
3
+ them to perform actions which require user-authentication,
4
+ such as associating a project with an account.
5
+
6
+
7
+ Scenario: Issuing 'api-key' without an argument
8
+ When I run "#{@bin_path}/fumes api-key"
9
+ Then it should fail with:
10
+ """
11
+ No API key specified
12
+ """
13
+ And the exit status should be 5
14
+
15
+ Scenario: Issuing 'api-key' with an argument
16
+ When I run "#{@bin_path}/fumes api-key userkey"
17
+ Then it should pass with:
18
+ """
19
+ Your API key has been saved to your CodeFumes config file
20
+ """
21
+ And the API key in the config file should be "userkey"
22
+ And the exit status should be 0
23
+
24
+ Scenario: Issuing 'api-key' with the --clear flag
25
+ When I run "#{@bin_path}/fumes api-key --clear"
26
+ Then it should pass with:
27
+ """
28
+ Your API key has been removed from your CodeFumes config file
29
+ """
30
+ And the API key in the config file should be cleared
31
+ And the exit status should be 0
32
+
33
+ Scenario: Issuing 'api-key' with the --clear flag and an argument
34
+ When I run "#{@bin_path}/fumes api-key userkey1"
35
+ And I run "#{@bin_path}/fumes api-key --clear userkey2"
36
+ Then it should pass with:
37
+ """
38
+ Your API key has been removed from your CodeFumes config file
39
+ """
40
+ And the API key in the config file should be cleared
41
+ And the exit status should be 0