#!/usr/bin/env ruby # git update-ghpages user/repo -b gh-pages -p manual/ -i require 'fileutils' require 'tmpdir' module Params def extract(what) i = index(what) and slice!(i, 2)[1] end; def first_is(what) shift if what.include?(self.first); end def self.[](*what) what.extend Params; end def ===(argv) argv.first_is(self); end end # ============================================================================ ARGV.extend Params class CLI # CLI options attr_reader :prefix #=> "doc/" attr_reader :input #=> "/home/me/projects/foo" attr_reader :message #=> "Updated" attr_reader :repo #=> "git@github.com:me/project.git" attr_reader :url #=> "http://me.github.com/project" attr_reader :branch #=> "gh-pages" def verbose?() @verbose; end def force?() @force; end def simulate?() @simulate; end def initialize # Switches @verbose = !! (ARGV.extract('--verbose') || ARGV.delete('-v')) @simulate = !! (ARGV.extract('--simulate') || ARGV.delete('-s')) @force = !! (ARGV.delete('--force') || ARGV.delete('-f')) # Stuff @prefix = ARGV.extract('--prefix') || ARGV.extract('-p') || '' @input = File.expand_path(ARGV.extract('--input') || ARGV.extract('-i') || '.') @message = ARGV.extract('--message') || ARGV.extract('-m') || 'Update' # Github info branch = ARGV.extract('--branch') || ARGV.extract('-b') || nil @repo, @url, @branch = get_github_info(ARGV.shift, branch) end def git_current_branch `git rev-parse --abbrev-ref HEAD`.strip end def git_deploy in_temp_path do |temppath| status "Cloning repository" system! "git clone #{repo} -b #{branch} #{temppath}" if git_current_branch != branch status "Warning: No #{branch} branch found in repo, creating one." return git_deploy_force end copy_files input, File.join(temppath, prefix) status "Committing files" system! "git add .; git add -u; git commit -m #{message.to_s.inspect}" unless simulate? status "Updating repo" system! "git push origin #{branch}" end true end end def git_deploy_force in_temp_path do |temppath| status "Creating new repository" system! "git init ." system! "git checkout -b gh-pages" copy_files input, File.join(temppath, prefix) status "Committing files" system! "git add . && git commit -m #{message.to_s.inspect}" unless simulate? status "Updating repo" system! "git push #{repo} gh-pages:#{branch} --force" end true end end def get_github_info(repo, branch=nil, prefix=nil) if github_format?(repo) user, repo_name = repo.split('/') r = "git@github.com:#{repo}.git" # User page or project page? if repo_name =~ /\.github\.com/ [r, "http://#{repo_name}/#{prefix}", branch || 'master' ] else [r, "http://#{user}.github.com/#{repo_name}/#{prefix}", branch || 'gh-pages' ] end else [repo, nil, branch] end end def run! unless repo print_help exit 128 end status "Deploying to #{repo} (branch #{branch})" msg "NOTE: Running in simulation mode." if simulate? msg "WARNING: If the repository has gh-pages history, it with be overriden." if force? && !simulate? result = force? ? git_deploy_force : git_deploy if result puts "" status "Done." msg "See: #{url}" if url && !simulate? else tip "Failed." exit 1 end end def status(str) puts "#{c('===>',34)} #{c(str, 32)}" end def msg(str) puts " #{c(str, 32)}" end def c(str, color) "\033[#{color}m#{str}\033[0m" end def print_help tip \ %{Usage: git update-ghpages username/repository [options] Flags: -f, --force Force an update (WARNING: kills the history!) -s, --simulate Creates the repository, but doesn't push. -v, --verbose Verbose mode Options: -p PATH, --prefix The prefix -i PATH, --input Input (defaults to current directory) -b BRANCH, --branch The branch to deploy to (defaults to gh-pages) -m MSG, --message Commit message (defaults to 'Update') Examples: Update the repo 'coffee' of github user 'james' with the files from the current directory. The files will be in http://james.github.com/coffee. $ git update-ghpages james/coffee Same as above, but take the files from 'doc/'. $ git update-ghpages james/coffee -i doc Same as the first, but the files will instead be in http://james.github.com/coffee/manual. $ git update-ghpages james/coffee -i doc -p manual }.gsub(/^ {4}/, '') end private # Helpers def tip(msg) $stderr.write "#{msg}\n" end def github_format?(str) str =~ /^([A-Za-z0-9\-_]+)\/([A-Za-z0-9\-_\.]+)$/ end # Performs actions inside a temp path. def in_temp_path(&blk) require 'tmpdir' Dir.mktmpdir do |dir| Dir.chdir(dir) { yield dir } end end def system!(str) puts `#{str} 2>&1`.strip.gsub(/^/, " ") raise "Failed with exit code #{$?.to_i}" unless $?.to_i == 0 end # Returns the current branch name def git_branch `git symbolic-ref HEAD`.strip.split('/').last end # Copy files from source folder to another def copy_files(from, to) status "Copying files #{from} => #{to}..." if verbose? Dir["#{from}/**/*"].each do |f| next unless File.file?(f) target = File.join(to, f.gsub(/^#{Regexp.escape from}/, '')) FileUtils.mkdir_p File.dirname(target) msg "%20s => %-20s" % [f, target] if verbose? FileUtils.cp f, target end end end CLI.new.run!