diff --git a/README.md b/README.md index a363004..c056054 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,19 @@ client.audit_license The above script leverages Github's API to check each repository's license. Log as an error each repository that does not have a license. +```ruby +require 'huborg' +# NOTE: You will need to include GITHUB_ACCESS_TOKEN in your ENV +client = Huborg::Client.new(org_names: ["samvera"]) +client.synchronize_mailmap!(template: '/path/to/my/MAILMAP_TEMPLATE') +``` + +The above will take the given template (which confirms to [Git's .mailmap +file format](https://www.git-scm.com/docs/git-check-mailmap), then +iterates on all of the repositories, adding any non-duplicates, then +writing back to the template before creating pull requests against +each of the organization's non-archived repositories. + **All of the commands have several parameters, many set to default values.** ## Prerequisites diff --git a/Rakefile b/Rakefile index b19e170..8576822 100644 --- a/Rakefile +++ b/Rakefile @@ -32,4 +32,13 @@ namespace :test do ) client.audit_license end + + task :mailmap do + require 'huborg' + client = Huborg::Client.new( + github_access_token: ENV.fetch("GITHUB_ACCESS_TOKEN"), + org_names: ENV.fetch("GITHUB_ORG_NAME") + ) + client.synchronize_mailmap!(template: ENV.fetch("MAILMAP_TEMPLATE_FILENAME")) + end end diff --git a/lib/huborg.rb b/lib/huborg.rb index 239de58..877d387 100644 --- a/lib/huborg.rb +++ b/lib/huborg.rb @@ -2,6 +2,7 @@ require 'octokit' require 'git' require 'fileutils' +require 'set' module Huborg class Error < RuntimeError; end @@ -11,6 +12,8 @@ class Error < RuntimeError; end # * {#push_template!} - push a file to all repositories # * {#clone_and_rebase!} - download all repositories for the org # * {#audit_license} - tooling to check the licenses of your org + # * {#synchronize_mailmap!} - ensure all git .mailmap files are + # synchronized class Client # Match all repositories DEFAULT_REPOSITORY_PATTERN = %r{\A.*\Z} @@ -121,6 +124,58 @@ def audit_license(skip_private: true, skip_archived: true, allowed_licenses: :al true end + # @api public + # + # Responsible for taking a template that confirms to Git's .mailmap + # file format (e.g. https://github.com/samvera/maintenance/blob/master/templates/MAILMAP) + # and adding in all .mailmap entries that exist within the + # organizations repositories. This merged .mailmap is then pushed + # out, via a pull request, to all of the non-archived repositories. + # + # @param template [String] path to the source template for .mailmap + # This does assume that there were prior efforts at + # consolidating a .mailmap file. If you don't have this, + # pass an empty file. + # @param consolidated_template [String] path that we will write our + # changes, this file will be pushed to all non-archived + # repositories + # @return [True] if successfully completed + # @see https://www.git-scm.com/docs/git-check-mailmap for more on + # git's .mailmap file + # @todo Ensure that this doesn't create a pull request if nothing + # has changed. + def synchronize_mailmap!(template:, consolidated_template: template) + mailmap_lines = Set.new + File.read(template).split("\n").each do |line| + mailmap_lines << line unless line.empty? + end + + each_github_repository do |repo| + begin + mailmap = client.contents(repo.full_name, path: '.mailmap') + lines = mailmap.rels[:download].get.data + lines.split("\n").each do |line| + mailmap_lines << line + end + rescue Octokit::NotFound + next + end + end + + # Write the contents to a file + File.open(consolidated_template, "w+") do |file| + mailmap_lines.to_a.sort.each do |line| + file.puts line + end + end + + each_github_repository do |repo| + next if repo.archived? + push_template_to!(filename: ".mailmap", template: consolidated_template, repo: repo, overwrite: true) + end + return true + end + # @api public # # Clone all repositories (that match the {#repository_pattern} for