github_records_archiver 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20d21627161e4f5e187d555f3a2f12b925a633c6c8c3c2ca3bf28b6aee26ed18
4
- data.tar.gz: '0805b746a6b6495fddf921a9638245286ec6d89da41dfcf6c30117f514ea8fb7'
3
+ metadata.gz: 04613c74d230d25723b56ff88a3e34c840d8a8ae56bc7543a386f0bb91fc7ca4
4
+ data.tar.gz: 0eff9d4f24718e4db5107ccfb6ffd8c45f5783f31de16b51edea0324cda20803
5
5
  SHA512:
6
- metadata.gz: 448ef59368b08c0ffb7e221ecad0b0674ed5cbfb85a1dceec9f2ba3bbd5103d00694032decbf896f6febd34fb7ddae2e6324e539e4da70482e2a6df0170e6b8a
7
- data.tar.gz: 2eaec591f98b3359ee9f3769c2797f71db0b94c85480be4f5eb29b80903fa7eccfb357c12b5616347f09fb335982aa5305ae83cd69cfe17a6fa96feef665220d
6
+ metadata.gz: 0704f880ce05d5f0990f36a02bd7962452e072255d88f53d00f5878b035908a7171cf155bdb4284b6cdc8ca5d94ff89f8fbd98fd55bbe6fffb9f6b6226ff860a
7
+ data.tar.gz: 328a9351d1161903ffe67f0b3bbcacb8f1830baf87bd72a157c5306d7ab8af75c84d7df49d689d34c9745fe58c843b479573ba038ab726cd9febd4d89d824867
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  /.env
2
2
  /archive
3
- Gemfile.lock
4
- spec/examples.txt
5
- *.gem
3
+ /coverage/
4
+ /Gemfile.lock
5
+ /spec/examples.txt
6
+ /*.gem
data/.rubocop.yml CHANGED
@@ -2,7 +2,8 @@ AllCops:
2
2
  Exclude:
3
3
  - archive/**/*
4
4
  - vendor/**/*
5
-
5
+ - bin/github-records-archiver
6
+
6
7
  Style/Documentation:
7
8
  Enabled: false
8
9
 
data/Gemfile CHANGED
@@ -3,3 +3,5 @@ source 'https://rubygems.org'
3
3
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  gemspec
6
+
7
+ gem 'coveralls', require: false
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GitHub Records Archiver
2
2
 
3
- [![Build Status](https://travis-ci.org/benbalter/github-records-archiver.svg?branch=master)](https://travis-ci.org/benbalter/github-records-archiver) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
3
+ [![Build Status](https://travis-ci.org/benbalter/github_records_archiver.svg?branch=master)](https://travis-ci.org/benbalter/github_records_archiver) [![Gem Version](https://badge.fury.io/rb/github_records_archiver.svg)](http://badge.fury.io/rb/github_records_archiver) [![Coverage Status](https://coveralls.io/repos/github/benbalter/github_records_archiver/badge.svg)](https://coveralls.io/github/benbalter/github_records_archiver) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
4
4
 
5
5
  Backs up a GitHub organization's repositories and all their associated information for archival purposes.
6
6
 
@@ -14,38 +14,72 @@ Backs up a GitHub organization's repositories and all their associated informati
14
14
  ## Requirements
15
15
 
16
16
  1. Ruby
17
- 2. A GitHub [personal access token](https://github.com/settings/tokens/new) with `public_repo` and `repo` scope.
17
+ 2. A GitHub [personal access token](https://github.com/settings/tokens/new) with `repo` scope.
18
18
 
19
19
  ## Setup
20
20
 
21
- 1. `git clone https://github.com/benbalter/github-records-archiver`
22
- 2. `cd github-records-archiver`
23
- 3. `gem install bundler`
24
- 4. `bundle install`
21
+ If you have Ruby installed, simply run:
25
22
 
26
- ## Usage
23
+ ```shell
24
+ gem install github_records_archiver
25
+ ```
27
26
 
28
- `bin/archive [ORGANIZATION]`
27
+ ## Basic usage
29
28
 
30
- You'll want to set the following environmental variable:
29
+ ```shell
30
+ $ github_records_archiver archive ORGANIZATION --token PERSONAL_ACCESS_TOKEN`
31
+ ```
32
+ Alternatively, you could pass the personal access token as the `GITHUB_TOKEN` environmental variable:
31
33
 
32
- * `GITHUB_TOKEN` - Your personal access token
33
-
34
- You *may* set the following environmental variables:
35
-
36
- * `GITHUB_ARCHIVE_DIR` to specify the output directory. It will default to `./archive`.
37
- * `GITHUB_ORGANIZATION` - The organization to archive if none is passed as an argument.
38
-
39
- These can be passed as `GITHUB_TOKEN=123ABC GITHUB_ORGANIZATION=whitehouse bin/archive`.
40
-
41
- You can also add the values to a `.env` file in the project's root directory, which will be automatically set as environmental variables.
34
+ ```shell
35
+ $ GITHUB_TOKEN=1234 github_records_archiver archive ORGANIZATION`
36
+ ```
42
37
 
43
38
  ## Output
44
39
 
45
- The script will create an `archive` directory, with one folder for each repository.
40
+ The script will create an `archive` directory, with one folder for each organization.
41
+
42
+ Within each organization folder, there will be one folder per repository.
46
43
 
47
- Within each folder will be the repository content as a git repository.
44
+ Within each repository folder will be the repository content as a git repository.
48
45
 
49
46
  If the repository has a Wiki, the wiki will be cloned as a `wiki` subfolder, as a Git repository.
50
47
 
51
48
  If the repository has issues or pull requests, it will create an `issues` sub-folder with each issue and its associated comments stored as both markdown (human readable) and JSON (machine readable).
49
+
50
+ Example output:
51
+
52
+ ```
53
+ ├─ archive
54
+ ├─── organization
55
+ ├──── repository
56
+ ├────── README.md
57
+ ├────── LICENSE.txt
58
+ ├──── wiki
59
+ ├────── wiki-page.md
60
+ ├──── issues
61
+ ├────── 1.md
62
+ ├────── 1.json
63
+ ├─── another organization
64
+ ├──── another-repository
65
+ ├────── README.md
66
+ ├────── LICENSE.txt
67
+ ├──── wiki
68
+ ├────── wiki-page.md
69
+ ├──── issues
70
+ ├────── 1.md
71
+ ├────── 1.json
72
+ ```
73
+
74
+ ## Advanced usage
75
+
76
+ You may set the following flags:
77
+
78
+ * `--dest-dir` - the destination archive directory, defaults to `./archive`
79
+ * `--verbose` - verbose output while archiving
80
+
81
+ Additionally, the following commands are also available:
82
+
83
+ * `delete [ORGANIZATION]` - delete the entire archive directory or an organization's archive
84
+ * `help` - display help information
85
+ * `version` - display the GitHub Record Archiver version
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'parallel'
5
+ require_relative '../lib/github_records_archiver'
6
+
7
+ class GitHubRecordsArchiverCLI < Thor
8
+ package_name 'GitHub Records Archiver'
9
+ class_option :dest_dir, type: :string, required: false,
10
+ desc: "The destination directory for the archive", default: GitHubRecordsArchiver.dest_dir
11
+ class_option :token, type: :string, required: false, desc: "A GitHub personal access token with repo scope"
12
+ class_option :verbose, type: :boolean, desc: 'Display verbose output while archiving', default: false
13
+
14
+ desc 'version', 'Outputs the GitHubRecordsArchiver version'
15
+ def version
16
+ say "GitHub Records Archiver v#{GitHubRecordsArchiver::VERSION}"
17
+ end
18
+
19
+ desc "delete [ORGANIZATION]", "Deletes all archives, or the archive for an organization"
20
+ def delete(org_name = nil)
21
+ path = GitHubRecordsArchiver.dest_dir
22
+ path = File.join path, org_name if org_name
23
+ FileUtils.rm_rf(path) if yes? "Are you sure? Remove #{path}?"
24
+ end
25
+
26
+ desc 'archive ORGANIZATION', 'Create or update archive for the given organization'
27
+ def archive(org_name)
28
+ start_time # Memoize start time for comparison
29
+ @org_name = org_name
30
+
31
+ GitHubRecordsArchiver.shell = shell
32
+ %i(token dest_dir verbose).each do |option|
33
+ next unless options[option]
34
+ GitHubRecordsArchiver.public_send "#{option}=".to_sym, options[option]
35
+ end
36
+
37
+ say "Starting archive for @#{org.name} in #{org.archive_dir}"
38
+ shell.indent(2) do
39
+ archive_teams
40
+ archive_repos
41
+ end
42
+ say "Done in #{Time.now - start_time} seconds.", :green
43
+ end
44
+
45
+ private
46
+
47
+ def start_time
48
+ @start_time ||= Time.now
49
+ end
50
+
51
+ def organization
52
+ @organization ||= GitHubRecordsArchiver::Organization.new @org_name
53
+ end
54
+ alias_method :org, :organization
55
+
56
+ def archive_teams
57
+ say_status "Teams found:", org.teams.count, :white
58
+ Parallel.each(org.teams, progress: 'Archiving teams', &:archive)
59
+ end
60
+
61
+ def archive_repos
62
+ say_status "Repositories found:", org.repos.count, :white
63
+
64
+ Parallel.each(org.repos, progress: 'Archiving repos') do |repo|
65
+ begin
66
+ repo.clone
67
+ repo.wiki.clone if repo.has_wiki?
68
+ Parallel.each(repo.issues, &:archive)
69
+ rescue GitHubRecordsArchiver::GitError => e
70
+ say "Failed to archive #{repo.name}", :red
71
+ say e.message, :red
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ GitHubRecordsArchiverCLI.start(ARGV)
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency 'octokit', '~> 4.0'
29
29
  spec.add_dependency 'parallel', '~> 1.10'
30
30
  spec.add_dependency 'ruby-progressbar', '~> 1.0'
31
+ spec.add_dependency 'thor', '~> 0.19'
31
32
  spec.add_development_dependency 'addressable', '~> 2.5'
32
33
  spec.add_development_dependency 'bundler', '~> 1.16'
33
34
  spec.add_development_dependency 'pry', '~> 0.10'
@@ -1,18 +1,14 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
 
3
- # stdlib
4
3
  require 'yaml'
5
4
  require 'json'
6
5
  require 'logger'
7
6
  require 'fileutils'
8
7
  require 'open3'
9
-
10
- # gems
8
+ require 'thor'
11
9
  require 'octokit'
12
- require 'dotenv'
10
+ require 'dotenv/load'
13
11
 
14
- # Configuration
15
- Dotenv.load
16
12
  Octokit.auto_paginate = true
17
13
 
18
14
  module GitHubRecordsArchiver
@@ -28,8 +24,10 @@ module GitHubRecordsArchiver
28
24
  autoload :Wiki, 'github_records_archiver/wiki'
29
25
 
30
26
  class << self
27
+ attr_writer :token, :dest_dir, :verbose, :shell
28
+
31
29
  def token
32
- ENV['GITHUB_TOKEN']
30
+ @token ||= ENV['GITHUB_TOKEN']
33
31
  end
34
32
 
35
33
  def client
@@ -37,7 +35,25 @@ module GitHubRecordsArchiver
37
35
  end
38
36
 
39
37
  def dest_dir
40
- ENV['GITHUB_ARCHIVE_DIR'] || File.expand_path('./archive', Dir.pwd)
38
+ @dest_dir ||= File.expand_path('./archive', Dir.pwd)
39
+ end
40
+
41
+ def verbose
42
+ @verbose ||= false
43
+ end
44
+ alias verbose? verbose
45
+
46
+ def shell
47
+ @shell ||= Thor::Base.shell.new
48
+ end
49
+
50
+ def verbose_status(status, message, color = :white)
51
+ return unless verbose?
52
+ shell.say_status status, remove_token(message), color
53
+ end
54
+
55
+ def remove_token(string)
56
+ string.gsub(GitHubRecordsArchiver.token, '<GITHUB_TOKEN>')
41
57
  end
42
58
  end
43
59
  end
@@ -3,11 +3,13 @@ module GitHubRecordsArchiver
3
3
 
4
4
  class GitRepository
5
5
  def clone
6
- if Dir.exist? repo_dir # Repo already exists, just pull new objects
6
+ # Repo already exists, just pull new objects
7
+ if Dir.exist? File.join(repo_dir, '.git')
7
8
  Dir.chdir repo_dir do
8
9
  git 'pull'
9
10
  end
10
- else # Clone Git content from scratch
11
+ else
12
+ # Clone Git content from scratch
11
13
  git 'clone', clone_url, repo_dir
12
14
  end
13
15
  end
@@ -43,8 +45,14 @@ module GitHubRecordsArchiver
43
45
  # Run a git command, piping output to stdout
44
46
  def git(*args)
45
47
  output, status = Open3.capture2e('git', *args)
48
+ cmd = "git #{args.join(' ')}"
49
+ cmd << " in #{Dir.pwd}" if args == ['pull']
50
+ GitHubRecordsArchiver.verbose_status 'Git command:', cmd
46
51
  return false if empty_repo?(output) || wiki_does_not_exist?(output)
47
- raise GitError, output if status.exitstatus != 0
52
+ if status.exitstatus != 0
53
+ output = GitHubRecordsArchiver.remove_token(output)
54
+ GitHubRecordsArchiver.shell.say_status 'Git Error', output, :red
55
+ end
48
56
  output
49
57
  end
50
58
  end
@@ -40,15 +40,13 @@ module GitHubRecordsArchiver
40
40
  end
41
41
  end
42
42
 
43
- private
44
-
45
- def repo_dir
46
- @repo_dir ||= File.expand_path data[:name], GitHubRecordsArchiver.dest_dir
47
- end
48
-
49
43
  def clone_url
50
44
  replacement = "https://#{GitHubRecordsArchiver.token}:x-oauth-basic@"
51
45
  data[:clone_url].gsub(%r{https?://}, replacement)
52
46
  end
47
+
48
+ def repo_dir
49
+ @repo_dir ||= File.expand_path full_name, GitHubRecordsArchiver.dest_dir
50
+ end
53
51
  end
54
52
  end
@@ -1,3 +1,3 @@
1
1
  module GitHubRecordsArchiver
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -13,8 +13,7 @@ module GitHubRecordsArchiver
13
13
  end
14
14
 
15
15
  def repo_dir
16
- name = "#{repository.full_name}/wiki"
17
- @repo_dir ||= File.expand_path name, GitHubRecordsArchiver.dest_dir
16
+ @repo_dir ||= File.join repository.repo_dir, 'wiki'
18
17
  end
19
18
 
20
19
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github_records_archiver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Balter
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.19'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.19'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: addressable
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -168,10 +182,11 @@ description:
168
182
  email:
169
183
  - ben.balter@github.com
170
184
  executables:
171
- - archive
185
+ - github-records-archiver
172
186
  extensions: []
173
187
  extra_rdoc_files: []
174
188
  files:
189
+ - ".coveralls.yml"
175
190
  - ".github/CODEOWNERS"
176
191
  - ".github/config.yml"
177
192
  - ".github/no-response.yml"
@@ -185,7 +200,7 @@ files:
185
200
  - LICENSE.md
186
201
  - README.md
187
202
  - Rakefile
188
- - bin/archive
203
+ - bin/github-records-archiver
189
204
  - docs/CODE_OF_CONDUCT.md
190
205
  - docs/CONTRIBUTING.md
191
206
  - github_records_archiver.gemspec
data/bin/archive DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # Backs up a GitHub organization's repositories and
3
- # all their associated information for archival purposes.
4
- # Usage: ruby archive.rb
5
-
6
- require './lib/github_records_archiver'
7
- require 'parallel'
8
-
9
- def logger
10
- @logger ||= Logger.new(STDOUT)
11
- end
12
-
13
- def log(msg)
14
- logger.info(msg)
15
- end
16
-
17
- def error(msg)
18
- logger.error(msg)
19
- end
20
-
21
- archiver = GitHubRecordsArchiver
22
- pwd = Dir.pwd
23
- start = Time.now
24
- org_name = ARGV[0] || ENV['GITHUB_ORGANIZATION']
25
- org = archiver::Organization.new org_name
26
-
27
- log "Starting archive for @#{org.name} in #{org.archive_dir}"
28
-
29
- log "Found #{org.teams.count} teams"
30
- Parallel.each(org.teams, progress: 'Archiving teams', &:archive)
31
-
32
- log "Found #{org.repos.count} repos"
33
- Parallel.each(org.repos, progress: 'Archiving repos') do |repo|
34
- begin
35
- repo.clone
36
- repo.wiki.clone if repo.has_wiki?
37
- Parallel.each(repo.issues, &:archive)
38
- rescue GitHubRecordsArchiver::GitError => e
39
- error "Failed to archive #{repo.name}"
40
- error e.message
41
- end
42
- end
43
-
44
- Dir.chdir pwd
45
- log "Done in #{Time.now - start} seconds."