codefumes 0.2.2 → 0.3.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.
- 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
|