Source

bitbucket-backup / lib / bitbucket-backup / repository.rb

Full commit
require "fileutils"
require "cgi"
require "open3"

module Bitbucket
    module Backup
        class Repository
            # @return [Hash]
            #     the hash of repo data from the Bitbucket API.
            #
            attr_reader :repo
            
            # @return [String]
            #     the plain-text password for the repo.
            #
            attr_reader :password
            
            # @return [String]
            #     the absolute path of the directory to backup the repo to.
            #
            attr_reader :backup_root
            
            # Creates a new repo.
            #
            # @param [Hash] repo
            #     the hash of repo data from the Bitbucket API.
            #
            # @param [String] password
            #     the plain-text passsword for the repo.
            #
            # @param [String] backup_root
            #     the absolute path of the directory to backup the repo to.
            #
            def initialize(repo, password, backup_root)
                @repo        = repo
                @password    = password
                @backup_root = backup_root
            end
            
            # Performs a backup of the repository.
            #
            def backup
                puts "Backing up: #{repo["slug"]}."
                
                unless Bitbucket::Backup.have_scm_tool?(repo["scm"])
                    puts "Warning: #{repo["scm"]} not found on PATH. Skipping..."
                    
                    return
                end
                
                backup_src
                
                backup_wiki
            end
            
            private
            def backup_src
                clone_or_update(:type => :src)
            end
            
            def backup_wiki
                clone_or_update(:type => :wiki) if repo["has_wiki"]
            end
            
            def clone_or_update(options)
                path = dir_for_repo(options)
                uri  = uri_for_repo(options)
                
                if File.exist?(path)
                   run_incremental_backup(path, uri)
                else
                   run_full_backup(uri, path)
                end
            end
            
            def is_repo?(path)
                FileUtils.cd(path)
                
                case repo["scm"]
                    when "hg"
                        Open3.popen3("hg status")
                    when "git"
                        Open3.popen3("git status")
                end
                
                return $.
            end
            
            def run_incremental_backup(path, uri)
                unless is_repo?(path)
                    puts "Warning: file already exists but is not a repo!! Skipping..."
                    
                    return false
                end
                
                FileUtils.cd(path)
                
                case repo["scm"]
                    when "hg"
                        system "hg pull -u #{uri}"
                    when "git"
                        system "git pull #{uri}"
                end
            end
            
            def run_full_backup(uri, dest)
                case repo["scm"]
                    when "hg"
                        system "hg clone #{uri} #{dest}"
                    when "git"
                        system "git clone #{uri} #{dest}"
                end
            end
            
            def dir_for_repo(options)
                if options.nil? || options[:type].nil?
                    raise RuntimeError
                end
                
                path = nil
                
                case options[:type]
                    when :src
                        path = File.expand_path("#{backup_root}/#{repo["slug"]}/src")
                    when :wiki
                        path = File.expand_path("#{backup_root}/#{repo["slug"]}/wiki")
                end
                
                return path
            end
            
            def uri_for_repo(options)
                base_uri = nil
                uri      = nil
                ext      = nil
                
                if repo["scm"] == "git"
                    ext = ".git"
                end
                
                if repo["is_private"]
                    base_uri = "https://#{repo["owner"]}:#{CGI.escape(password)}@bitbucket.org/#{repo["owner"]}/#{repo["slug"]}#{ext}"
                else
                    base_uri = "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}#{ext}"
                end
                
                case options[:type]
                    when :src
                        uri = base_uri
                    when :wiki
                        uri = "#{base_uri}/wiki"
                end
                
                return uri
            end
        end
    end
end