codefumes_harvester 0.1.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/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
|
+
|