Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
tachikoma/lib/tachikoma/application.rb
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
336 lines (303 sloc)
12.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'safe_yaml' | |
require 'uri' | |
require 'tachikoma' | |
require 'octokit' | |
require 'fileutils' | |
require 'restore_bundled_with' | |
module Tachikoma | |
# Main logic of Tachikoma | |
class Application | |
include FileUtils | |
def self.run(strategy) | |
new.run(strategy) | |
end | |
def run(strategy) | |
load | |
fetch | |
send(strategy) if respond_to?(strategy) | |
pull_request | |
end | |
def load | |
@build_for = ENV['BUILD_FOR'] | |
@github_token = ENV[github_token_key(@build_for)] | |
base_config_path = File.join(Tachikoma.original_data_path, 'default.yaml') | |
base_config = YAML.safe_load_file(base_config_path) || {} | |
user_config_path = File.join(Tachikoma.data_path, '__user_config__.yaml') | |
user_config = YAML.safe_load_file(user_config_path) if File.exist?(user_config_path) | |
user_config ||= {} | |
each_config_path = File.join(Tachikoma.data_path, "#{@build_for}.yaml") | |
each_config = YAML.safe_load_file(each_config_path) if File.exist?(each_config_path) | |
unless each_config | |
fail %(Something wrong, BUILD_FOR: #{@build_for}, your config_path: #{each_config_path}) | |
end | |
@configure = base_config.merge(user_config).merge(each_config) | |
@commiter_name = @configure['commiter_name'] | |
@commiter_email = @configure['commiter_email'] | |
@github_account = @configure['github_account'] | |
@url = @configure['url'] | |
@type = @configure['type'] | |
@base_remote_branch = @configure['base_remote_branch'] | |
@authorized_compare_url = authorized_compare_url_with_type(@url, @type, @github_token, @github_account) | |
@authorized_base_url = authorized_base_url_with_type(@url, @type, @github_token, @github_account) | |
@timestamp_format = @configure['timestamp_format'] | |
@readable_time = Time.now.utc.strftime(@timestamp_format) | |
@parallel_option = bundler_parallel_option(Bundler::VERSION, @configure['bundler_parallel_number']) | |
@depth_option = git_clone_depth_option(@configure['git_clone_depth']) | |
@bundler_restore_bundled_with = @configure['bundler_restore_bundled_with'] | |
@target_head = target_repository_user(@type, @url, @github_account) | |
@pull_request_url = repository_identity(@url) | |
@pull_request_body = @configure['pull_request_body'] | |
@pull_request_base = @configure['pull_request_base'] | |
@pull_request_head = "#{@target_head}:tachikoma/update-#{@readable_time}" | |
@pull_request_title = (@configure['pull_request_title'] || 'Exec tachikoma update %s') % @readable_time | |
end | |
def clean | |
mkdir_p(Tachikoma.repos_path) | |
rm_rf(Dir.glob(File.join(Tachikoma.repos_path, '*'))) | |
end | |
def fetch | |
clean | |
sh(*([ | |
'git', 'clone', | |
*@depth_option, | |
@authorized_base_url, | |
"#{Tachikoma.repos_path}/#{@build_for}" | |
].compact)) | |
end | |
def bundler | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
Bundler.with_clean_env do | |
sh(*['ruby', '-i', '-pe', '$_.gsub! /^ruby/, "#ruby"', 'Gemfile']) | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
if File.exist?('Gemfile') | |
@bundler_key_file = 'Gemfile' | |
@bundler_lock_file = 'Gemfile.lock' | |
elsif File.exist?('gems.rb') | |
@bundler_key_file = 'gems.rb' | |
@bundler_lock_file = 'gems.locked' | |
else | |
@bundler_key_file = 'Gemfile' | |
@bundler_lock_file = 'Gemfile.lock' | |
end | |
sh(*([ | |
'bundle', | |
'--gemfile', @bundler_key_file, | |
'--no-deployment', | |
'--without', 'nothing', | |
'--path', 'vendor/bundle', | |
@parallel_option | |
].compact)) | |
sh(*%w(bundle update)) | |
if @bundler_restore_bundled_with | |
# restore_bundled_with | |
lock_file_contents = File.read(@bundler_lock_file) | |
lock_file = RestoreBundledWith::Lock.restore( | |
lock_file_contents, @bundler_lock_file) | |
File.write(@bundler_lock_file, lock_file.body) | |
end | |
sh(*['git', 'add', @bundler_lock_file]) | |
sh(*['git', 'commit', '-m', "Bundle update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
end | |
def bundle | |
warn '[DEPRECATION] `bundle` is deprecated. Please use `bundler` instead.' | |
bundler | |
end | |
def carton | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*%w(carton install)) | |
sh(*%w(carton update)) | |
sh(*['git', 'add', 'carton.lock']) if File.exist?('carton.lock') | |
sh(*['git', 'add', 'cpanfile.snapshot']) if File.exist?('cpanfile.snapshot') | |
sh(*['git', 'commit', '-m', "Carton update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def none | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*['git', 'commit', '--allow-empty', '-m', "None update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def david | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*['david', 'update', '--warn404']) | |
if File.exist?('npm-shrinkwrap.json') | |
sh(*['rm', '-rf', 'node_modules/', 'npm-shrinkwrap.json']) | |
sh(*['npm', 'install']) | |
sh(*['npm', 'shrinkwrap']) | |
sh(*['git', 'add', 'package.json']) | |
sh(*['git', 'add', 'npm-shrinkwrap.json']) | |
else | |
sh(*['git', 'add', 'package.json']) | |
end | |
sh(*['git', 'commit', '-m', "David update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def composer | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
# FIXME: Use Octokit.api_endpoint for GitHub Enterprise | |
sh(*['composer', 'config', 'github-oauth.github.com', @github_token]) | |
sh(*['composer', 'install', '--no-interaction']) | |
sh(*['composer', 'update', '--no-interaction']) | |
sh(*['git', 'add', 'composer.lock']) | |
sh(*['git', 'commit', '-m', "Composer update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def carthage | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*%w(carthage bootstrap)) | |
sh(*%w(carthage update)) | |
sh(*['git', 'add', 'Cartfile.resolved']) | |
sh(*['git', 'commit', '-m', "Carthage update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def cocoapods | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*%w(pod install)) | |
sh(*%w(pod update)) | |
sh(*['git', 'add', 'Podfile.lock']) | |
sh(*['git', 'commit', '-m', "Cocoapods update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def ncu | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*%w(ncu -u)) | |
sh(*['git', 'add', 'package.json']) if File.exist?('package.json') | |
sh(*['git', 'commit', '-m', "Ncu update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def yarn | |
Dir.chdir("#{Tachikoma.repos_path}/#{@build_for}") do | |
sh(*['git', 'config', 'user.name', @commiter_name]) | |
sh(*['git', 'config', 'user.email', @commiter_email]) | |
sh(*['git', 'checkout', '-b', "tachikoma/update-#{@readable_time}", @base_remote_branch]) | |
sh(*%w(yarn install)) | |
sh(*%w(yarn outdated)) | |
sh(*%w(yarn upgrade)) | |
sh(*['git', 'add', 'package.json']) | |
sh(*['git', 'add', 'yarn.lock']) | |
sh(*['git', 'commit', '-m', "Yarn update #{@readable_time}"]) do | |
# ignore exitstatus | |
end | |
sh(*['git', 'push', @authorized_compare_url, "tachikoma/update-#{@readable_time}"]) | |
end | |
end | |
def pull_request | |
@client = Octokit::Client.new access_token: @github_token | |
@client.create_pull_request( | |
@pull_request_url, | |
@pull_request_base, | |
@pull_request_head, | |
@pull_request_title, | |
@pull_request_body | |
) | |
rescue Octokit::UnprocessableEntity | |
end | |
# build_for = fenix-knight, github_token_key = TOKEN_FENIX_KNIGHT | |
def github_token_key(build_for) | |
"TOKEN_#{build_for}".gsub(/-/, '_').upcase | |
end | |
def authorized_compare_url_with_type(fetch_url, type, github_token, github_account) | |
uri = URI.parse(fetch_url) | |
case type | |
when 'fork' | |
%(#{uri.scheme}://#{github_token}:x-oauth-basic@#{uri.host}#{path_for_fork(uri.path, github_account)}) | |
when 'shared' | |
"#{uri.scheme}://#{github_token}:x-oauth-basic@#{uri.host}#{uri.path}" | |
else | |
fail InvalidType, "Invalid type #{type}" | |
end | |
end | |
def authorized_base_url_with_type(fetch_url, type, github_token, _github_account) | |
uri = URI.parse(fetch_url) | |
case type | |
when 'fork', 'shared' | |
"#{uri.scheme}://#{github_token}:x-oauth-basic@#{uri.host}#{uri.path}" | |
else | |
fail InvalidType, "Invalid type #{type}" | |
end | |
end | |
def path_for_fork(path, github_account) | |
path.sub(%r{^/[^/]+}) { '/' + github_account } | |
end | |
def target_repository_user(type, fetch_url, github_account) | |
case type | |
when 'fork' | |
github_account | |
when 'shared' | |
URI.parse(fetch_url).path.split('/', 3)[1] | |
else | |
fail InvalidType, "Invalid type: #{type}" | |
end | |
end | |
def repository_identity(url) | |
project_name, user_name, _ = url.split('/').reverse | |
project_name_identity = strip_extension(project_name) | |
user_name + '/' + project_name_identity | |
end | |
def strip_extension(name) | |
/\A(?<identity>.*?)(?:\.git)?\z/ =~ name | |
identity | |
end | |
# TODO: refactor to returning command args array like git clone depth option | |
def bundler_parallel_option(bundler_version, parallel_number) | |
return if !bundler_parallel_available?(bundler_version) || parallel_number <= 1 | |
"--jobs=#{parallel_number}" | |
end | |
def bundler_parallel_available?(bundler_version) | |
# bundler 1.4.0 gets parallel number option | |
Gem::Version.create(bundler_version) >= Gem::Version.create('1.4.0') | |
end | |
def git_clone_depth_option(depth) | |
return [nil] unless depth | |
['--depth', depth.to_s] | |
end | |
end | |
end |