codefumes 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +2 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +81 -135
- data/History.txt +27 -0
- data/Manifest.txt +4 -0
- data/bin/fumes +84 -7
- data/features/claiming_a_project.feature +5 -5
- data/features/deleting_a_project.feature +4 -4
- data/features/managing_project_build_status.feature +63 -0
- data/features/releasing_a_project.feature +6 -6
- data/features/step_definitions/cli_steps.rb +12 -2
- data/features/storing_user_api_key.feature +4 -4
- data/features/synchronizing_repository_with_project.feature +4 -4
- data/lib/codefumes.rb +1 -1
- data/lib/codefumes/api/build.rb +8 -0
- data/lib/codefumes/api/commit.rb +22 -0
- data/lib/codefumes/cli_helpers.rb +8 -0
- data/lib/codefumes/errors.rb +6 -0
- data/lib/codefumes/exit_codes.rb +13 -9
- data/spec/codefumes/api/build_spec.rb +35 -3
- data/spec/codefumes/api/commit_spec.rb +23 -0
- data/spec/codefumes_service_helpers.rb +6 -0
- data/spec/fixtures/builds.xml +21 -0
- data/spec/fixtures/no_builds.xml +3 -0
- data/tasks/cucumber.rake +6 -0
- metadata +77 -44
@@ -7,21 +7,21 @@ Feature: Deleting a project
|
|
7
7
|
Scenario: The specified project does not exist on CodeFumes.com
|
8
8
|
When I run "#{@bin_path}/fumes delete -p bad-public-key"
|
9
9
|
Then the output should contain "Not Found"
|
10
|
-
And the exit status should be
|
10
|
+
And the exit status should be "SUCCESS"
|
11
11
|
|
12
12
|
Scenario: Attempting to delete a project without having an API key entry in the CodeFumes config file
|
13
13
|
Given I have cloned and synchronized 1 project
|
14
14
|
And I have claimed the project
|
15
15
|
When I delete the project
|
16
16
|
Then the output should contain 1 successful delete messages
|
17
|
-
And the exit status should be
|
17
|
+
And the exit status should be "SUCCESS"
|
18
18
|
|
19
19
|
Scenario: Deleting one of multiple projects in your CodeFumes config file
|
20
20
|
Given I have cloned and synchronized 2 projects
|
21
21
|
And I have claimed the 1st project
|
22
22
|
When I delete the 1st project
|
23
23
|
Then the output should contain 1 successful delete messages
|
24
|
-
And the exit status should be
|
24
|
+
And the exit status should be "SUCCESS"
|
25
25
|
|
26
26
|
Scenario: Releasing all projects in your CodeFumes config file
|
27
27
|
Given valid user credentials have been stored in the CodeFumes config file
|
@@ -29,4 +29,4 @@ Feature: Deleting a project
|
|
29
29
|
And I run "#{@bin_path}/fumes claim -a"
|
30
30
|
When I run "#{@bin_path}/fumes delete -a"
|
31
31
|
Then the output should contain 2 successful delete messages
|
32
|
-
And the exit status should be
|
32
|
+
And the exit status should be "SUCCESS"
|
@@ -0,0 +1,63 @@
|
|
1
|
+
Feature: Managing a project's build status
|
2
|
+
Tracking the build status of a project is one of, if not
|
3
|
+
THE primary purpose of CodeFumes.com. As the owner of a project
|
4
|
+
I want to be able to easily manage and the state of an read
|
5
|
+
the state of various builds for a project.
|
6
|
+
|
7
|
+
Scenario: Starting a build for a project
|
8
|
+
Given I have cloned and synchronized 1 project
|
9
|
+
And I cd to "project_1/"
|
10
|
+
When I run "#{@bin_path}/fumes build --start ie7"
|
11
|
+
Then the output should contain "Setting 'ie7' build status to 'started'"
|
12
|
+
And the output should contain "'ie7' build successfully marked as 'started'"
|
13
|
+
And the exit status should be "SUCCESS"
|
14
|
+
|
15
|
+
Scenario: Setting a project build status to 'failure'
|
16
|
+
Given I have cloned and synchronized 1 project
|
17
|
+
And I cd to "project_1/"
|
18
|
+
And I run "#{@bin_path}/fumes build --start ie7"
|
19
|
+
When I run "#{@bin_path}/fumes build --finished=failed ie7"
|
20
|
+
Then the output should contain "Setting 'ie7' build status to 'failed'"
|
21
|
+
And the output should contain "'ie7' build successfully marked as 'failed'"
|
22
|
+
And the exit status should be "SUCCESS"
|
23
|
+
|
24
|
+
Scenario: Setting a project build status to an invalid state
|
25
|
+
Given I have cloned and synchronized 1 project
|
26
|
+
And I cd to "project_1/"
|
27
|
+
And I run "#{@bin_path}/fumes build --start ie7"
|
28
|
+
When I run "#{@bin_path}/fumes build --finished=badstate ie7"
|
29
|
+
Then the output should contain "Invalid build state"
|
30
|
+
And the exit status should be "INVALID_BUILD_STATE"
|
31
|
+
|
32
|
+
Scenario: Attempting to set multiple build states in same command
|
33
|
+
Given I have cloned and synchronized 1 project
|
34
|
+
And I cd to "project_1/"
|
35
|
+
And I run "#{@bin_path}/fumes build --start ie7"
|
36
|
+
When I run "#{@bin_path}/fumes build --finished=failed --start ie7"
|
37
|
+
Then the output should contain "multiple states"
|
38
|
+
And the exit status should be "INVALID_COMMAND_SYNTAX"
|
39
|
+
|
40
|
+
Scenario: Retrieving the current build state of a specific build
|
41
|
+
Given I have cloned and synchronized 1 project
|
42
|
+
And I cd to "project_1/"
|
43
|
+
And I run "#{@bin_path}/fumes build --start ie7"
|
44
|
+
When I run "#{@bin_path}/fumes build --status ie7"
|
45
|
+
Then the output should contain "running"
|
46
|
+
And the exit status should be "SUCCESS"
|
47
|
+
|
48
|
+
Scenario: Retrieving the current build state of a all builds of the latest commit
|
49
|
+
Given I have cloned and synchronized 1 project
|
50
|
+
And I cd to "project_1/"
|
51
|
+
And I run "#{@bin_path}/fumes build --start ie7"
|
52
|
+
And I run "#{@bin_path}/fumes build --start specs"
|
53
|
+
When I run "#{@bin_path}/fumes build --status --all"
|
54
|
+
Then the output should contain 2 running build status messages
|
55
|
+
And the exit status should be "SUCCESS"
|
56
|
+
|
57
|
+
Scenario: Running the build command without any arguments
|
58
|
+
Given I have cloned and synchronized 1 project
|
59
|
+
And I cd to "project_1/"
|
60
|
+
When I run "#{@bin_path}/fumes build"
|
61
|
+
Then the output should contain "build [options]"
|
62
|
+
Then the output should contain "Options:"
|
63
|
+
And the exit status should be "SUCCESS"
|
@@ -7,14 +7,14 @@ Feature: Claiming a project
|
|
7
7
|
Given valid user credentials have been stored in the CodeFumes config file
|
8
8
|
When I run "#{@bin_path}/fumes release -p bad-public-key"
|
9
9
|
Then the output should contain "Not Found"
|
10
|
-
And the exit status should be
|
10
|
+
And the exit status should be "SUCCESS"
|
11
11
|
|
12
12
|
Scenario: Attempting to claim a project without having an API key entry in the CodeFumes config file
|
13
13
|
And I have cloned and synchronized 1 project
|
14
14
|
And I have claimed the project
|
15
15
|
When I release the project
|
16
16
|
Then the output should contain instructions about storing your API key
|
17
|
-
And the exit status should be
|
17
|
+
And the exit status should be "NO_USER_CREDENTIALS"
|
18
18
|
|
19
19
|
Scenario: Attempting to release a project with an invalid API key entry in the user's CodeFumes config file
|
20
20
|
Given invalid user credentials have been stored in the CodeFumes config file
|
@@ -23,7 +23,7 @@ Feature: Claiming a project
|
|
23
23
|
And invalid user credentials have been stored in the CodeFumes config file
|
24
24
|
When I release the project
|
25
25
|
Then the output should contain "Denied"
|
26
|
-
And the exit status should be
|
26
|
+
And the exit status should be "SUCCESS"
|
27
27
|
|
28
28
|
Scenario: Releasing a project using the key stored in a CodeFumes project directory
|
29
29
|
Given valid user credentials have been stored in the CodeFumes config file
|
@@ -31,7 +31,7 @@ Feature: Claiming a project
|
|
31
31
|
And I have claimed the project
|
32
32
|
When I release the project
|
33
33
|
Then the output should contain "Success"
|
34
|
-
And the exit status should be
|
34
|
+
And the exit status should be "SUCCESS"
|
35
35
|
|
36
36
|
Scenario: Releasing one of multiple projects in your CodeFumes config file
|
37
37
|
Given valid user credentials have been stored in the CodeFumes config file
|
@@ -39,7 +39,7 @@ Feature: Claiming a project
|
|
39
39
|
And I have claimed the 1st project
|
40
40
|
When I release the 1st project
|
41
41
|
Then the output should contain 1 successful release message
|
42
|
-
And the exit status should be
|
42
|
+
And the exit status should be "SUCCESS"
|
43
43
|
|
44
44
|
Scenario: Releasing all projects in your CodeFumes config file
|
45
45
|
Given valid user credentials have been stored in the CodeFumes config file
|
@@ -47,4 +47,4 @@ Feature: Claiming a project
|
|
47
47
|
And I run "#{@bin_path}/fumes claim -a"
|
48
48
|
When I run "#{@bin_path}/fumes release -a"
|
49
49
|
Then the output should contain 2 successful release messages
|
50
|
-
And the exit status should be
|
50
|
+
And the exit status should be "SUCCESS"
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# still a super-hack...but at least it's not duplicated, right?
|
2
2
|
# ...
|
3
3
|
# ...right?
|
4
|
-
def output_message_qty(action)
|
5
|
-
combined_output.scan(/#{action}\.\.\.'.*':
|
4
|
+
def output_message_qty(action, result = "Success")
|
5
|
+
combined_output.scan(/#{action}\.\.\.'.*': #{result}/).count
|
6
6
|
end
|
7
7
|
|
8
8
|
def clone_fixture_repo_into(dir_name)
|
@@ -24,6 +24,12 @@ Then /^the output should contain (\d+) successful delete message[s]?$/ do |count
|
|
24
24
|
output_message_qty("Deleting").should == count.to_i
|
25
25
|
end
|
26
26
|
|
27
|
+
# TODO: Get a better way of testing this...output is horrible
|
28
|
+
Then /^the output should contain (\d+) running build status message[s]?$/ do |count|
|
29
|
+
running_status_count = combined_output.scan(/'.*' build: running/).count
|
30
|
+
running_status_count.should == count.to_i
|
31
|
+
end
|
32
|
+
|
27
33
|
Then /^the output should contain instructions about storing your API key$/ do
|
28
34
|
Then "the output should contain \"fumes setup\""
|
29
35
|
end
|
@@ -96,3 +102,7 @@ Then /^the API key in the config file should be ("[^"]*"|cleared)$/ do |api_key_
|
|
96
102
|
expected_value = api_key_value == 'cleared' ? nil : api_key_value.gsub(/"/,'')
|
97
103
|
ConfigFile.api_key.should == expected_value
|
98
104
|
end
|
105
|
+
|
106
|
+
Then /^the exit status should be "([^"]*)"$/ do |exit_code_name|
|
107
|
+
Then "the exit status should be #{instance_eval("CodeFumes::ExitCodes::#{exit_code_name}")}"
|
108
|
+
end
|
@@ -10,7 +10,7 @@ Feature: Storing a User's API key
|
|
10
10
|
"""
|
11
11
|
No API key specified
|
12
12
|
"""
|
13
|
-
And the exit status should be
|
13
|
+
And the exit status should be "NO_API_KEY_SPECIFIED"
|
14
14
|
|
15
15
|
Scenario: Issuing 'api-key' with an argument
|
16
16
|
When I run "#{@bin_path}/fumes api-key userkey"
|
@@ -19,7 +19,7 @@ Feature: Storing a User's API key
|
|
19
19
|
Your API key has been saved to your CodeFumes config file
|
20
20
|
"""
|
21
21
|
And the API key in the config file should be "userkey"
|
22
|
-
And the exit status should be
|
22
|
+
And the exit status should be "SUCCESS"
|
23
23
|
|
24
24
|
Scenario: Issuing 'api-key' with the --clear flag
|
25
25
|
When I run "#{@bin_path}/fumes api-key --clear"
|
@@ -28,7 +28,7 @@ Feature: Storing a User's API key
|
|
28
28
|
Your API key has been removed from your CodeFumes config file
|
29
29
|
"""
|
30
30
|
And the API key in the config file should be cleared
|
31
|
-
And the exit status should be
|
31
|
+
And the exit status should be "SUCCESS"
|
32
32
|
|
33
33
|
Scenario: Issuing 'api-key' with the --clear flag and an argument
|
34
34
|
When I run "#{@bin_path}/fumes api-key userkey1"
|
@@ -38,4 +38,4 @@ Feature: Storing a User's API key
|
|
38
38
|
Your API key has been removed from your CodeFumes config file
|
39
39
|
"""
|
40
40
|
And the API key in the config file should be cleared
|
41
|
-
And the exit status should be
|
41
|
+
And the exit status should be "SUCCESS"
|
@@ -10,12 +10,12 @@ Feature: Synchronizing a repository with CodeFumes
|
|
10
10
|
"""
|
11
11
|
Unsupported
|
12
12
|
"""
|
13
|
-
And the exit status should be
|
13
|
+
And the exit status should be "UNSUPPORTED_SCM"
|
14
14
|
|
15
15
|
Scenario: Successful synchronization
|
16
16
|
Given I have cloned 1 project
|
17
17
|
When I synchronize the project
|
18
|
-
Then the exit status should be
|
18
|
+
Then the exit status should be "SUCCESS"
|
19
19
|
And the output should contain "Successfully saved"
|
20
20
|
And the output should contain "Visit http://"
|
21
21
|
|
@@ -24,10 +24,10 @@ Feature: Synchronizing a repository with CodeFumes
|
|
24
24
|
When I synchronize the project
|
25
25
|
Then the output should contain "non-production"
|
26
26
|
And the output should contain "test.codefumes.com"
|
27
|
-
And the exit status should be
|
27
|
+
And the exit status should be "SUCCESS"
|
28
28
|
|
29
29
|
Scenario: Specifying a custom, but non-existant public/private key combination
|
30
30
|
Given I have cloned 1 project
|
31
31
|
And I cd to "project_1/"
|
32
32
|
When I run "#{@bin_path}/fumes sync -p non-existant-pubkey -a non-existant-privkey"
|
33
|
-
And the exit status should be
|
33
|
+
And the exit status should be "PROJECT_NOT_FOUND"
|
data/lib/codefumes.rb
CHANGED
data/lib/codefumes/api/build.rb
CHANGED
@@ -6,6 +6,7 @@ module CodeFumes
|
|
6
6
|
# the current status (running, failed, success) and the
|
7
7
|
# start & end times of the Build process.
|
8
8
|
class Build
|
9
|
+
VALID_BUILD_RESULT_STATES = [:running,:failed,:successful]
|
9
10
|
attr_reader :created_at, :api_uri, :identifier, :commit, :project
|
10
11
|
attr_accessor :started_at, :ended_at, :state, :name
|
11
12
|
|
@@ -25,6 +26,7 @@ module CodeFumes
|
|
25
26
|
@state = state.to_s
|
26
27
|
@started_at = options[:started_at] || options['started_at'] || Time.now
|
27
28
|
@ended_at = options[:ended_at] || options['ended_at']
|
29
|
+
validate_build_state
|
28
30
|
end
|
29
31
|
|
30
32
|
# Overrides existing attributes with those supplied in +options+. This
|
@@ -59,6 +61,7 @@ module CodeFumes
|
|
59
61
|
# ---
|
60
62
|
# TODO: Make this consistent w/ other class' create/update handling
|
61
63
|
def save
|
64
|
+
validate_build_state
|
62
65
|
response = exists? ? update : create
|
63
66
|
|
64
67
|
case response.code
|
@@ -134,6 +137,11 @@ module CodeFumes
|
|
134
137
|
def standard_content_hash
|
135
138
|
{:name => name,:started_at => started_at, :ended_at => ended_at, :state => state}
|
136
139
|
end
|
140
|
+
|
141
|
+
def validate_build_state
|
142
|
+
raise(Errors::InvalidBuildState) if state.nil? || state.empty?
|
143
|
+
raise(Errors::InvalidBuildState) unless VALID_BUILD_RESULT_STATES.include?(state.to_sym)
|
144
|
+
end
|
137
145
|
end
|
138
146
|
end
|
139
147
|
end
|
data/lib/codefumes/api/commit.rb
CHANGED
@@ -139,12 +139,34 @@ module CodeFumes
|
|
139
139
|
latest_commit.nil? ? nil : latest_commit.identifier
|
140
140
|
end
|
141
141
|
|
142
|
+
# Returns the all associated builds
|
143
|
+
def builds
|
144
|
+
response = API.get("/projects/#{project.public_key}/commits/#{identifier}/builds")
|
145
|
+
|
146
|
+
case response.code
|
147
|
+
when 200
|
148
|
+
builds_returned(response).map do |build_data|
|
149
|
+
build_name = build_data.delete("name")
|
150
|
+
build_state = build_data.delete("state")
|
151
|
+
Build.new(self, build_name, build_state, build_data)
|
152
|
+
end
|
153
|
+
else
|
154
|
+
nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
142
158
|
private
|
143
159
|
def convert_custom_attributes_keys_to_symbols
|
144
160
|
@custom_attributes = @custom_attributes.inject({}) do |results, key_and_value|
|
145
161
|
results.merge! key_and_value.first.to_sym => key_and_value.last
|
146
162
|
end
|
147
163
|
end
|
164
|
+
|
165
|
+
def builds_returned(response)
|
166
|
+
return [] if response["builds"].nil? || response["builds"].empty?
|
167
|
+
return [] if response["builds"]["build"].nil? || response["builds"]["build"].empty?
|
168
|
+
[response["builds"]["build"]].flatten
|
169
|
+
end
|
148
170
|
end
|
149
171
|
end
|
150
172
|
end
|
@@ -10,6 +10,10 @@ module CodeFumes
|
|
10
10
|
puts "NOTE: Sending all requests & data to non-production server! (#{API.base_uri})"
|
11
11
|
end
|
12
12
|
|
13
|
+
def issuing_build_command?(command)
|
14
|
+
command && command.name.to_sym == :build
|
15
|
+
end
|
16
|
+
|
13
17
|
def issue_project_commands(message, public_keys, &block)
|
14
18
|
public_keys.each do |public_key|
|
15
19
|
print "#{message}...'#{public_key}': "
|
@@ -50,5 +54,9 @@ module CodeFumes
|
|
50
54
|
def open_in_browser(url, &block)
|
51
55
|
has_launchy? {Launchy::Browser.new.visit url}
|
52
56
|
end
|
57
|
+
|
58
|
+
def multiple_build_states?(options)
|
59
|
+
options[:start] && options[:finished]
|
60
|
+
end
|
53
61
|
end
|
54
62
|
end
|
data/lib/codefumes/errors.rb
CHANGED
@@ -9,12 +9,18 @@ module CodeFumes
|
|
9
9
|
class UnknownProjectError < StandardError #:nodoc:
|
10
10
|
end
|
11
11
|
|
12
|
+
class InvalidCommandSyntax < StandardError #:nodoc:
|
13
|
+
end
|
14
|
+
|
12
15
|
class NoUserApiKeyError < ArgumentError #:nodoc:
|
13
16
|
end
|
14
17
|
|
15
18
|
class NoApiKeySpecified < ArgumentError #:nodoc:
|
16
19
|
end
|
17
20
|
|
21
|
+
class InvalidBuildState < ArgumentError #:nodoc:
|
22
|
+
end
|
23
|
+
|
18
24
|
class MissingLaunchyGem < Gem::LoadError #:nodoc:
|
19
25
|
end
|
20
26
|
end
|
data/lib/codefumes/exit_codes.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module CodeFumes
|
2
|
+
module ExitCodes
|
3
|
+
SUCCESS = 0
|
4
|
+
UNSUPPORTED_SCM = 1
|
5
|
+
PROJECT_NOT_FOUND = 2
|
6
|
+
NO_USER_CREDENTIALS = 3
|
7
|
+
INCORRECT_USER_CREDENTIALS = 4
|
8
|
+
NO_API_KEY_SPECIFIED = 5
|
9
|
+
MISSING_DEPENDENCY = 6
|
10
|
+
INVALID_BUILD_STATE = 7
|
11
|
+
INVALID_COMMAND_SYNTAX = 8
|
12
|
+
UNKNOWN = 100
|
13
|
+
end
|
10
14
|
end
|
@@ -14,7 +14,7 @@ describe "API::Build" do
|
|
14
14
|
@build = Build.new(@commit, @build_name, @state, {:started_at => @started_at})
|
15
15
|
end
|
16
16
|
|
17
|
-
describe "save" do
|
17
|
+
describe "#save" do
|
18
18
|
it "sets basic auth with the public and private key" do
|
19
19
|
@build.stub!(:exists?).and_return(false)
|
20
20
|
register_create_uri(["401", "Unauthorized"], "")
|
@@ -26,6 +26,22 @@ describe "API::Build" do
|
|
26
26
|
@build.save
|
27
27
|
end
|
28
28
|
|
29
|
+
it "raises an exception when an invalid state is specified" do
|
30
|
+
lambda {
|
31
|
+
setup_fixture_base
|
32
|
+
@build.state = 'invalid_state'
|
33
|
+
@build.save
|
34
|
+
}.should raise_error(Errors::InvalidBuildState)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "does not raise an InvalidBuildState exception when a valid state is specified" do
|
38
|
+
lambda {
|
39
|
+
setup_fixture_base
|
40
|
+
@build.state = Build::VALID_BUILD_RESULT_STATES.first
|
41
|
+
@build.save
|
42
|
+
}.should_not raise_error(Errors::InvalidBuildState)
|
43
|
+
end
|
44
|
+
|
29
45
|
context "when it's a new build for the specified commit" do
|
30
46
|
before(:each) do
|
31
47
|
@build.stub!(:exists?).and_return(false)
|
@@ -70,7 +86,7 @@ describe "API::Build" do
|
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
73
|
-
describe "find" do
|
89
|
+
describe "#find" do
|
74
90
|
before(:each) do
|
75
91
|
setup_fixture_base
|
76
92
|
setup_build_fixtures
|
@@ -89,7 +105,7 @@ describe "API::Build" do
|
|
89
105
|
end
|
90
106
|
end
|
91
107
|
|
92
|
-
describe "destroy" do
|
108
|
+
describe "#destroy" do
|
93
109
|
before(:each) do
|
94
110
|
setup_fixture_base
|
95
111
|
setup_build_fixtures
|
@@ -106,4 +122,20 @@ describe "API::Build" do
|
|
106
122
|
@build.destroy.should be_false
|
107
123
|
end
|
108
124
|
end
|
125
|
+
|
126
|
+
describe "#new" do
|
127
|
+
it "raises an exception when an invalid state is specified" do
|
128
|
+
lambda {
|
129
|
+
setup_fixture_base
|
130
|
+
Build.new(@commit, @build_name, 'invalid_state')
|
131
|
+
}.should raise_error(Errors::InvalidBuildState)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "does not raise an InvalidBuildState exception when a valid state is specified" do
|
135
|
+
lambda {
|
136
|
+
setup_fixture_base
|
137
|
+
Build.new(@commit, @build_name, Build::VALID_BUILD_RESULT_STATES.first)
|
138
|
+
}.should_not raise_error(Errors::InvalidBuildState)
|
139
|
+
end
|
140
|
+
end
|
109
141
|
end
|