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