codefumes_harvester 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +17 -0
- data/PostInstall.txt +7 -0
- data/README.txt +114 -0
- data/Rakefile +40 -0
- data/bin/harvest_repo_metrics +10 -0
- data/lib/codefumes_harvester.rb +22 -0
- data/lib/codefumes_harvester/errors.rb +6 -0
- data/lib/codefumes_harvester/harvester.rb +110 -0
- data/lib/codefumes_harvester/quick_metric.rb +19 -0
- data/lib/codefumes_harvester/source_control.rb +132 -0
- data/lib/harvest_repo_metrics/cli.rb +63 -0
- data/spec/codometer_harvester/harvester_spec.rb +71 -0
- data/spec/codometer_harvester/source_control_spec.rb +199 -0
- data/spec/harvest_repo_metrics_cli_spec.rb +26 -0
- data/spec/spec_helper.rb +19 -0
- data/tasks/codefumes.rake +66 -0
- metadata +123 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
bin/harvest_repo_metrics
|
7
|
+
lib/codefumes_harvester.rb
|
8
|
+
lib/codefumes_harvester/errors.rb
|
9
|
+
lib/codefumes_harvester/harvester.rb
|
10
|
+
lib/codefumes_harvester/quick_metric.rb
|
11
|
+
lib/codefumes_harvester/source_control.rb
|
12
|
+
lib/harvest_repo_metrics/cli.rb
|
13
|
+
spec/codometer_harvester/harvester_spec.rb
|
14
|
+
spec/codometer_harvester/source_control_spec.rb
|
15
|
+
spec/harvest_repo_metrics_cli_spec.rb
|
16
|
+
spec/spec_helper.rb
|
17
|
+
tasks/codefumes.rake
|
data/PostInstall.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
= codefumes-harvester
|
2
|
+
|
3
|
+
* http://codefumes.com
|
4
|
+
* http://cf-harvester.rubyforge.org/rdoc
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
CodeFumesHarvester provides a set of high-level tools for gathering
|
9
|
+
history and common metrics from a local repository and sending them to
|
10
|
+
CodeFumes.com[http://codefumes.com].
|
11
|
+
|
12
|
+
== FEATURES/PROBLEMS:
|
13
|
+
|
14
|
+
=== Features
|
15
|
+
|
16
|
+
* Executable +harvest_repo_metrics+ script which gathers a repository's
|
17
|
+
commit lineage
|
18
|
+
* Rake tasks for grabbing content from metric_fu (coverage,
|
19
|
+
lines of 'code code' vs 'test code' ratio).
|
20
|
+
* QuickMetric class which can be used to link a custom attribute to the
|
21
|
+
current repository commit identifier of a code base to a CodeFumes
|
22
|
+
project. This makes it trivial to send up custom attributes from
|
23
|
+
scripts, rake tasks, or even the console, if desired.
|
24
|
+
|
25
|
+
=== Problems / Things to Note
|
26
|
+
* The only supported SCM right now is git. We know this sucks. Adding
|
27
|
+
in other SCMs shouldn't be hard, and is planned. It's just not done
|
28
|
+
yet.
|
29
|
+
|
30
|
+
== SYNOPSIS:
|
31
|
+
|
32
|
+
=== Associating a repository to a CodeFumes.com project
|
33
|
+
|
34
|
+
# Use +harvest_repo_metrics+ to create a project on CodeFumes.com
|
35
|
+
# In your terminal (not IRB):
|
36
|
+
|
37
|
+
# If you don't care what the public key of your project is:
|
38
|
+
$ harvest_repo_metrics
|
39
|
+
=> results in a project URI similar to: http://codefumes.com/p/uPnf
|
40
|
+
|
41
|
+
# If you want to customize the public_key of your project:
|
42
|
+
$ harvest_repo_metrics -k my_custom_key
|
43
|
+
=> results in a project URI of: http://codefumes.com/p/my_custom_key
|
44
|
+
(assuming it was not already taken)
|
45
|
+
|
46
|
+
# If you are on another machine and want to update that project,
|
47
|
+
# you can pass both the public & private keys of the project for
|
48
|
+
# authentication:
|
49
|
+
$ harvest_repo_metrics -k my_custom_key -a some-generated-private-key
|
50
|
+
|
51
|
+
|
52
|
+
=== Sending up custom metrics using QuickMetric
|
53
|
+
|
54
|
+
# Send up your own data and associate it with the current commit
|
55
|
+
# (HEAD, in git parlance) of your current directory. This makes the
|
56
|
+
# (bold) assumption that the repository is already set up as a CodeFumes
|
57
|
+
# project, and that you want to link the data provided to the current
|
58
|
+
# commit only (think 'rake task' and 'build server').
|
59
|
+
#
|
60
|
+
# An IRB example:
|
61
|
+
> require 'codefumes_harvester'
|
62
|
+
> QuickMetric.save(:name_of_metric => "some value")
|
63
|
+
=> true # returns +false+ if it failed
|
64
|
+
...that's all
|
65
|
+
|
66
|
+
=== Sending up coverage info from metric_fu output
|
67
|
+
# Assuming you have run 'rake metrics:all'
|
68
|
+
$ rake cf:metric_fu:coverage
|
69
|
+
|
70
|
+
=== Sending up lines of code code / lines of test code ratio
|
71
|
+
|
72
|
+
# This is only on Rails projects, I believe
|
73
|
+
$ rake cf:metric_fu:loc_lot_stats
|
74
|
+
|
75
|
+
|
76
|
+
== REQUIREMENTS:
|
77
|
+
|
78
|
+
* +codefumes+ gem
|
79
|
+
|
80
|
+
== INSTALL:
|
81
|
+
|
82
|
+
From RubyForge:
|
83
|
+
|
84
|
+
sudo gem install codefumes-harvester
|
85
|
+
|
86
|
+
Or, from Github:
|
87
|
+
|
88
|
+
sudo gem install cosyn-codefumes-harvester
|
89
|
+
|
90
|
+
|
91
|
+
== LICENSE:
|
92
|
+
|
93
|
+
(The MIT License)
|
94
|
+
|
95
|
+
Copyright (c) 2009 FIXME full name
|
96
|
+
|
97
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
98
|
+
a copy of this software and associated documentation files (the
|
99
|
+
'Software'), to deal in the Software without restriction, including
|
100
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
101
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
102
|
+
permit persons to whom the Software is furnished to do so, subject to
|
103
|
+
the following conditions:
|
104
|
+
|
105
|
+
The above copyright notice and this permission notice shall be
|
106
|
+
included in all copies or substantial portions of the Software.
|
107
|
+
|
108
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
109
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
110
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
111
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
112
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
113
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
114
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils rubigen hoe].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/codefumes_harvester'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "hanna/rdoctask"
|
6
|
+
rescue LoadError
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Load in the harvester ane metric_fu gems if available so we can collect metrics
|
11
|
+
begin
|
12
|
+
require "metric_fu"
|
13
|
+
rescue LoadError
|
14
|
+
end
|
15
|
+
|
16
|
+
$hoe = Hoe.spec('codefumes_harvester') do |p|
|
17
|
+
p.developer('Cosyn Technologies', 'tom.kersten@cosyntech.com')
|
18
|
+
p.rubyforge_name = 'codefumes'
|
19
|
+
p.extra_deps = [
|
20
|
+
['codefumes','>= 0.1.0'],
|
21
|
+
['mojombo-grit','>= 1.1.1']
|
22
|
+
]
|
23
|
+
p.extra_dev_deps = [
|
24
|
+
['mojombo-chronic', ">= 0.3.0"],
|
25
|
+
['jscruggs-metric_fu', ">= 1.1.5"]
|
26
|
+
]
|
27
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
28
|
+
#path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
29
|
+
#p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
30
|
+
p.remote_rdoc_dir = ''
|
31
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
32
|
+
end
|
33
|
+
|
34
|
+
# load /tasks/*.rake, except for the codefumes.rake file, that will be
|
35
|
+
# done in lib/codefumes_harvester.rb (the task_defined? method didn't seem
|
36
|
+
# to catch it)
|
37
|
+
# TODO: Find out what is/was being done wrong that this is necessary
|
38
|
+
Dir['tasks/**/*.rake'].each { |t| load t unless t[/codefumes.rake/]}
|
39
|
+
|
40
|
+
task :default => [:spec]
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created on 2009-9-2.
|
4
|
+
# Copyright (c) 2009. All rights reserved.
|
5
|
+
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/codefumes_harvester")
|
7
|
+
|
8
|
+
require "harvest_repo_metrics/cli"
|
9
|
+
|
10
|
+
HarvestRepoMetric::CLI.execute(STDOUT, ARGV)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'grit'
|
2
|
+
require 'rake'
|
3
|
+
require 'codefumes'
|
4
|
+
require 'codefumes_harvester/harvester'
|
5
|
+
require 'codefumes_harvester/source_control'
|
6
|
+
require 'codefumes_harvester/errors'
|
7
|
+
require 'codefumes_harvester/quick_metric'
|
8
|
+
|
9
|
+
include CodeFumesHarvester
|
10
|
+
|
11
|
+
module CodeFumesHarvester
|
12
|
+
VERSION = '0.1.0'
|
13
|
+
LIB_ROOT = File.dirname(__FILE__)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Shamelessly borrowed from metric_fu setup (http://metric-fu.rubyforge.org/)
|
17
|
+
unless Rake::Task.task_defined? "cf:store_repo_metrics"
|
18
|
+
# Load the rakefile so users of the gem get the default cf tasks
|
19
|
+
# This makes a rash assumption that if you have the 'store_repo_metrics'
|
20
|
+
# task, all others will be defined...and that if you don't, you want them all.
|
21
|
+
load File.join(CodeFumesHarvester::LIB_ROOT, '..', 'tasks', 'codefumes.rake')
|
22
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module CodeFumesHarvester
|
2
|
+
# Simple class responsible for creating a project on the CodeFumes
|
3
|
+
# website, storing project information in the CodeFumes config file,
|
4
|
+
# and synchronizing a repository's commit history with CodeFumes.com.
|
5
|
+
#
|
6
|
+
# NOTE: Technically this can be used by anything (obviously), but it
|
7
|
+
# was written with the intention of being used with the
|
8
|
+
# +harvest_repo_metrics+ script, and is essentially geared for that
|
9
|
+
# scenario.
|
10
|
+
class Harvester
|
11
|
+
attr_reader :path
|
12
|
+
DEFAULT_PATH = './' #:nodoc:
|
13
|
+
|
14
|
+
# Accepts the following options:
|
15
|
+
# * +:path+ - the path of the repository to gather information from
|
16
|
+
# (Defaults to './').
|
17
|
+
# * +:public_key+ - Sets the public key of the project. This
|
18
|
+
# property will be read from the CodeFumes config file if on
|
19
|
+
# exists for the repository supplied at +:path+.
|
20
|
+
# * +:private_key+ - Sets the private key of the project. This
|
21
|
+
# property will be read from the CodeFumes config file if on
|
22
|
+
# exists for the repository supplied at +:path+.
|
23
|
+
# * +:name+ - Sets the name of the project on the CodeFumes site
|
24
|
+
def initialize(passed_in_options = {})
|
25
|
+
options = passed_in_options.dup
|
26
|
+
@path = File.expand_path(options.delete(:path) || DEFAULT_PATH)
|
27
|
+
@repository = initialize_repository
|
28
|
+
options.merge!(:public_key => options[:public_key] || @repository.public_key)
|
29
|
+
options.merge!(:private_key => options[:private_key] || @repository.private_key)
|
30
|
+
@project = initialize_project(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates or updates a project information on the CodeFumes site,
|
34
|
+
# synchronizes the repository's commit history, and prints the
|
35
|
+
# results to STDOUT.
|
36
|
+
#
|
37
|
+
# Returns +true+ if the process succeeded.
|
38
|
+
#
|
39
|
+
# Returns +false+ if the process failed.
|
40
|
+
def publish_data!
|
41
|
+
if @project.save
|
42
|
+
store_public_key_in_repository
|
43
|
+
update_codefumes_config_file
|
44
|
+
payload_results = generate_and_save_payload
|
45
|
+
if payload_results.nil?
|
46
|
+
puts "Local repository is in sync with server. No updates posted."
|
47
|
+
else
|
48
|
+
puts "Successfully saved #{payload_results[:successful_count]} of #{payload_results[:total_count]} payloads."
|
49
|
+
end
|
50
|
+
puts "Exiting."
|
51
|
+
puts
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the CodeFumes public key of the project that is located
|
59
|
+
# at the supplied path.
|
60
|
+
def public_key
|
61
|
+
@project.public_key
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the CodeFumes private key of the project that is located
|
65
|
+
# at the supplied path.
|
66
|
+
def private_key
|
67
|
+
@project.private_key
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the CodeFumes 'short uri' of the project that is located
|
71
|
+
# at the supplied path. The 'short uri' is similar to a Tiny URL for
|
72
|
+
# a project.
|
73
|
+
def short_uri
|
74
|
+
@project.short_uri
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def initialize_repository
|
79
|
+
begin
|
80
|
+
SourceControl.new(@path)
|
81
|
+
rescue Grit::InvalidGitRepositoryError
|
82
|
+
raise Errors::UnsupportedScmToolError
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize_project(options = {})
|
87
|
+
options = CodeFumes::ConfigFile.options_for_project(options[:public_key]).merge(options)
|
88
|
+
CodeFumes::Project.new(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def store_public_key_in_repository
|
92
|
+
@repository.store_public_key(@project.public_key)
|
93
|
+
end
|
94
|
+
|
95
|
+
def update_codefumes_config_file
|
96
|
+
CodeFumes::ConfigFile.save_project(@project)
|
97
|
+
end
|
98
|
+
|
99
|
+
def generate_and_save_payload
|
100
|
+
payload = @repository.payload(CodeFumes::Commit.latest_identifier(public_key), "HEAD")
|
101
|
+
if payload.empty?
|
102
|
+
nil
|
103
|
+
else
|
104
|
+
payloads = CodeFumes::Payload.prepare(:public_key => public_key, :private_key => private_key, :content => payload)
|
105
|
+
successful_requests = payloads.select {|payload| payload.save == true}
|
106
|
+
{:successful_count => successful_requests.size, :total_count => payloads.size}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CodeFumesHarvester
|
2
|
+
class QuickMetric
|
3
|
+
# Associates a Hash of +custom_attributes+ with the current commit
|
4
|
+
# identifier of the repository located at +repository_path+.
|
5
|
+
#
|
6
|
+
# Returns +true+ if the request succeeded.
|
7
|
+
#
|
8
|
+
# Returns +false+ if the request failed.
|
9
|
+
def self.save(custom_attributes, repository_path = './')
|
10
|
+
repo = SourceControl.new(repository_path)
|
11
|
+
commit = {:identifier => repo.local_revision_identifier,
|
12
|
+
:custom_attributes => custom_attributes
|
13
|
+
}
|
14
|
+
content = {:commits => [commit]}
|
15
|
+
payload_set = CodeFumes::Payload.prepare(:public_key => repo.public_key, :private_key => repo.private_key, :content => content)
|
16
|
+
payload_set.reject {|payload| payload.save}.empty?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module CodeFumesHarvester
|
2
|
+
# Defines the contract between CodeFumes and any local source control
|
3
|
+
# management system (SCM).
|
4
|
+
#
|
5
|
+
# *NOTE:* Git is currently the only supported SCM. We look
|
6
|
+
# forward to changing this soon.
|
7
|
+
class SourceControl
|
8
|
+
SUPPORTED_SCMS_AND_DETECTORS = {:git => '.git'} #:nodoc:
|
9
|
+
|
10
|
+
# Sets up a SourceControl object to read content from the repository
|
11
|
+
# located at +path+.
|
12
|
+
def initialize(path)
|
13
|
+
@repository = Grit::Repo.new(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns a serialized Hash containing a single +:commits+ key
|
17
|
+
# associated with an Array of serialized commit information, ready
|
18
|
+
# to be sent to the CodeFumes service.
|
19
|
+
def payload_between(from = initial_commit_identifier, to = "HEAD")
|
20
|
+
start_commit = from || initial_commit_identifier
|
21
|
+
end_commit = to || "HEAD"
|
22
|
+
new_commits = commits_between(start_commit, end_commit)
|
23
|
+
new_commits.empty? ? {} : {:commits => new_commits}
|
24
|
+
end
|
25
|
+
alias :payload :payload_between
|
26
|
+
|
27
|
+
# Returns the first commit identifier of a repository's history.
|
28
|
+
def initial_commit_identifier
|
29
|
+
initial_commit.sha
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns an array of 'symbolized' executable names for all
|
33
|
+
# supported SCMs.
|
34
|
+
#
|
35
|
+
# The names are returned as symbols.
|
36
|
+
def self.supported_systems
|
37
|
+
SUPPORTED_SCMS_AND_DETECTORS.keys
|
38
|
+
end
|
39
|
+
|
40
|
+
# Accepts command-line executable name of SCM and returns whether it
|
41
|
+
# is a supported SCM or not. +tool_cli_name+ should be the
|
42
|
+
# name of the executable, not the 'full name' of the application
|
43
|
+
# (ex: 'svn' not 'subversion').
|
44
|
+
#
|
45
|
+
# Returns +true+ if the SCM is supported
|
46
|
+
#
|
47
|
+
# Returns +false+ if the SCM is not supported.
|
48
|
+
def self.supported_system?(tool_cli_name)
|
49
|
+
SUPPORTED_SCMS_AND_DETECTORS.keys.include?(tool_cli_name.to_sym)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Stores the public_key of the project associated with the
|
53
|
+
# underlying local repository. This will not be necessary
|
54
|
+
# with all SCMs.
|
55
|
+
#
|
56
|
+
# For example, in a git repository, this method will store a
|
57
|
+
# +codefumes+ key in the +.git/config+ file. This value can be used
|
58
|
+
# as a lookup key for other tools to use in conjunction with the
|
59
|
+
# CodeFumes config file (see CodeFumes::Config) to interact with a
|
60
|
+
# CodeFumes project.
|
61
|
+
def store_public_key(public_key)
|
62
|
+
@repository.config["codefumes.public-key"] = public_key
|
63
|
+
end
|
64
|
+
|
65
|
+
# Removes any association to the CodeFumes service which would have
|
66
|
+
# been added using the +store_public_key+ method.
|
67
|
+
#
|
68
|
+
# This method does not remove anything from the user's global
|
69
|
+
# CodeFumes config file.
|
70
|
+
def unlink_from_codefumes!
|
71
|
+
@repository.git.config({}, "--remove-section", "codefumes")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the public key of the project associated with this
|
75
|
+
# repository.
|
76
|
+
def public_key
|
77
|
+
@repository.config["codefumes.public-key"]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the private key of the project assciated with this
|
81
|
+
# repository.
|
82
|
+
def private_key
|
83
|
+
CodeFumes::ConfigFile.options_for_project(public_key)[:private_key]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the current revision identifier of the underlying
|
87
|
+
# repository ('HEAD' of the supplied branch in git parlance).
|
88
|
+
def local_revision_identifier(branch_name = "master")
|
89
|
+
raise ArgumentError, "nil branch name supplied" if branch_name.nil?
|
90
|
+
@repository.get_head(branch_name).commit.sha
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the full path of the underlying repository.
|
94
|
+
def path
|
95
|
+
@repository.path
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def initial_commit
|
100
|
+
@repository.log.last
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns Array of serialized commit data. Each item in the Array
|
104
|
+
# contains attributes of a single commit.
|
105
|
+
def commits_between(from, to, including_from_commit = false)
|
106
|
+
commit_list = @repository.commits_between(from,to)
|
107
|
+
|
108
|
+
if including_from_commit == true || from == initial_commit_identifier
|
109
|
+
commit_list = [initial_commit] + commit_list
|
110
|
+
end
|
111
|
+
|
112
|
+
commit_list.map do |commit|
|
113
|
+
{
|
114
|
+
:identifier => commit.sha,
|
115
|
+
:author_name => commit.author.name,
|
116
|
+
:author_email => commit.author.email,
|
117
|
+
:committer_name => commit.committer.name,
|
118
|
+
:committer_email => commit.committer.email,
|
119
|
+
:authored_at => commit.authored_date,
|
120
|
+
:committed_at => commit.committed_date,
|
121
|
+
:message => commit.message,
|
122
|
+
:short_message => commit.short_message,
|
123
|
+
:parent_identifiers => commit.parents.map(&:sha).join(','),
|
124
|
+
:line_additions => commit.stats.additions,
|
125
|
+
:line_deletions => commit.stats.deletions,
|
126
|
+
:line_total => commit.stats.deletions,
|
127
|
+
:affected_file_count => commit.stats.files.count
|
128
|
+
}
|
129
|
+
end.reverse
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
module HarvestRepoMetric
|
7
|
+
# Class by the +harvest_metric+ script for command line parsing and
|
8
|
+
# delegation to the Harvester class.
|
9
|
+
class CLI
|
10
|
+
include CodeFumesHarvester
|
11
|
+
|
12
|
+
# Parses command-line options passed into +harvest_repo_metrics+
|
13
|
+
# and sends data collected to CodeFumes website.
|
14
|
+
def self.execute(stdout, arguments=[])
|
15
|
+
parse_command_line_options!(stdout, arguments)
|
16
|
+
harvester = Harvester.new(@options)
|
17
|
+
|
18
|
+
if harvester.publish_data!
|
19
|
+
stdout.puts "Project saved to CodeFumes.com. Visit #{harvester.short_uri}"
|
20
|
+
else
|
21
|
+
stdout.puts "Error saving project...please try again or file a bug report."
|
22
|
+
exit(1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def self.parse_command_line_options!(stdout, cl_arguments)
|
28
|
+
@options = {}
|
29
|
+
begin
|
30
|
+
OptionParser.new do |opts|
|
31
|
+
opts.banner = <<-BANNER.gsub(/^ /,'')
|
32
|
+
Tool used to upload code & repository statistics to
|
33
|
+
the CodeFumes website.
|
34
|
+
|
35
|
+
Usage: #{File.basename($0)} [options]
|
36
|
+
|
37
|
+
Options are:
|
38
|
+
BANNER
|
39
|
+
opts.separator ""
|
40
|
+
opts.on("-k", "--project_key [PROJECT_KEY]",
|
41
|
+
"The public CodeFumes API key for this project."
|
42
|
+
) {|public_key| @options[:public_key] = public_key}
|
43
|
+
opts.on("-a", "--access_key [PROJECT_PRIVATE_KEY]",
|
44
|
+
"The private CodeFumes API access key for this project."
|
45
|
+
) {|private_key| @options[:private_key] = private_key}
|
46
|
+
opts.on("-p", "--path [REPOSITORY_PATH]",
|
47
|
+
"The local path to the directory containing this project's code."
|
48
|
+
) {|path| @options[:path] = Dir.new(File.expand_path(path)).path}
|
49
|
+
opts.on("-h", "--help",
|
50
|
+
"Show this help message."
|
51
|
+
) { stdout.puts opts; exit }
|
52
|
+
opts.on("-n", "--name [PROJECT_NAME]",
|
53
|
+
"Name of project."
|
54
|
+
) {|name| @options[:name] = name}
|
55
|
+
opts.parse!(cl_arguments)
|
56
|
+
end
|
57
|
+
rescue Errno::ENOTDIR
|
58
|
+
stdout.puts "Path supplied is not a directory. Please supply a path to a directory containing your project."
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Harvester do
|
4
|
+
before(:each) do
|
5
|
+
this_files_dir = File.dirname(__FILE__)
|
6
|
+
@no_scm_path = "#{this_files_dir}/../fixtures/sample_project_dirs/no_scm"
|
7
|
+
@git_scm_path = "#{this_files_dir}/../fixtures/sample_project_dirs/git_repository"
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "initialization" do
|
11
|
+
context "with a path to a directory that is using the git SCM tool" do
|
12
|
+
before(:each) do
|
13
|
+
@repository = Grit::Repo.new(@git_scm_path)
|
14
|
+
@project = CodeFumes::Project.new
|
15
|
+
Grit::Repo.stub!(:new).with(File.expand_path(@git_scm_path)).and_return(@repository)
|
16
|
+
CodeFumes::Project.stub!(:new).and_return(@project)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize_with_git
|
20
|
+
Harvester.new(:path => @git_scm_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "creates a new Grit::Repo instance" do
|
24
|
+
Grit::Repo.should_receive(:new).with(File.expand_path(@git_scm_path)).and_return(@repository)
|
25
|
+
initialize_with_git
|
26
|
+
end
|
27
|
+
|
28
|
+
it "creates a new CodeFumes::Project instance" do
|
29
|
+
CodeFumes::Project.should_receive(:new).and_return(@project)
|
30
|
+
initialize_with_git
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with a path to a directory that is not using a supported SCM" do
|
35
|
+
it "raises an error" do
|
36
|
+
lambda {Harvester.new(:path => @no_scm_path)}.should raise_error(Errors::UnsupportedScmToolError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "path" do
|
42
|
+
it "returns the full path to a project" do
|
43
|
+
harvester = Harvester.new(:path => @git_scm_path)
|
44
|
+
harvester.path.should == File.expand_path(@git_scm_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "publish_data!" do
|
49
|
+
before(:each) do
|
50
|
+
@harvester = Harvester.new(:path => @git_scm_path)
|
51
|
+
@payload = mock("CodeFumes::Payload instance", :save => true)
|
52
|
+
CodeFumes::Payload.stub!(:new).and_return(@payload)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "initializes a new Payload object" do
|
56
|
+
payload_params = {:public_key => anything(), :private_key => anything(), :content => anything()}
|
57
|
+
CodeFumes::Payload.should_receive(:new).with(payload_params).and_return(@payload)
|
58
|
+
@harvester.publish_data!
|
59
|
+
end
|
60
|
+
|
61
|
+
it "saves the Payload to the website" do
|
62
|
+
@payload.should_receive(:save).and_return(true)
|
63
|
+
@harvester.publish_data!
|
64
|
+
end
|
65
|
+
|
66
|
+
it "updates the config file's project information" do
|
67
|
+
CodeFumes::ConfigFile.should_receive(:save_project)
|
68
|
+
@harvester.publish_data!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
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 CodeFumes::ConfigFile.path == File.expand_path('~/.codefumes_config')
|
7
|
+
File.delete(CodeFumes::ConfigFile.path) if File.exists?(CodeFumes::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(Grit::InvalidGitRepositoryError)
|
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/revision #) 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 = CodeFumes::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_revision_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 revision identifier for the local repository" do
|
185
|
+
@repository.local_revision_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
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'harvest_repo_metrics/cli'
|
3
|
+
|
4
|
+
describe HarvestRepoMetric::CLI, "execute" do
|
5
|
+
before(:each) do
|
6
|
+
@stdout_io = StringIO.new
|
7
|
+
HarvestRepoMetric::CLI.execute(@stdout_io, ["-p", GIT_FIXTURE_REPO_PATH])
|
8
|
+
@stdout_io.rewind
|
9
|
+
@stdout = @stdout_io.read
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:each) do
|
13
|
+
unless CodeFumes::ConfigFile.path == File.expand_path('~/.codefumes_config')
|
14
|
+
File.delete(CodeFumes::ConfigFile.path)
|
15
|
+
SourceControl.new(GIT_FIXTURE_REPO_PATH).unlink_from_codefumes!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "prints out the path of the project" do
|
20
|
+
@stdout.should =~ /Project saved/
|
21
|
+
end
|
22
|
+
|
23
|
+
it "creates a .codefumes_config file" do
|
24
|
+
File.exist?(CodeFumes::ConfigFile.path).should be_true
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'ruby-debug'
|
4
|
+
require 'ruby-debug'
|
5
|
+
require 'chronic'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'spec/autorun'
|
9
|
+
rescue LoadError
|
10
|
+
gem 'rspec'
|
11
|
+
require 'spec/autorun'
|
12
|
+
end
|
13
|
+
|
14
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
15
|
+
require 'codefumes_harvester'
|
16
|
+
|
17
|
+
GIT_FIXTURE_REPO_PATH = File.expand_path(File.dirname(__FILE__) + "/fixtures/sample_project_dirs/git_repository")
|
18
|
+
ENV['CODEFUMES_CONFIG_FILE'] = File.expand_path(File.dirname(__FILE__) + '/sample_codefumes_config.tmp')
|
19
|
+
CodeFumes::API.mode(:test)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
namespace :cf do
|
2
|
+
desc "Send scm metrics to Codefumes.com"
|
3
|
+
task :store_repo_metrics, [:public_key, :private_key] do |tsk, args|
|
4
|
+
harvest_repo_metrics_executable = File.expand_path(File.dirname(__FILE__) + "/../bin/harvest_repo_metrics")
|
5
|
+
options = args.public_key.nil? ? "" : "-k #{args.public_key}"
|
6
|
+
options += args.private_key.nil? ? "" : " -a #{args.private_key}"
|
7
|
+
sh "#{harvest_repo_metrics_executable} #{options}"
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
namespace :metric_fu do
|
12
|
+
desc "Collect commit's overall code coverage statistic from metric_fu output"
|
13
|
+
task :coverage do |tsk, args|
|
14
|
+
err_msg = "No 'rcov' section found in '#{file_path}'"
|
15
|
+
puts rcov_section.nil? ? err_msg : save(:coverage => rcov_section[:global_percent_run])
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Collect current commit's \"lines of code\" & \"lines of test\" metrics from metric_fu output"
|
19
|
+
task :loc_lot_stats do |tsk, args|
|
20
|
+
if stats_section.nil?
|
21
|
+
puts "No 'line stats' section found in '#{file_path}'"
|
22
|
+
else
|
23
|
+
metrics = {:code_lines_of_code => stats_section[:codeLOC],
|
24
|
+
:test_lines_of_code => stats_section[:testLOC],
|
25
|
+
:code_to_test_ratio => "1:#{stats_section[:code_to_test_ratio]}"
|
26
|
+
}
|
27
|
+
puts save(metrics)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def file_path
|
32
|
+
@file_path ||= File.expand_path('tmp/metric_fu/report.yml')
|
33
|
+
end
|
34
|
+
|
35
|
+
def marshalled_mf_report
|
36
|
+
return @marshalled_content unless @marshalled_content.nil?
|
37
|
+
|
38
|
+
unless File.exists?(file_path)
|
39
|
+
raise "No report.yml file. Has 'rake metrics:all' been run yet?"
|
40
|
+
end
|
41
|
+
@marshalled_content = File.open(file_path) {|f| YAML.load(f)}
|
42
|
+
end
|
43
|
+
|
44
|
+
def rcov_section
|
45
|
+
@rcov_section ||= marshalled_mf_report[:rcov]
|
46
|
+
end
|
47
|
+
|
48
|
+
def stats_section
|
49
|
+
@stats_section ||= marshalled_mf_report[:stats]
|
50
|
+
end
|
51
|
+
|
52
|
+
def save(custom_attributes_hash)
|
53
|
+
nil_value_attributes = custom_attributes_hash.select {|name, value| value.nil?}
|
54
|
+
unless nil_value_attributes.empty?
|
55
|
+
return "No value supplied for the attributes: #{nil_value_attributes.keys.join(', ')}"
|
56
|
+
end
|
57
|
+
|
58
|
+
metric_names = custom_attributes_hash.keys.join(', ')
|
59
|
+
if QuickMetric.save(custom_attributes_hash)
|
60
|
+
"Successfully saved metric(s): #{metric_names}"
|
61
|
+
else
|
62
|
+
"Error saving metric(s): #{metric_names}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codefumes_harvester
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cosyn Technologies
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-08 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: codefumes
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.1.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mojombo-grit
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: mojombo-chronic
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.3.0
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: jscruggs-metric_fu
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.1.5
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: hoe
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.3.2
|
64
|
+
version:
|
65
|
+
description: CodeFumesHarvester provides a set of high-level tools for gathering history and common metrics from a local repository and sending them to CodeFumes.com[http://codefumes.com].
|
66
|
+
email:
|
67
|
+
- tom.kersten@cosyntech.com
|
68
|
+
executables:
|
69
|
+
- harvest_repo_metrics
|
70
|
+
extensions: []
|
71
|
+
|
72
|
+
extra_rdoc_files:
|
73
|
+
- History.txt
|
74
|
+
- Manifest.txt
|
75
|
+
- PostInstall.txt
|
76
|
+
- README.txt
|
77
|
+
files:
|
78
|
+
- History.txt
|
79
|
+
- Manifest.txt
|
80
|
+
- PostInstall.txt
|
81
|
+
- README.txt
|
82
|
+
- Rakefile
|
83
|
+
- bin/harvest_repo_metrics
|
84
|
+
- lib/codefumes_harvester.rb
|
85
|
+
- lib/codefumes_harvester/errors.rb
|
86
|
+
- lib/codefumes_harvester/harvester.rb
|
87
|
+
- lib/codefumes_harvester/quick_metric.rb
|
88
|
+
- lib/codefumes_harvester/source_control.rb
|
89
|
+
- lib/harvest_repo_metrics/cli.rb
|
90
|
+
- spec/codometer_harvester/harvester_spec.rb
|
91
|
+
- spec/codometer_harvester/source_control_spec.rb
|
92
|
+
- spec/harvest_repo_metrics_cli_spec.rb
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- tasks/codefumes.rake
|
95
|
+
has_rdoc: true
|
96
|
+
homepage: http://codefumes.com
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options:
|
99
|
+
- --main
|
100
|
+
- README.txt
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: "0"
|
108
|
+
version:
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: "0"
|
114
|
+
version:
|
115
|
+
requirements: []
|
116
|
+
|
117
|
+
rubyforge_project: codefumes
|
118
|
+
rubygems_version: 1.3.1
|
119
|
+
signing_key:
|
120
|
+
specification_version: 2
|
121
|
+
summary: CodeFumesHarvester provides a set of high-level tools for gathering history and common metrics from a local repository and sending them to CodeFumes.com[http://codefumes.com].
|
122
|
+
test_files: []
|
123
|
+
|