codeinventory 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d36a885396b768230bc4748b7b18dfcf6da7fb8
4
- data.tar.gz: e178759f92968f5fa78c53fabf508a87debbca35
3
+ metadata.gz: 992cc66fc416b27e661d1aa3519a5bb2530bb06a
4
+ data.tar.gz: 500dbda2306c83785d09b89ce7ad63ef07eba0f1
5
5
  SHA512:
6
- metadata.gz: 66f579ac72173b3d8e2a79ffc664b9b982930a2a6f903d5daf7ed60c0cbd7e2d71d4339c58d257a4861946d8ccc255167d2da6d87f72583a7eb63ded95101d40
7
- data.tar.gz: 7878947ca4b20cc8d99ba5596eedae88b2932688289b4578fa1cdebae15a65b6becae9d369b4656d51d8edf49e43896c8a1ae8ce9d434f11279f1f4aa349e53f
6
+ metadata.gz: a1ee75116c0d295503b0b7db4411b9e3c2e3796ea7d188b19ff5c2f01771898a2989f0c924446f464afe1641aa160523a9b2d75ece4a6e1360bdb09523f7136f
7
+ data.tar.gz: f88d4b3e63b10e470fdef237521fbb186e35768369e10794268de2b514177a638198bde82bf894906913d8621dc90b7fe40285140d5b9794c83b9799a3b43809
data/README.md CHANGED
@@ -6,9 +6,8 @@ The `codeinventory` gem is a tool to harvest project metadata from an agency's r
6
6
 
7
7
  * JSON files
8
8
  * CSV files
9
- * GitHub
10
9
 
11
- More sources can be added.
10
+ More sources can be added via plugins.
12
11
 
13
12
  ## Installation
14
13
 
@@ -28,20 +27,72 @@ Or install it yourself as:
28
27
 
29
28
  ## Usage
30
29
 
30
+ Basically:
31
+
31
32
  ```ruby
32
- json_source = CodeInventory::Source::JSONFile.new(File.new("some_projects.json"))
33
- csv_source = CodeInventory::Source::CSVFile.new(File.new("more_projects.csv"))
34
- github_source = CodeInventory::Source::GitHub.new(access_token: "GITHUB_ACCESS_TOKEN", org: "github_org_name")
33
+ json_source = CodeInventory::JSONFile.new(File.new("some_projects.json"))
34
+ csv_source = CodeInventory::CSVFile.new(File.new("more_projects.csv"))
35
+
36
+ inventory = CodeInventory::Inventory.new(json_source, csv_source)
37
+ inventory.projects # Returns an array of all projects in the JSON and CSV files
38
+ ```
39
+
40
+ ### JSON Source
41
+
42
+ When using `CodeInventory::JSONFile`, the source file is expected to be a JSON file in the following format:
43
+
44
+ ```json
45
+ [
46
+ {
47
+ "name": "Product One",
48
+ "description": "An awesome product.",
49
+ "license": "http://www.usa.gov/publicdomain/label/1.0/",
50
+ "openSourceProject": 1,
51
+ "governmentWideReuseProject": 1,
52
+ "tags": [
53
+ "usa"
54
+ ],
55
+ "contact": {
56
+ "email": "example@example.com"
57
+ }
58
+ },
59
+ {
60
+ "name": "Product Two",
61
+ "description": "Another awesome product.",
62
+ "license": "http://www.usa.gov/publicdomain/label/1.0/",
63
+ "openSourceProject": 0,
64
+ "governmentWideReuseProject": 0,
65
+ "tags": [
66
+ "national-security",
67
+ "top-secret"
68
+ ],
69
+ "contact": {
70
+ "email": "example@example.com"
71
+ }
72
+ }
73
+ ]
74
+
75
+ ```
76
+
77
+ See the [Code.gov documentation](https://code.gov/#/policy-guide/docs/compliance/inventory-code) for specifics on required fields and value types.
78
+
79
+ ### CSV Source
35
80
 
36
- inventory = CodeInventory::Inventory.new(json_source, csv_source, github_source)
37
- inventory.projects # Returns an array of all projects
81
+ When using `CodeInventory::CSVFile`, the source file is expected to be a CSV file in the following format:
82
+
83
+ ```csv
84
+ name,description,license,openSourceProject,governmentWideReuseProject,tags,contact.email
85
+ Product One,An awesome product.,http://www.usa.gov/publicdomain/label/1.0/,1,1,usa,example@example.com
86
+ Product Two,Another awesome product.,http://www.usa.gov/publicdomain/label/1.0/,0,0,"national-security,top-secret",example@example.com
38
87
  ```
39
88
 
89
+ See the [Code.gov documentation](https://code.gov/#/policy-guide/docs/compliance/inventory-code) for specifics on required fields and value types.
90
+
40
91
  ## Development
41
92
 
42
93
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
43
94
 
44
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
95
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in [`version.rb`](/lib/codeinventory/version.rb), and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
45
96
 
46
97
  ## Contributing
47
98
 
@@ -28,4 +28,5 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "pry", "~> 0.10"
29
29
 
30
30
  spec.add_runtime_dependency "octokit", "~> 4.6"
31
+ spec.add_runtime_dependency "thor", "~> 0.19"
31
32
  end
data/exe/codeinv ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "codeinventory/cli"
4
+
5
+ CodeInventory::CLI::App.start(ARGV)
@@ -0,0 +1,35 @@
1
+ require "codeinventory"
2
+ require "thor"
3
+ require "pathname"
4
+
5
+ module CodeInventory
6
+ module CLI
7
+ class App < Thor
8
+ desc "csv FILENAME", "Build an inventory from a CSV file"
9
+ def csv(filename)
10
+ file = Pathname.new(filename)
11
+ unless File.exist? file
12
+ puts "No such file: #{file}"
13
+ exit 1
14
+ end
15
+ source = CodeInventory::CSVFile.new(file)
16
+ inventory = CodeInventory::Inventory.new(source)
17
+ projects = inventory.projects
18
+ puts JSON.pretty_generate(projects)
19
+ end
20
+
21
+ desc "json FILENAME", "Build an inventory from a JSON file"
22
+ def json(filename)
23
+ file = Pathname.new(filename)
24
+ unless File.exist? file
25
+ puts "No such file: #{file}"
26
+ exit 1
27
+ end
28
+ source = CodeInventory::JSONFile.new(file)
29
+ inventory = CodeInventory::Inventory.new(source)
30
+ projects = inventory.projects
31
+ puts JSON.pretty_generate(projects)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ require "codeinventory/cli/app"
@@ -0,0 +1,51 @@
1
+ require "csv"
2
+
3
+ module CodeInventory
4
+ class CSVFile
5
+ attr_reader :csv
6
+
7
+ def initialize(csv_file)
8
+ @csv = CSV.read(csv_file, { headers: true, converters: [ :integer ] })
9
+ validate_headers
10
+ end
11
+
12
+ def projects
13
+ @csv.collect do |row|
14
+ csv_data = row.to_hash
15
+ csv_data.inject({}) do |memo, pair|
16
+ csv_header, csv_value = pair
17
+ case
18
+ when csv_header == "tags"
19
+ new_pair = { "tags" => csv_value.split(",").collect { |tag| tag.strip } }
20
+ when csv_header.include?(".")
21
+ new_pair = dotted_to_nested(csv_header, csv_value)
22
+ else
23
+ new_pair = { csv_header => csv_value }
24
+ end
25
+ memo.merge(new_pair)
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ # Convert a dotted notation header and a value to a nested hash
33
+ # e.g., "contact.email" header with value "me@example.com" becomes
34
+ # { "contact" => { "email" => "me@example.com" } }
35
+ def dotted_to_nested(path, value)
36
+ path.split(".").reverse.inject(value) do |hash, element|
37
+ { element => hash }
38
+ end
39
+ end
40
+
41
+ def validate_headers
42
+ required_headers = [ "name", "description", "license", "openSourceProject", "governmentWideReuseProject", "tags", "contact.email" ]
43
+ required_headers.each do |required|
44
+ raise FileFormatError unless @csv.headers.include? required
45
+ end
46
+ end
47
+ end
48
+
49
+ class FileFormatError < StandardError
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ require "json"
2
+
3
+ module CodeInventory
4
+ class JSONFile
5
+ attr_accessor :projects
6
+
7
+ def initialize(json_file)
8
+ @projects = JSON.load(json_file)
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module CodeInventory
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/codeinventory.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  require "codeinventory/version"
2
2
  require "codeinventory/inventory"
3
- require "codeinventory/source/json_file"
4
- require "codeinventory/source/csv_file"
5
- require "codeinventory/source/github"
6
-
7
- module CodeInventory
8
- end
3
+ require "codeinventory/cli"
4
+ require "codeinventory/json_file"
5
+ require "codeinventory/csv_file"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: codeinventory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Fredrickson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-15 00:00:00.000000000 Z
11
+ date: 2017-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,11 +94,26 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '4.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: thor
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.19'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.19'
97
111
  description: Harvests project metadata from an agency's repositories to build a code
98
112
  inventory. This helps agencies comply with the Federal Source Code Policy.
99
113
  email:
100
114
  - jeffrey.fredrickson@gsa.gov
101
- executables: []
115
+ executables:
116
+ - codeinv
102
117
  extensions: []
103
118
  extra_rdoc_files: []
104
119
  files:
@@ -112,11 +127,13 @@ files:
112
127
  - bin/console
113
128
  - bin/setup
114
129
  - codeinventory.gemspec
130
+ - exe/codeinv
115
131
  - lib/codeinventory.rb
132
+ - lib/codeinventory/cli.rb
133
+ - lib/codeinventory/cli/app.rb
134
+ - lib/codeinventory/csv_file.rb
116
135
  - lib/codeinventory/inventory.rb
117
- - lib/codeinventory/source/csv_file.rb
118
- - lib/codeinventory/source/github.rb
119
- - lib/codeinventory/source/json_file.rb
136
+ - lib/codeinventory/json_file.rb
120
137
  - lib/codeinventory/version.rb
121
138
  homepage: https://github.com/GSA/codeinventory
122
139
  licenses:
@@ -1,50 +0,0 @@
1
- require "csv"
2
-
3
- module CodeInventory
4
- module Source
5
- class CSVFile
6
- attr_reader :csv
7
-
8
- def initialize(csv_file)
9
- @csv = CSV.read(csv_file, { headers: true, converters: [ :integer ] })
10
- validate_headers
11
- end
12
-
13
- def projects
14
- @csv.collect do |row|
15
- csv_data = row.to_hash
16
- csv_data.inject({}) do |memo, pair|
17
- csv_header, csv_value = pair
18
- case
19
- when csv_header == "tags"
20
- new_pair = { "tags" => csv_value.split(",").collect { |tag| tag.strip } }
21
- when csv_header.include?(".")
22
- new_pair = dotted_to_nested(csv_header, csv_value)
23
- else
24
- new_pair = { csv_header => csv_value }
25
- end
26
- memo.merge(new_pair)
27
- end
28
- end
29
- end
30
-
31
- private
32
-
33
- def dotted_to_nested(path, value)
34
- path.split(".").reverse.inject(value) do |hash, element|
35
- { element => hash }
36
- end
37
- end
38
-
39
- def validate_headers
40
- required_headers = [ "name", "description", "license", "openSourceProject", "governmentWideReuseProject", "tags", "contact.email" ]
41
- required_headers.each do |required|
42
- raise FileFormatError unless @csv.headers.include? required
43
- end
44
- end
45
- end
46
-
47
- class FileFormatError < StandardError
48
- end
49
- end
50
- end
@@ -1,47 +0,0 @@
1
- require "octokit"
2
- require "yaml"
3
- require "base64"
4
-
5
- module CodeInventory
6
- module Source
7
- class GitHub
8
- attr_accessor :org
9
-
10
- def initialize(access_token:, org:)
11
- Octokit.auto_paginate = true
12
- @access_token = access_token
13
- @org = org
14
- end
15
-
16
- def projects
17
- repos = client.organization_repositories(@org)
18
- projects = []
19
- repos.each do |repo|
20
- begin
21
- contents_metadata = client.contents(repo[:full_name], path: ".codeinventory.yml")
22
- type = :yaml
23
- raw_content = Base64.decode64(contents_metadata[:content])
24
- rescue Octokit::NotFound
25
- begin
26
- contents_metadata = client.contents(repo[:full_name], path: ".codeinventory.json")
27
- type = :json
28
- raw_content = Base64.decode64(contents_metadata[:content])
29
- rescue Octokit::NotFound
30
- # Ignore repositories that don't have a CodeInventory metadata file
31
- end
32
- end
33
- if type == :yaml
34
- projects << YAML.load(raw_content).to_hash
35
- elsif type == :json
36
- projects << JSON.parse(raw_content)
37
- end
38
- end
39
- projects
40
- end
41
-
42
- def client
43
- @client ||= Octokit::Client.new(access_token: @access_token)
44
- end
45
- end
46
- end
47
- end
@@ -1,13 +0,0 @@
1
- require "json"
2
-
3
- module CodeInventory
4
- module Source
5
- class JSONFile
6
- attr_accessor :projects
7
-
8
- def initialize(json_file)
9
- @projects = JSON.load(json_file)
10
- end
11
- end
12
- end
13
- end