| #!/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! |