maintainers 0.1.2 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a242cfe2e2085d761f6fd899e539b342ae8ab65
4
- data.tar.gz: 2b3d13830eecdd5b0e33cb6d2381ed68ba3131f8
3
+ metadata.gz: 637a6814e58b1d5692780afee0338f597a9e334f
4
+ data.tar.gz: 310211fbde7e866fc7ca8e0843705fe1449e083d
5
5
  SHA512:
6
- metadata.gz: 33040a6040571e954c8e0ebc1b724cdf864a32bd5b6796f428fe46e5b7b75f9fa8559448c1881466de3f7148e1a3a0745dde4ac62ffd547dc88e8fa2c06ffe70
7
- data.tar.gz: 1cb1627cd7a50e59c5c2d4d3957a12224c107ae0d5638aa931eebf62fe07d1cae3c47991922c172d0e0d54e093bb4165e171bba438532e43422c38ea43f05f3b
6
+ metadata.gz: 5bff305dcea9ef041947ab5c2fd7de7e2a4082498d258cc9d4ed2e12de22feca265870ad1f142948d77c009abbd628ed056e2af0ffb50de77b0009f43f380ddb
7
+ data.tar.gz: ff073623557c64dd1c115f43e6f25213f3b77798bdefd2726392b56d832a65d136d607bbc99d236dfcd3e9cafcc0fdb7562984c7b43ac47d514aec90ced3cc21
data/CHANGELOG CHANGED
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.0] - 2016-09-05
10
+ ### Added
11
+ - Add a 'validate' subcommand
12
+ - Add a 'list' subcommand
13
+ - Add a 'report' subcommand
14
+ - Add half-decent usage
15
+ - Add an optional 'file_format' field referring back to this repo
16
+
9
17
  ## [0.1.2] - 2016-09-02
10
18
  ### Fixed
11
19
  - Fixed (for realz) schema file reference
@@ -19,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
19
27
  ### Added
20
28
  - Initial support for a json format for MAINTAINERS
21
29
 
22
- [Unreleased]: https://github.com/puppetlabs/maintainers/compare/0.1.2...HEAD
30
+ [Unreleased]: https://github.com/puppetlabs/maintainers/compare/0.2.0...HEAD
31
+ [0.2.0]: https://github.com/puppetlabs/maintainers/compare/0.1.2...0.2.0
23
32
  [0.1.2]: https://github.com/puppetlabs/maintainers/compare/0.1.1...0.1.2
24
33
  [0.1.1]: https://github.com/puppetlabs/maintainers/compare/0.1.0...0.1.1
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
+ "file_format": "This MAINTAINERS file format is described at https://github.com/puppetlabs/maintainers",
3
4
  "issues": "https://github.com/puppetlabs/maintainers/issues",
4
5
  "people": [
5
6
  {
@@ -8,4 +9,4 @@
8
9
  "name": "Kylo Ginsberg"
9
10
  }
10
11
  ]
11
- }
12
+ }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
+ "file_format": "This MAINTAINERS file format is described at https://github.com/puppetlabs/maintainers",
3
4
  "issues": "https://github.com/graceland/issues",
4
5
  "people": [
5
6
  {
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
+ "file_format": "This MAINTAINERS file format is described at https://github.com/puppetlabs/maintainers",
3
4
  "maintained": false,
4
- "issues": "https://github.com/graceland/issues",
5
+ "issues": "This repo is not maintained",
5
6
  "people": []
6
7
  }
@@ -13,20 +13,57 @@ module Maintainers
13
13
  end
14
14
 
15
15
  def usage
16
- puts "Imagine a usage statement here"
16
+ usage = <<USAGE
17
+ usage: maintainers <command> [<args>]
18
+
19
+ See below for subcommands. All operate on a file called 'MAINTAINERS'
20
+ in the current working directory, except for 'report' which looks for
21
+ that file throughout a github organization.
22
+
23
+ create --issues <issues text> [--unmaintained]
24
+
25
+ Creates a MAINTAINERS file scaffold (but with no people yet).
26
+ - An 'issues' string is required - typically this is the
27
+ URL at which to file issues, but knock yourself out.
28
+ - An 'unmaintained' flag is optional - this make explicit
29
+ that a repo is not maintained.
30
+
31
+ add --github <github id> [--email <email address] [--name <street name>]
32
+
33
+ Add a maintainer to the MAINTAINERS file. This can also be used to update
34
+ a maintainer (e.g. adding an 'email' or 'name'): if the specified 'github' id
35
+ is already in the MAINTAINERS file, the new fields will be merged with
36
+ the exiting ones.
37
+ - A 'github' id is required.
38
+ - An 'email' address is optional.
39
+ - A 'name' is optional.
40
+
41
+ remove --github <github id>
42
+
43
+ Remove a maintainer from the MAINTAINERS file.
44
+ - A 'github' id is required.
45
+
46
+ list
47
+
48
+ List the maintainers in a tabular form.
49
+
50
+ validate
51
+
52
+ Validate that the MAINTAINERS file can be read. This can
53
+ be useful if you hand-edit the file but want to double-check
54
+ that it is still machine readable.
55
+
56
+ report
57
+
58
+ Report on maintainers throughout a github organization.
59
+ Note: for the report to include private repos, generate a github
60
+ token with full control of private repositories, and then
61
+ set the environment variable GITHUB_TOKEN to that token.
62
+ USAGE
63
+ puts usage
17
64
  exit 1
18
65
  end
19
66
 
20
- SUBCOMMANDS_WE_LOVE = [
21
- "create",
22
- "add",
23
- "remove",
24
- "list",
25
- "report",
26
- "help",
27
- "--help",
28
- ]
29
-
30
67
  # @return [Hash] Return an options hash
31
68
  def parse(args)
32
69
 
@@ -54,19 +91,18 @@ module Maintainers
54
91
  end,
55
92
  'list' => OptionParser.new do |opts|
56
93
  end,
94
+ 'validate' => OptionParser.new do |opts|
95
+ end,
96
+ 'report' => OptionParser.new do |opts|
97
+ end,
57
98
  }
58
99
 
59
- if args.count == 0
60
- $stderr.puts "Give me some args please"
61
- usage
62
- end
100
+ usage if args.count == 0
63
101
 
64
102
  subcommand = args.shift
65
103
  options[:subcommand] = subcommand
66
104
 
67
- unless subcommands.keys.include? subcommand
68
- usage
69
- end
105
+ usage unless subcommands.keys.include? subcommand
70
106
 
71
107
  subcommands[subcommand].order!
72
108
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'json'
5
5
  require 'json-schema'
6
+ require 'octokit'
6
7
 
7
8
  module Maintainers
8
9
  # Runner entry point
@@ -12,6 +13,9 @@ module Maintainers
12
13
 
13
14
  # for now just assume it's always MAINTAINERS
14
15
  options[:filename] ||= 'MAINTAINERS'
16
+
17
+ # for now just assume it's always puppetlabs
18
+ options[:org] ||= 'puppetlabs'
15
19
  end
16
20
 
17
21
  # Run, Lola, Run
@@ -24,6 +28,12 @@ module Maintainers
24
28
  add(@options)
25
29
  when 'remove'
26
30
  remove(@options)
31
+ when 'list'
32
+ list(@options)
33
+ when 'validate'
34
+ validate(@options)
35
+ when 'report'
36
+ report(@options)
27
37
  end
28
38
  end
29
39
 
@@ -35,14 +45,19 @@ module Maintainers
35
45
  JSON.parse(File.read(schema_path))
36
46
  end
37
47
 
38
- def validate(maintainers)
39
- JSON::Validator.validate(maintainers_schema, maintainers)
48
+ def validate_json(maintainers, quiet = false)
49
+ begin
50
+ JSON::Validator.validate!(maintainers_schema, maintainers)
51
+ rescue JSON::Schema::ValidationError => e
52
+ puts e unless quiet
53
+ false
54
+ end
40
55
  end
41
56
 
42
57
  def write_file(filename, maintainers)
43
58
  maintainers_json = JSON.pretty_generate(maintainers)
44
59
 
45
- if !validate(maintainers_json)
60
+ if !validate_json(maintainers_json)
46
61
  $stderr.puts "Invalid maintainers string!"
47
62
  exit 1
48
63
  end
@@ -60,6 +75,7 @@ module Maintainers
60
75
  # minimum content for a maintainers file
61
76
  maintainers = {}
62
77
  maintainers["version"] = 1
78
+ maintainers["file_format"] = "This MAINTAINERS file format is described at https://github.com/puppetlabs/maintainers"
63
79
  maintainers["issues"] = options[:issues]
64
80
  maintainers["people"] = []
65
81
 
@@ -106,5 +122,96 @@ module Maintainers
106
122
 
107
123
  write_file(filename, maintainers)
108
124
  end
125
+
126
+ def list(options)
127
+ filename = options[:filename]
128
+ if !File.exist?(filename)
129
+ $stderr.puts "No #{filename} file exists yet. You can use the 'create' subcommand to create one."
130
+ exit 1
131
+ end
132
+
133
+ maintainers_json = File.read(filename)
134
+ validate_json(maintainers_json)
135
+ maintainers = JSON.load(maintainers_json)
136
+
137
+ maintainers['people'].each { |p|
138
+ puts "%-16s %-20s %s" % [ p['github'], p['name'], p['email'] ]
139
+ }
140
+ end
141
+
142
+ def validate(options)
143
+ filename = options[:filename]
144
+ if !File.exist?(filename)
145
+ $stderr.puts "No #{filename} file exists yet. You can use the 'create' subcommand to create one."
146
+ exit 1
147
+ end
148
+
149
+ if validate_json(File.read(filename))
150
+ puts "#{filename} looks good"
151
+ else
152
+ puts "There's something wrong with #{filename}"
153
+ exit 1
154
+ end
155
+ end
156
+
157
+ def report(options)
158
+ puts "Ok, hang tight, this may take a while as I query github ..."
159
+ client = Octokit::Client.new(:access_token => ENV['GITHUB_TOKEN'], :auto_paginate => true)
160
+
161
+ repos = client.org_repos(options[:org])
162
+
163
+ puts "Found a total of #{repos.count} #{options[:org]} repos"
164
+
165
+ # For now hardwire some arbitrary filters to help narrow down the
166
+ # number of repos reported on (and thus github API calls):
167
+ # - ignore repos with < 5 forks
168
+ # - ignore repos on a blocklist
169
+ # There are pretty arbitrary lines, so could be parameterized (or dropped).
170
+
171
+ lightly_forked_repos, repos = repos.partition { |repo| repo.forks < 5 }
172
+
173
+ blocklist = [
174
+ 'puppetlabs-modules',
175
+ 'courseware',
176
+ 'courseware-virtual',
177
+ 'showoff',
178
+ 'education-builds',
179
+ 'robby3',
180
+ 'pltraining-classroom',
181
+ 'pltraining-bootstrap',
182
+ 'sfdc_reporting',
183
+ 'tse-control-repo',
184
+ 'puppet-quest-guide',
185
+ 'courseware-lvm',
186
+ ]
187
+
188
+ blocklisted_repos, repos = repos.partition { |repo| blocklist.include? repo.name }
189
+
190
+ no_maintainers_file_repos, repos = repos.partition { |repo|
191
+ begin
192
+ contents = client.contents("#{options[:org]}/#{repo.name}", :path => 'MAINTAINERS')
193
+ rescue Octokit::NotFound
194
+ end
195
+
196
+ contents.nil?
197
+ }
198
+
199
+ unrecognized_maintainers_file_repos, repos = repos.partition { |repo|
200
+ contents = client.contents("#{options[:org]}/#{repo.name}", :path => 'MAINTAINERS')
201
+ # the file content is base64 encoded with some '\n's sprinkled in.
202
+ # the split.join maneuver below strips out those '\n' sprinkles.
203
+ maintainers = Base64.decode64(contents[:content].split.join)
204
+
205
+ !validate_json(maintainers, true)
206
+ }
207
+
208
+ puts "Skipped #{lightly_forked_repos.count} repos with fewer than 5 forks" if lightly_forked_repos
209
+ puts "Skipped #{blocklisted_repos.count} repos on a blocklist" if blocklisted_repos
210
+ puts "Skipped #{no_maintainers_file_repos.count} without a MAINTAINERS file" if no_maintainers_file_repos
211
+ puts "Skipped #{unrecognized_maintainers_file_repos.count} with a MAINTAINERS file in a different format" if unrecognized_maintainers_file_repos
212
+ puts "Found #{repos.count} repos with MAINTAINERS files"
213
+
214
+ end
215
+
109
216
  end
110
217
  end
@@ -1,3 +1,3 @@
1
1
  module Maintainers
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -21,5 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "rspec", "~> 3.0"
22
22
  spec.add_development_dependency "byebug", "~> 9.0"
23
23
 
24
+ spec.add_runtime_dependency 'octokit', '~> 4.3'
24
25
  spec.add_runtime_dependency 'json-schema', '~> 2.6'
25
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maintainers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-02 00:00:00.000000000 Z
11
+ date: 2016-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '9.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: octokit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.3'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: json-schema
71
85
  requirement: !ruby/object:Gem::Requirement