github_members 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/README.md +72 -0
- data/exe/github_members +5 -0
- data/lib/github_members/cli.rb +85 -0
- data/lib/github_members/client.rb +31 -0
- data/lib/github_members/defaults.rb +6 -0
- data/lib/github_members/errors.rb +3 -0
- data/lib/github_members/markdown_writer.rb +73 -0
- data/lib/github_members/member.rb +21 -0
- data/lib/github_members/options.rb +79 -0
- data/lib/github_members/version.rb +3 -0
- data/lib/github_members.rb +17 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f27c99dd181831eb919dead2f515dbdfe8677732df33a529681f3218523fd045
|
4
|
+
data.tar.gz: 2a8315bd3492878505168b9d6bb1b77f58e5aa50cd7780eece32d48c8fd0e45c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ad928b3fad1ff61336a0c435e113ab534538465144c38f0bd056898018d2ee29b98d52cd63f75191610af477e548cbb197894d159a2f3f0c422f07086ec4a736
|
7
|
+
data.tar.gz: 90a313c90fa74106671683b6143e84c7189bee49b1262df2ba88062a4b3a7ee6ebb119df07a2255675adb53bdbe2f3e60c15748c7c4a0f0e118ec73d63a195c3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Masafumi Koba
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# GitHub Members
|
2
|
+
|
3
|
+
Manage GitHub members who belong to an organization.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your `Gemfile`:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'github_members', require: false
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Run `bundle exec github_members --help`:
|
16
|
+
|
17
|
+
<!-- HELP:BEGIN -->
|
18
|
+
|
19
|
+
```
|
20
|
+
Usage: github_members [options] <org>
|
21
|
+
-g, --github-token GITHUB_TOKEN A GitHub access token. The `GITHUB_TOKEN` environment variable
|
22
|
+
is also available.
|
23
|
+
-m, --markdown-file [FILE] A Markdown file path. Default: `members.md`
|
24
|
+
-y, --yaml-file [FILE] A YAML file path. Default: `members.yml`
|
25
|
+
-f, --fields [FIELDS] A comma-separated list of a member additional fields
|
26
|
+
-h, --help Show help
|
27
|
+
-v, --version Show version
|
28
|
+
|
29
|
+
Examples:
|
30
|
+
# By default
|
31
|
+
github_members <org>
|
32
|
+
|
33
|
+
# Set a token
|
34
|
+
GITHUB_TOKEN=*** github_members <org>
|
35
|
+
github_members <org> -g ***
|
36
|
+
|
37
|
+
# Specify files
|
38
|
+
github_members <org> -m README.md -y company_members.yml
|
39
|
+
|
40
|
+
# Add fields
|
41
|
+
github_members <org> -f slack,note
|
42
|
+
```
|
43
|
+
|
44
|
+
<!-- HELP:END -->
|
45
|
+
|
46
|
+
### Prepare GitHub access token
|
47
|
+
|
48
|
+
By default, only public members of a specified organization is fetched.
|
49
|
+
Perhaps, you may need to [generate an access token](https://github.com/settings/tokens/new?scopes=read:org&description=github_members) to fetch all members.
|
50
|
+
|
51
|
+
### Generate YAML file including members
|
52
|
+
|
53
|
+
Fetched organization members are saved in a YAML file. You can add any attributes for a member.
|
54
|
+
|
55
|
+
### Insert members table into Markdown file
|
56
|
+
|
57
|
+
Prepare the begin/end tags in your Markdown file like this:
|
58
|
+
|
59
|
+
```markdown
|
60
|
+
<!-- GITHUB_MEMBERS:BEGIN -->
|
61
|
+
<!-- GITHUB_MEMBERS:END -->
|
62
|
+
```
|
63
|
+
|
64
|
+
When running `github_members`, a table of a specified organization members is inserted into the markdown file:
|
65
|
+
|
66
|
+
```markdown
|
67
|
+
<!-- GITHUB_MEMBERS:BEGIN -->
|
68
|
+
|
69
|
+
{inserted_table}
|
70
|
+
|
71
|
+
<!-- GITHUB_MEMBERS:END -->
|
72
|
+
```
|
data/exe/github_members
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module GithubMembers
|
2
|
+
class CLI
|
3
|
+
EXIT_SUCCESS = 0
|
4
|
+
EXIT_FAILURE = 1
|
5
|
+
|
6
|
+
attr_reader :argv
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(argv)
|
10
|
+
@argv = argv
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
begin
|
15
|
+
@options = Options.new(argv)
|
16
|
+
rescue Options::ParseError => e
|
17
|
+
warn e.message
|
18
|
+
return EXIT_FAILURE
|
19
|
+
end
|
20
|
+
|
21
|
+
case
|
22
|
+
when options.help_shown
|
23
|
+
puts options.help
|
24
|
+
return EXIT_SUCCESS
|
25
|
+
when options.version
|
26
|
+
puts options.version
|
27
|
+
return EXIT_SUCCESS
|
28
|
+
when options.github_org.nil?
|
29
|
+
warn "Error: Organization is missing"
|
30
|
+
warn ""
|
31
|
+
warn options.help
|
32
|
+
return EXIT_FAILURE
|
33
|
+
end
|
34
|
+
|
35
|
+
members = read_members
|
36
|
+
|
37
|
+
Client.new(options).fetch_members(options.github_org).each do |new_member|
|
38
|
+
github = new_member.fetch(:github)
|
39
|
+
fullname = new_member.fetch(:fullname)
|
40
|
+
avatar = new_member.fetch(:avatar)
|
41
|
+
|
42
|
+
if members.key?(github)
|
43
|
+
members[github].tap do |m|
|
44
|
+
m.fullname = fullname
|
45
|
+
m.avatar = avatar
|
46
|
+
m.updated = true
|
47
|
+
end
|
48
|
+
else
|
49
|
+
members[github] = member_class.new(
|
50
|
+
github: github,
|
51
|
+
fullname: fullname,
|
52
|
+
avatar: avatar,
|
53
|
+
updated: true
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
members.delete_if { |_, m| !m.updated }
|
59
|
+
|
60
|
+
write_members(members.values)
|
61
|
+
|
62
|
+
MarkdownWriter.new.write(members: members.values, file: options.markdown_file)
|
63
|
+
|
64
|
+
EXIT_SUCCESS
|
65
|
+
end
|
66
|
+
|
67
|
+
private def member_class
|
68
|
+
@member_class ||= Member.new.define_class(*options.fields)
|
69
|
+
end
|
70
|
+
|
71
|
+
private def read_members
|
72
|
+
file = options.yaml_file
|
73
|
+
return {} unless file.exist?
|
74
|
+
|
75
|
+
YAML.safe_load(File.read(file)).each_with_object({}) do |member, hash|
|
76
|
+
m = member_class.new(**member, updated: false)
|
77
|
+
hash[m.github] = m
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private def write_members(members)
|
82
|
+
options.yaml_file.write(members.map(&:to_h).to_yaml)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "octokit"
|
2
|
+
|
3
|
+
module GithubMembers
|
4
|
+
class Client
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
|
10
|
+
Octokit.configure do |c|
|
11
|
+
token = options.github_token
|
12
|
+
c.access_token = token unless token.empty?
|
13
|
+
c.auto_paginate = true
|
14
|
+
c.per_page = 100
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch_members(org)
|
19
|
+
Octokit.organization_members(org).map do |gh_member|
|
20
|
+
gh_user = Octokit.user(gh_member.login)
|
21
|
+
fullname = gh_user.name.then { |name| name.to_s.empty? ? "" : name }
|
22
|
+
|
23
|
+
{
|
24
|
+
github: gh_member.login,
|
25
|
+
fullname: fullname,
|
26
|
+
avatar: gh_member.avatar_url
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "markdown-tables"
|
2
|
+
|
3
|
+
module GithubMembers
|
4
|
+
class MarkdownWriter
|
5
|
+
TAG_BEGIN = "<!-- GITHUB_MEMBERS:BEGIN -->"
|
6
|
+
TAG_END = "<!-- GITHUB_MEMBERS:END -->"
|
7
|
+
|
8
|
+
def write(members:, file:)
|
9
|
+
unless file.exist?
|
10
|
+
file.write(<<~MARKDOWN)
|
11
|
+
# Members
|
12
|
+
|
13
|
+
#{TAG_BEGIN}
|
14
|
+
#{TAG_END}
|
15
|
+
MARKDOWN
|
16
|
+
end
|
17
|
+
|
18
|
+
table = render_table(members)
|
19
|
+
write_file(table, members.size, file)
|
20
|
+
end
|
21
|
+
|
22
|
+
private def render_table(members)
|
23
|
+
return "" if members.empty?
|
24
|
+
|
25
|
+
fields = members.first.additional_fields
|
26
|
+
labels = ["", "GitHub", "Fullname"]
|
27
|
+
align = ["c", "l", "l"]
|
28
|
+
|
29
|
+
fields.each do |field|
|
30
|
+
labels << field.capitalize
|
31
|
+
align << "l"
|
32
|
+
end
|
33
|
+
|
34
|
+
data = members.map do |member|
|
35
|
+
# NOTE: GitHub's default avatar doesn't support `&s=<size>`, so this uses the `<img>` tag.
|
36
|
+
avatar_url = URI(member.avatar).tap { |u| u.query = "s=64" }.to_s
|
37
|
+
avatar = %(<img src="#{avatar_url}" alt="@#{member.github}" width="32" height="32">)
|
38
|
+
github = "[@#{member.github}](#{member.github_url})"
|
39
|
+
fullname = member.fullname.to_s.then { |v| v.empty? ? "-" : v }
|
40
|
+
|
41
|
+
[avatar, github, fullname].tap do |d|
|
42
|
+
fields.each do |field|
|
43
|
+
d << member[field].to_s.then { |v| v.empty? ? "-" : v }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
MarkdownTables.make_table(labels, data, is_rows: true, align: align)
|
49
|
+
end
|
50
|
+
|
51
|
+
private def write_file(table, member_count, file)
|
52
|
+
content = file.read
|
53
|
+
|
54
|
+
unless content.include?(TAG_BEGIN)
|
55
|
+
raise MissingTagInMarkdown, "#{file} must include `#{TAG_BEGIN}`"
|
56
|
+
end
|
57
|
+
|
58
|
+
unless content.include?(TAG_END)
|
59
|
+
raise MissingTagInMarkdown, "#{file} must include `#{TAG_END}`"
|
60
|
+
end
|
61
|
+
|
62
|
+
content.sub!(/#{TAG_BEGIN}.+#{TAG_END}/mo, <<~MARKDOWN.strip)
|
63
|
+
#{TAG_BEGIN}
|
64
|
+
**#{member_count}** member#{member_count == 1 ? "" : "s"}
|
65
|
+
|
66
|
+
#{table}
|
67
|
+
#{TAG_END}
|
68
|
+
MARKDOWN
|
69
|
+
|
70
|
+
file.write(content)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GithubMembers
|
2
|
+
class Member
|
3
|
+
DEFAULT_FIELDS = [:github, :fullname, :avatar, :updated].freeze
|
4
|
+
|
5
|
+
def define_class(*fields)
|
6
|
+
Struct.new(*DEFAULT_FIELDS, *fields, keyword_init: true) do
|
7
|
+
def github_url
|
8
|
+
"https://github.com/#{github}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def additional_fields
|
12
|
+
members - DEFAULT_FIELDS
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_h
|
16
|
+
super.to_h.transform_keys(&:to_s).reject { |k, _| k == "updated" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module GithubMembers
|
4
|
+
class Options
|
5
|
+
class ParseError < Error; end
|
6
|
+
|
7
|
+
attr_accessor :github_org
|
8
|
+
attr_accessor :github_token
|
9
|
+
attr_accessor :markdown_file
|
10
|
+
attr_accessor :yaml_file
|
11
|
+
attr_accessor :fields
|
12
|
+
attr_accessor :help
|
13
|
+
attr_accessor :help_shown
|
14
|
+
attr_accessor :version
|
15
|
+
|
16
|
+
def initialize(argv)
|
17
|
+
self.github_token = ENV["GITHUB_TOKEN"].to_s
|
18
|
+
|
19
|
+
args = parser.parse!(argv)
|
20
|
+
self.help = parser.help + help_examples
|
21
|
+
self.github_org = args.first
|
22
|
+
self.markdown_file = Pathname(markdown_file || Defaults::MARKDOWN_FILE)
|
23
|
+
self.yaml_file = Pathname(yaml_file || Defaults::YAML_FILE)
|
24
|
+
rescue OptionParser::ParseError => e
|
25
|
+
raise ParseError, e.message
|
26
|
+
end
|
27
|
+
|
28
|
+
private def parser
|
29
|
+
@parser ||= OptionParser.new do |opts|
|
30
|
+
opts.banner = "Usage: github_members [options] <org>"
|
31
|
+
|
32
|
+
opts.on("-g", "--github-token GITHUB_TOKEN",
|
33
|
+
"A GitHub access token. The `GITHUB_TOKEN` environment variable",
|
34
|
+
"is also available.") do |v|
|
35
|
+
self.github_token = v
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-m", "--markdown-file [FILE]", "A Markdown file path. Default: `#{Defaults::MARKDOWN_FILE}`") do |v|
|
39
|
+
self.markdown_file = v
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("-y", "--yaml-file [FILE]", "A YAML file path. Default: `#{Defaults::YAML_FILE}`") do |v|
|
43
|
+
self.yaml_file = v
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on("-f", "--fields [FIELDS]", Array, "A comma-separated list of a member additional fields") do |v|
|
47
|
+
self.fields = v
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on_tail("-h", "--help", "Show help") do
|
51
|
+
self.help_shown = true
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on_tail("-v", "--version", "Show version") do
|
55
|
+
self.version = VERSION
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private def help_examples
|
61
|
+
<<~HELP
|
62
|
+
|
63
|
+
Examples:
|
64
|
+
# By default
|
65
|
+
github_members <org>
|
66
|
+
|
67
|
+
# Set a token
|
68
|
+
GITHUB_TOKEN=*** github_members <org>
|
69
|
+
github_members <org> -g ***
|
70
|
+
|
71
|
+
# Specify files
|
72
|
+
github_members <org> -m README.md -y company_members.yml
|
73
|
+
|
74
|
+
# Add fields
|
75
|
+
github_members <org> -f slack,note
|
76
|
+
HELP
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "json"
|
2
|
+
require "pathname"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
require_relative "github_members/errors"
|
6
|
+
|
7
|
+
require_relative "github_members/client"
|
8
|
+
require_relative "github_members/defaults"
|
9
|
+
require_relative "github_members/markdown_writer"
|
10
|
+
require_relative "github_members/member"
|
11
|
+
require_relative "github_members/options"
|
12
|
+
require_relative "github_members/version"
|
13
|
+
|
14
|
+
require_relative "github_members/cli"
|
15
|
+
|
16
|
+
module GithubMembers
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: github_members
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masafumi Koba
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: markdown-tables
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: octokit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.21'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.21'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- ybiquitous@gmail.com
|
44
|
+
executables:
|
45
|
+
- github_members
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- CHANGELOG.md
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
52
|
+
- exe/github_members
|
53
|
+
- lib/github_members.rb
|
54
|
+
- lib/github_members/cli.rb
|
55
|
+
- lib/github_members/client.rb
|
56
|
+
- lib/github_members/defaults.rb
|
57
|
+
- lib/github_members/errors.rb
|
58
|
+
- lib/github_members/markdown_writer.rb
|
59
|
+
- lib/github_members/member.rb
|
60
|
+
- lib/github_members/options.rb
|
61
|
+
- lib/github_members/version.rb
|
62
|
+
homepage: https://github.com/ybiquitous/github_members
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata:
|
66
|
+
homepage_uri: https://github.com/ybiquitous/github_members
|
67
|
+
source_code_uri: https://github.com/ybiquitous/github_members
|
68
|
+
changelog_uri: https://github.com/ybiquitous/github_members/blob/main/CHANGELOG.md
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.7.0
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.2.22
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Manage GitHub members who belong to an organization.
|
88
|
+
test_files: []
|