2015-10-30 16:03:22 +00:00
|
|
|
# encoding: utf-8
|
2015-10-30 13:38:21 +00:00
|
|
|
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
|
|
|
# License:: Apache License, Version 2.0
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
#
|
|
|
|
|
|
|
|
require 'rake'
|
|
|
|
|
2015-10-30 15:17:17 +00:00
|
|
|
SOURCE = File.join(File.dirname(__FILE__), '..', 'MAINTAINERS.toml')
|
|
|
|
TARGET = File.join(File.dirname(__FILE__), '..', 'MAINTAINERS.md')
|
2015-10-30 13:38:21 +00:00
|
|
|
|
|
|
|
# The list of repositories that teams should own
|
2015-10-30 15:17:17 +00:00
|
|
|
REPOSITORIES = ['chef/inspec']
|
2015-10-30 13:38:21 +00:00
|
|
|
|
|
|
|
begin
|
|
|
|
require 'tomlrb'
|
|
|
|
require 'octokit'
|
|
|
|
require 'pp'
|
2015-10-30 16:03:22 +00:00
|
|
|
task default: :generate
|
2015-10-30 13:38:21 +00:00
|
|
|
|
|
|
|
namespace :maintainers do
|
2015-10-30 15:17:17 +00:00
|
|
|
desc 'Generate MarkDown version of MAINTAINERS file'
|
2015-10-30 13:38:21 +00:00
|
|
|
task :generate do
|
|
|
|
maintainers = Tomlrb.load_file SOURCE
|
|
|
|
out = "<!-- This is a generated file. Please do not edit directly -->\n\n"
|
|
|
|
out << "<!-- Modify MAINTAINERS.toml and run `rake maintainers:generate` to regenerate. -->\n\n"
|
2015-10-30 15:17:17 +00:00
|
|
|
out << '# ' + maintainers['Preamble']['title'] + "\n\n"
|
2015-10-30 16:18:50 +00:00
|
|
|
out << maintainers['Preamble']['text'] + "\n"
|
2015-10-30 15:17:17 +00:00
|
|
|
out << components(maintainers['people'], maintainers['Org']['Components'])
|
|
|
|
File.open(TARGET, 'w') { |fn|
|
2015-10-30 13:38:21 +00:00
|
|
|
fn.write out
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2015-10-30 15:17:17 +00:00
|
|
|
desc 'Synchronize GitHub teams'
|
2015-10-30 13:38:21 +00:00
|
|
|
task :synchronize do
|
|
|
|
Octokit.auto_paginate = true
|
2015-10-30 16:03:22 +00:00
|
|
|
github_teams
|
2015-10-30 15:17:17 +00:00
|
|
|
prepare_teams(source['Org']['Components'].dup)
|
2015-10-30 13:38:21 +00:00
|
|
|
sync_teams!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def github
|
2015-10-30 16:03:22 +00:00
|
|
|
@github ||= Octokit::Client.new(netrc: true)
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def source
|
|
|
|
@source ||= Tomlrb.load_file SOURCE
|
|
|
|
end
|
|
|
|
|
|
|
|
def teams
|
2015-10-30 16:03:22 +00:00
|
|
|
@teams ||= { 'inspec-maintainers' => { 'title' => 'Maintainers of the InSpec toolset' } }
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def add_members(team, name)
|
2015-10-30 15:17:17 +00:00
|
|
|
teams['inspec-maintainers']['members'] ||= []
|
|
|
|
teams['inspec-maintainers']['members'] << name
|
2015-10-30 13:38:21 +00:00
|
|
|
teams[team] ||= {}
|
2015-10-30 15:17:17 +00:00
|
|
|
teams[team]['members'] ||= []
|
|
|
|
teams[team]['members'] << name
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def set_team_title(team, title)
|
|
|
|
teams[team] ||= {}
|
2015-10-30 15:17:17 +00:00
|
|
|
teams[team]['title'] = title
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def gh_teams
|
|
|
|
@gh_teams ||= {}
|
|
|
|
end
|
|
|
|
|
|
|
|
# we have to resolve team names to ids. While we're at it, we can get the privacy
|
|
|
|
# setting, so we know whether we need to update it
|
2015-10-30 16:03:22 +00:00
|
|
|
def github_teams
|
2015-10-30 15:17:17 +00:00
|
|
|
github.org_teams('chef').each do |team|
|
2015-10-30 16:03:22 +00:00
|
|
|
gh_teams[team[:slug]] = { 'id' => team[:id], 'privacy' => team[:privacy] }
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_github_team(team)
|
2015-10-30 15:17:17 +00:00
|
|
|
github.team_members(gh_teams[team]['id']).map do |member|
|
2015-10-30 13:38:21 +00:00
|
|
|
member[:login]
|
|
|
|
end.sort.uniq.map(&:downcase)
|
|
|
|
rescue
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_team(team)
|
2015-10-30 15:17:17 +00:00
|
|
|
puts "creating new github team: #{team} with title: #{teams[team]['title']} "
|
|
|
|
t = github.create_team('chef', name: team, description: teams[team]['title'],
|
|
|
|
privacy: 'closed', repo_names: REPOSITORIES,
|
|
|
|
accept: 'application/vnd.github.ironman-preview+json')
|
|
|
|
gh_teams[team] = { 'id' => t[:id], 'privacy' => t[:privacy] }
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def compare_teams(current, desired)
|
|
|
|
# additions are the subtraction of the current state from the desired state
|
|
|
|
# deletions are the subtraction of the desired state from the current state
|
|
|
|
[desired - current, current - desired]
|
|
|
|
end
|
|
|
|
|
|
|
|
def prepare_teams(cmp)
|
2015-10-30 16:03:22 +00:00
|
|
|
%w{text paths}.each { |k| cmp.delete(k) }
|
2015-10-30 15:17:17 +00:00
|
|
|
if cmp.key?('team')
|
|
|
|
team = cmp.delete('team')
|
|
|
|
add_members(team, cmp.delete('lieutenant')) if cmp.key?('lieutenant')
|
|
|
|
add_members(team, cmp.delete('maintainers')) if cmp.key?('maintainers')
|
|
|
|
set_team_title(team, cmp.delete('title'))
|
2015-10-30 13:38:21 +00:00
|
|
|
else
|
2015-10-30 16:03:22 +00:00
|
|
|
%w{maintainers lieutenant title}.each { |k| cmp.delete(k) }
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
cmp.each { |_k, v| prepare_teams(v) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_team(team, additions, deletions)
|
|
|
|
create_team(team) unless gh_teams.key?(team)
|
|
|
|
update_team_privacy(team)
|
|
|
|
add_team_members(team, additions)
|
|
|
|
remove_team_members(team, deletions)
|
|
|
|
rescue
|
|
|
|
puts "failed for #{team}"
|
|
|
|
end
|
|
|
|
|
2015-10-30 16:18:50 +00:00
|
|
|
def update_team_privacy(_team)
|
|
|
|
# return
|
|
|
|
# return if gh_teams[team]['privacy'] == 'closed'
|
|
|
|
# puts "Setting #{team} privacy to closed from #{gh_teams[team]['privacy']}"
|
|
|
|
# github.update_team(gh_teams[team]['id'], privacy: 'closed',
|
|
|
|
# accept: 'application/vnd.github.ironman-preview+json')
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def add_team_members(team, additions)
|
|
|
|
additions.each do |member|
|
|
|
|
puts "Adding #{member} to #{team}"
|
2015-10-30 15:17:17 +00:00
|
|
|
github.add_team_membership(gh_teams[team]['id'], member, role: 'member',
|
|
|
|
accept: 'application/vnd.github.ironman-preview+json')
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def remove_team_members(team, deletions)
|
|
|
|
deletions.each do |member|
|
|
|
|
puts "Removing #{member} from #{team}"
|
2015-10-30 15:17:17 +00:00
|
|
|
github.remove_team_membership(gh_teams[team]['id'], member,
|
|
|
|
accept: 'application/vnd.github.ironman-preview+json')
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def sync_teams!
|
|
|
|
teams.each do |name, details|
|
|
|
|
current = get_github_team(name)
|
2015-10-30 15:17:17 +00:00
|
|
|
desired = details['members'].flatten.sort.uniq.map(&:downcase)
|
2015-10-30 13:38:21 +00:00
|
|
|
additions, deletions = compare_teams(current, desired)
|
|
|
|
update_team(name, additions, deletions)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_person(person)
|
2015-10-30 15:17:17 +00:00
|
|
|
source['people'][person]
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def components(list, cmp)
|
2015-10-30 15:17:17 +00:00
|
|
|
out = '## ' + cmp.delete('title') + "\n\n"
|
2015-10-30 16:03:22 +00:00
|
|
|
out << cmp.delete('text') + "\n" if cmp.key?('text')
|
|
|
|
out << "To mention the team, use @chef/#{cmp.delete('team')}\n\n" if cmp.key?('team')
|
|
|
|
if cmp.key?('lieutenant')
|
|
|
|
out << "### Lieutenant\n\n"
|
|
|
|
out << person(list, cmp.delete('lieutenant')) + "\n\n"
|
|
|
|
end
|
|
|
|
out << maintainers(list, cmp.delete('maintainers')) + "\n" if cmp.key?('maintainers')
|
2015-10-30 15:17:17 +00:00
|
|
|
cmp.delete('paths')
|
2015-10-30 16:03:22 +00:00
|
|
|
cmp.each { |_k, v| out << components(list, v) }
|
2015-10-30 13:38:21 +00:00
|
|
|
out
|
|
|
|
end
|
|
|
|
|
|
|
|
def maintainers(list, people)
|
|
|
|
o = "### Maintainers\n\n"
|
|
|
|
people.each do |p|
|
|
|
|
o << person(list, p) + "\n"
|
|
|
|
end
|
|
|
|
o
|
|
|
|
end
|
|
|
|
|
2015-10-30 16:03:22 +00:00
|
|
|
# rubocop:disable Metrics/AbcSize
|
2015-10-30 13:38:21 +00:00
|
|
|
def person(list, person)
|
2015-10-30 16:03:22 +00:00
|
|
|
if list[person].key?('GitHub')
|
2015-10-30 15:17:17 +00:00
|
|
|
out = "* [#{list[person]['Name']}](https://github.com/#{list[person]['GitHub']})"
|
2015-10-30 13:38:21 +00:00
|
|
|
else
|
2015-10-30 16:18:50 +00:00
|
|
|
out = "* #{list[person]['Name']}"
|
2015-10-30 13:38:21 +00:00
|
|
|
end
|
2015-10-30 16:03:22 +00:00
|
|
|
out << "\n * IRC - #{list[person]['IRC']}" if list[person].key?('IRC')
|
|
|
|
out << "\n * [@#{list[person]['Twitter']}](https://twitter.com/#{list[person]['Twitter']})" if list[person].key?('Twitter')
|
|
|
|
out << "\n * [#{list[person]['email']}](mailto:#{list[person]['email']})" if list[person].key?('email')
|
|
|
|
out << "\n * #{list[person]['phone']}" if list[person].key?('phone')
|
|
|
|
out << "\n * [ServerFault](#{list[person]['ServerFault']})" if list[person].key?('ServerFault')
|
2015-10-30 13:38:21 +00:00
|
|
|
out
|
|
|
|
end
|
2015-10-30 16:03:22 +00:00
|
|
|
# rubocop:enable all
|
2015-10-30 13:38:21 +00:00
|
|
|
|
|
|
|
rescue LoadError
|
|
|
|
STDERR.puts "\n*** TomlRb not available.\n\n"
|
|
|
|
end
|