Permalink
Browse files

first version of backup-github command

  • Loading branch information...
mcortesi committed Jan 31, 2012
1 parent ab35e9e commit e3155d67bf88e4c8b847af7c18e574999f2be713
Showing with 385 additions and 22 deletions.
  1. +1 −1 .gitignore
  2. +16 −7 Gemfile
  3. +71 −0 Gemfile.lock
  4. +1 −1 LICENSE.txt
  5. +1 −1 README.rdoc
  6. +3 −8 Rakefile
  7. +73 −0 backup-github.gemspec
  8. +9 −0 bin/gh-backup-issues
  9. +92 −0 lib/backup-github.rb
  10. +42 −0 lib/backup-github/cli.rb
  11. +76 −4 spec/backup-github_spec.rb
View
@@ -28,7 +28,7 @@ pkg
#
# For MacOS:
#
-#.DS_Store
+.DS_Store
# For TextMate
#*.tmproj
View
23 Gemfile
@@ -1,14 +1,23 @@
-source "http://rubygems.org"
-# Add dependencies required to use your gem here.
-# Example:
-# gem "activesupport", ">= 2.3.5"
+source :rubygems
-# Add dependencies to develop your gem here.
-# Include everything needed to run rake, tests, features, etc.
group :development do
gem "rspec", "~> 2.8.0"
gem "rdoc", "~> 3.12"
gem "bundler", "~> 1.0.0"
gem "jeweler", "~> 1.8.3"
- gem "rcov", ">= 0"
+ gem "mocha", "~> 0.10.3"
+end
+
+group :default do
+ gem "octokit", "~> 0.6.5"
+ gem "json", "~> 1.6.4"
+ gem "grit", "~> 2.4.1"
+ gem "trollop", "~> 1.16.2"
+end
+
+# yo dawg, i herd u lieked jeweler
+group :xzibit do
+ # steal a page from bundler's gemspec:
+ # add this directory as jeweler, in order to bundle exec jeweler and use the current working directory
+ gem 'backup-github', :path => '.'
end
View
@@ -0,0 +1,71 @@
+PATH
+ remote: .
+ specs:
+ backup-github (0.0.0)
+ grit (~> 2.4.1)
+ json (~> 1.6.4)
+ octokit (~> 0.6.5)
+ trollop (~> 1.16.2)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ addressable (2.2.6)
+ diff-lcs (1.1.3)
+ faraday (0.7.6)
+ addressable (~> 2.2)
+ multipart-post (~> 1.1)
+ rack (~> 1.1)
+ faraday_middleware (0.7.0)
+ faraday (~> 0.7.3)
+ git (1.2.5)
+ grit (2.4.1)
+ diff-lcs (~> 1.1)
+ mime-types (~> 1.15)
+ hashie (1.2.0)
+ jeweler (1.8.3)
+ bundler (~> 1.0)
+ git (>= 1.2.5)
+ rake
+ rdoc
+ json (1.6.5)
+ metaclass (0.0.1)
+ mime-types (1.17.2)
+ mocha (0.10.3)
+ metaclass (~> 0.0.1)
+ multi_json (1.0.4)
+ multipart-post (1.1.4)
+ octokit (0.6.5)
+ addressable (~> 2.2.6)
+ faraday (~> 0.7.4)
+ faraday_middleware (~> 0.7.0)
+ hashie (~> 1.2.0)
+ multi_json (~> 1.0.2)
+ rack (1.4.1)
+ rake (0.9.2.2)
+ rdoc (3.12)
+ json (~> 1.4)
+ rspec (2.8.0)
+ rspec-core (~> 2.8.0)
+ rspec-expectations (~> 2.8.0)
+ rspec-mocks (~> 2.8.0)
+ rspec-core (2.8.0)
+ rspec-expectations (2.8.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.8.0)
+ trollop (1.16.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ backup-github!
+ bundler (~> 1.0.0)
+ grit (~> 2.4.1)
+ jeweler (~> 1.8.3)
+ json (~> 1.6.4)
+ mocha (~> 0.10.3)
+ octokit (~> 0.6.5)
+ rdoc (~> 3.12)
+ rspec (~> 2.8.0)
+ trollop (~> 1.16.2)
View
@@ -1,4 +1,4 @@
-Copyright (c) 2012 Mariano A. Cortesi
+Copyright (c) 2012 Zauber (http://www.zaubersoftware.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
View
@@ -14,6 +14,6 @@ Description goes here.
== Copyright
-Copyright (c) 2012 Mariano A. Cortesi. See LICENSE.txt for
+Copyright (c) 2012 Zauber. See LICENSE.txt for
further details.
View
@@ -15,10 +15,10 @@ require 'jeweler'
Jeweler::Tasks.new do |gem|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
gem.name = "backup-github"
- gem.homepage = "http://github.com/mcortesi/backup-github"
+ gem.homepage = "http://github.com/zauberlabs/backup-github"
gem.license = "MIT"
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.summary = %Q{Backups Github Issues}
+ gem.description = %Q{Command to backup Github Issues as JSON files in a Git Repository}
gem.email = "mariano@zauberlabs.com"
gem.authors = ["Mariano A. Cortesi"]
# dependencies defined in Gemfile
@@ -31,11 +31,6 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = FileList['spec/**/*_spec.rb']
end
-RSpec::Core::RakeTask.new(:rcov) do |spec|
- spec.pattern = 'spec/**/*_spec.rb'
- spec.rcov = true
-end
-
task :default => :spec
require 'rdoc/task'
View
@@ -0,0 +1,73 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = "backup-github"
+ s.version = "0.0.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Mariano A. Cortesi"]
+ s.date = "2012-01-30"
+ s.description = "Command to backup Github Issues as JSON files in a Git Repository"
+ s.email = "mariano@zauberlabs.com"
+ s.extra_rdoc_files = [
+ "LICENSE.txt",
+ "README.rdoc"
+ ]
+ s.files = [
+ ".document",
+ ".rspec",
+ "Gemfile",
+ "LICENSE.txt",
+ "README.rdoc",
+ "Rakefile",
+ "VERSION",
+ "lib/backup-github.rb",
+ "spec/backup-github_spec.rb",
+ "spec/spec_helper.rb"
+ ]
+ s.homepage = "http://github.com/zauberlabs/backup-github"
+ s.licenses = ["MIT"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = "1.8.15"
+ s.summary = "Backups Github Issues"
+
+ if s.respond_to? :specification_version then
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<octokit>, ["~> 0.6.5"])
+ s.add_runtime_dependency(%q<json>, ["~> 1.6.4"])
+ s.add_runtime_dependency(%q<grit>, ["~> 2.4.1"])
+ s.add_runtime_dependency(%q<trollop>, ["~> 1.16.2"])
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
+ s.add_development_dependency(%q<mocha>, ["~> 0.10.3"])
+ else
+ s.add_dependency(%q<octokit>, ["~> 0.6.5"])
+ s.add_dependency(%q<json>, ["~> 1.6.4"])
+ s.add_dependency(%q<grit>, ["~> 2.4.1"])
+ s.add_dependency(%q<trollop>, ["~> 1.16.2"])
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
+ s.add_dependency(%q<mocha>, ["~> 0.10.3"])
+ end
+ else
+ s.add_dependency(%q<octokit>, ["~> 0.6.5"])
+ s.add_dependency(%q<json>, ["~> 1.6.4"])
+ s.add_dependency(%q<grit>, ["~> 2.4.1"])
+ s.add_dependency(%q<trollop>, ["~> 1.16.2"])
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
+ s.add_dependency(%q<mocha>, ["~> 0.10.3"])
+ end
+end
+
View
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+
+# Don't try to run this binary directory. Instead:
+# * `bundle exec backup-github` if doing backup-github development
+# * `backup-github` if installed through rubygems, relying on the rubygem wrapper
+
+require "backup-github/cli"
+
+BackupGithub::Cli::run()
View
@@ -0,0 +1,92 @@
+#!/usr/bin/env ruby
+require 'rubygems'
+require 'octokit'
+require 'json'
+require 'grit'
+require 'ostruct'
+require 'pathname'
+require 'logger'
+
+$logger = Logger.new($stdout)
+
+class GithubBackup
+
+ def initialize(github)
+ @github = github
+ end
+
+ def serialize_issue(issue)
+ JSON.pretty_generate( issue.content )
+ end
+
+ def save_issue(dir, issue)
+ file = dir + ('%04d.js' % issue.number)
+ file.open('w') { |f| f.write(serialize_issue(issue)) }
+ @gitrepo.add(file.to_s)
+ end
+
+ def run( orgname, local_repo )
+ Dir.chdir(local_repo) do |path|
+ @gitrepo = begin
+ Grit::Repo.new(".")
+ rescue Grit::InvalidGitRepositoryError => e
+ $logger.warn "Not a git Repository, creating a new one"
+ Grit::Repo.init(".")
+ end
+
+ orgdir = Pathname.new orgname
+
+ repositories = @github.repositories orgname
+
+ repositories.each do |repo|
+ reponame = "#{orgname}/#{repo.name}"
+ $logger.info "Backuping #{reponame}"
+
+ if @github.has_issues? reponame
+ projectdir = orgdir + repo.name
+ projectdir.mkpath
+
+ @github.on_issues(reponame) {|issue| save_issue(projectdir, issue)}
+ else
+ $logger.info "No issues for #{reponame}"
+ end
+ end
+
+ @gitrepo.commit_index("Backup...")
+ $logger.info 'Backup Finished'
+ end
+ end
+
+end
+
+
+class GithubAPIAdapter
+ @@ISSUE_STATES = %w{open closed}
+
+ def initialize(client)
+ @client = client
+ end
+
+ def has_issues?(reponame)
+ @client.repository(reponame).has_issues
+ end
+
+ def on_issues(reponame, &block)
+ @@ISSUE_STATES.each do |status|
+ page = 0
+
+ begin
+ issues = @client.list_issues( reponame, :page => page, :state => status )
+ issues.each do |issue|
+ yield OpenStruct.new(:number => issue.number,
+ :content => @client.issue(reponame, issue.number))
+ end
+ page += 1
+ end until issues.length != 10
+ end
+ end
+
+ def repositories(orgname)
+ @client.repositories(orgname)
+ end
+end
View
@@ -0,0 +1,42 @@
+require 'trollop'
+require 'backup-github'
+
+module BackupGithub
+ module Cli
+
+ def self.parse_options
+ opts = Trollop::options do
+ opt :localrepo, "Path to the local repository where backups are saved",
+ :short => "-r",
+ :type => String,
+ :required => true
+ opt :organization, "Organization as known by Github",
+ :short => "-o",
+ :type => String,
+ :required => true
+ opt :github_user, "Github User",
+ :short => "-u",
+ :type => String,
+ :required => true
+ opt :github_password, "Github Password",
+ :short => "-p",
+ :type => String,
+ :required => true
+ end
+ Trollop::die :localrepo, "must exist" unless File.directory? opts[:localrepo]
+ opts
+ end
+
+ def self.run
+ opts = parse_options
+
+ walker = GithubIssuesWalker.new(
+ Octokit::Client.new(
+ :login => opts[:github_user],
+ :password => opts[:github_password]))
+ GithubBackup.new(walker).run(opts[:organization], opts[:localrepo])
+ end
+
+ end
+end
+
Oops, something went wrong.

0 comments on commit e3155d6

Please sign in to comment.