Skip to content

Instantly share code, notes, and snippets.

@op-ct
Created November 10, 2015 22:17
Show Gist options
  • Save op-ct/8a76e97c70b56f3c1882 to your computer and use it in GitHub Desktop.
Save op-ct/8a76e97c70b56f3c1882 to your computer and use it in GitHub Desktop.
Octokit multi-repo thingamie
source 'https://rubygems.org'
gem 'octokit' # github api
# native ruby git ops
gem 'rugged', git: 'git://github.com/libgit2/rugged.git', submodules: true
gem 'highline' # pretty prings
gem 'ruby-progressbar'
gem 'rake'
gem 'pry' # god-mode
gem 'pry-doc'
gem 'guard' # instant gratification (or disappointment)
gem 'guard-shell'
gem 'guard-rspec'
gem 'guard-rake'
require 'octokit'
require 'highline'
require 'rugged'
require 'pry' # TODO: delete
# TODO: Re-namespace to ~Simp::DevTools::Foss::Sucker? # not nearly as fun
module Simp
module Devtools
module Github
end
end
end
# #TODO: refactor authenticated_github_client into separate class(Singleton?)
module Simp::Devtools::Github
class AuthenticatedClient
def initialize
Octokit.auto_paginate = true
@github_client = nil
end
def client
return @github_client if @github_client
begin
if !ENV.key? 'SIMPSUCKER_GITHUB_TOKEN'
fail 'env var SIMPSUCKER_GITHUB_TOKEN is empty!'
elsif ENV['SIMPSUCKER_GITHUB_TOKEN'].size != 40
fail 'env var SIMPSUCKER_GITHUB_TOKEN does not contain a GitHub API token!'
end
client = Octokit::Client.new( :access_token => ENV['SIMPSUCKER_GITHUB_TOKEN'] )
user = client.user
user.login
rescue Exception => e
line = '-'*71
warnings = [
line,
"An error (#{e.class}) was encountered when authenticating to GitHub.",
'Ensure the env var SIMPSUCKER_GITHUB_TOKEN contains a GitHub API token',
line,
'The original error message was:',
e.message,
line,
].map{ |x| HighLine.color x.gsub( /^.+/, 'WARNING: \0' ), HighLine::RED }
warn warnings
fail 'Could not authenticate with GitHub'
end
@github_client = client
end
end
end
module Simp::Devtools
class Sucker
def initialize( label='simpsucker-vanillas' )
@label = label
@org = 'simp'
@verbose = 1
@github_client = Simp::Devtools::Github::AuthenticatedClient.new.client
@data = OpenStruct.new
initialize_from_github
end
# cache API data we'll need later
def initialize_from_github
### @org_repos = Octokit.repos( "#{@org}/" )
begin
@data.user = @github_client.user
@data.repos = @github_client.repositories
@data.org_repos = @github_client.organization_repositories( @org )
@data.org_repo_names = @data.org_repos.map{ |x| x.full_name }
rescue Exception => e
binding.pry #TODO: better error-checking
end
end
def log_msg log, msg, severity = :info
severities = {:info => HighLine::BLUE,
:warn => HighLine::YELLOW,
:error => HighLine::RED}
msg = HighLine.color msg, severities.fetch( severity, HighLine::UNDERLINE )
warn msg
log << msg
end
def warn msg
Kernel.warn msg if @verbose > 0
end
def info msg
puts ::HighLine.color msg, HighLine::GRAY
end
def hooray msg
puts ::HighLine.color msg, HighLine::GREEN
end
# repo_name = full repo name, inclusing org
def carefully_fork_repo repo, problems
repo_name = repo.full_name
forked_repo = nil
# if we have a matching full name NOTE: can rename repos
our_matching_repos = @data.repos.select{ |r| r.name == repo.name }
if our_matching_repos.size > 0
our_repo_name = our_matching_repos.first.full_name
info " repo exists as: #{our_repo_name}"
# get information on out own
# NOTE: we must query again, because @data.repos doesn't include parents
our_repo = @github_client.repo( our_repo_name )
if !our_repo.fork?
log_msg problems, " repo exists but is not a fork: #{our_repo_name}", :error
elsif our_repo.parent.full_name != repo_name
log_msg problems, " fork exists but from another parent: #{our_repo.parent.full_name}", :error
else
info " repo exists with same parent"
forked_repo = our_repo
end
else
our_repo = @github_client.fork( repo_name ) # TODO: error checking
if our_repo.parent.full_name != repo_name
log_msg problems, " fork exists (as #{our_repo.name}) but from another parent: #{our_repo.parent.full_name}", :error
else
hooray "forked #{ repo_name }"
forked_repo = our_repo
end
end
forked_repo
end
# Ensure that all repos under an organization are forked into your own GitHub account
# repos = Array of Octokit repo objects (in org) to fork
# forks = Array of Octokit repo objects (in our repo) that were forked
def fork_these_repos repos
problems = []
forks = []
repos.each do |org_repo|
forked_repo = carefully_fork_repo( org_repo, problems )
forks << forked_repo if forked_repo
end
if problems.size > 0
warn "\n\n=== problems:"
warn problems
end
forks
end
def clone_these_forked_repos( forked_repos, path )
pwd = Dir.pwd
forked_repos.each do |repo|
url = repo.ssh_url
parent_url = repo.parent.clone_url
begin
require 'ruby-progressbar'
require 'fileutils'
require 'highline/import'
local_repo_path = File.expand_path( repo.name, path )
puts HighLine.color "CLONING #{url}\n to #{local_repo_path}!!!!", HighLine::BLUE
if File.exists? local_repo_path
[ "Directory already exists!",
"(for now, clones need to be fresh each time)!",
" #{local_repo_path}" ].each{ |m| puts HighLine.color m, HighLine::YELLOW }
exit unless HighLine.agree('Blow away the directory? (y/n) (n=exit)')
FileUtils.rm_rf local_repo_path
end
# TODO get total objects
progress_bar = ProgressBar.create(:title => "git (#{repo.name})", total: nil)
# FIXME: better auth scheme, allow straight SSH keys
auth = Rugged::Credentials::SshKeyFromAgent.new( username: 'git' )
Rugged::Repository.clone_at( url, local_repo_path,
credentials: auth,
transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes|
#progress_bar.total = total_objects # redraws every update
progress_bar.progress = received_objects
}
)
puts
rescue Exception => e
puts
puts HighLine.color e.class.to_s, HighLine::RED+HighLine::BOLD
puts HighLine.color e.message, HighLine::RED
puts HighLine.color e.backtrace.join("\n"), HighLine::YELLOW
binding.pry
end
end
Dir.chdir pwd
end
# :everything, :modules, :rubygems
def suck( it, path )
pwd = Dir.pwd
forks = []
things_to_suck = []
if it == :everything
things_to_suck = @data.org_repos
else
# FIXME: this is a silly option
simp_core = @github_client.repo( 'simp/simp-core' )
things_to_suck = [@data.org_repos.first, simp_core]
end
forks = fork_these_repos( things_to_suck )
local_repos = clone_these_forked_repos( forks, path )
# TODO bail if not found
Dir.chdir path
binding.pry # TODO: delete
0
end
end
end
desc 'Ensure gemspec-safe permissions on all files'
task :suck do
sucker = Simp::Devtools::Sucker.new
path = File.join( Dir.pwd, 'land_o_forks' )
FileUtils.mkdir_p path
###sucker.suck :everything, path
sucker.suck :something, path
end
# ------------------------------------------------------------------------------
# packaging tasks
# ------------------------------------------------------------------------------
require 'rake/clean'
require 'find'
@package='simp-sucker'
@rakefile_dir=File.dirname(__FILE__)
CLEAN.include "#{@package}-*.gem"
CLEAN.include 'pkg'
CLEAN.include 'dist'
Find.find( @rakefile_dir ) do |path|
if File.directory? path
CLEAN.include path if File.basename(path) == 'tmp'
else
Find.prune
end
end
desc 'Ensure gemspec-safe permissions on all files'
task :chmod do
gemspec = File.expand_path( "#{@package}.gemspec", @rakefile_dir ).strip
spec = Gem::Specification::load( gemspec )
spec.files.each do |file|
FileUtils.chmod 'go=r', file
end
end
desc 'run all RSpec tests'
task :spec do
Dir.chdir @rakefile_dir
sh 'bundle exec rspec spec'
end
namespace :pkg do
desc "build rubygem package for #{@package}"
task :gem => :chmod do
Dir.chdir @rakefile_dir
Dir['*.gemspec'].each do |spec_file|
cmd = %Q{SIMP_RPM_BUILD=1 bundle exec gem build "#{spec_file}"}
sh cmd
FileUtils.mkdir_p 'dist'
FileUtils.mv Dir.glob("#{@package}*.gem"), 'dist/'
end
end
desc "build and install rubygem package for #{@package}"
task :install_gem => [:clean, :gem] do
Dir.chdir @rakefile_dir
Dir.glob("dist/#{@package}*.gem") do |pkg|
sh %Q{bundle exec gem install #{pkg}}
end
end
end
# vim: syntax=ruby
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment