huborg 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 +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +78 -19
- data/Rakefile +46 -0
- data/huborg.gemspec +3 -0
- data/lib/huborg.rb +244 -20
- data/lib/huborg/version.rb +3 -1
- metadata +45 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bca5b4b64bf1da62ccbb67ce27f6cf459ca1ec65c3989ed1d8fb095cdf8bb956
|
4
|
+
data.tar.gz: 2c44b381a4cdd5cabcae0afa7f35e83dbbd643b5d5f6df2025fcdc5687033736
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6c8b6fe6e75a86ee922a0e52dc2515aae8a47f01394b04c2c8c17ddf785c04d9f7bf547bf227d8736ae638e2ca3b25d8b53ac9b856a3123e34ce1c6c068508b
|
7
|
+
data.tar.gz: 67bdc6e7138eb137393d34c2ffd9275fe6db99979c93bac6bfb17feb0e47c15f6f1b570f36db7e605bd0e9b40bb6b8482720bde62c5551e41a09ca87626d820b
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [v0.2.0](https://github.com/samvera-labs/huborg/tree/v0.2.0) (2020-02-28)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/samvera-labs/huborg/compare/v0.1.0...v0.2.0)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Adding release process [\#4](https://github.com/samvera-labs/huborg/pull/4) ([jeremyf](https://github.com/jeremyf))
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Tweaking documentation [\#5](https://github.com/samvera-labs/huborg/pull/5) ([jeremyf](https://github.com/jeremyf))
|
14
|
+
- Adding Huborg::Client\#synchronize\_mailmap! [\#3](https://github.com/samvera-labs/huborg/pull/3) ([jeremyf](https://github.com/jeremyf))
|
15
|
+
- Adding Huborg::Client\#audit\_license [\#2](https://github.com/samvera-labs/huborg/pull/2) ([jeremyf](https://github.com/jeremyf))
|
16
|
+
- Adding Huborg::Client\#clone\_and\_rebase! [\#1](https://github.com/samvera-labs/huborg/pull/1) ([jeremyf](https://github.com/jeremyf))
|
17
|
+
|
18
|
+
## [v0.1.0](https://github.com/samvera-labs/huborg/tree/v0.1.0) (2020-02-26)
|
19
|
+
|
20
|
+
[Full Changelog](https://github.com/samvera-labs/huborg/commit/cc1ce07147ad552cabf343c8195610bbb8c9376d)
|
21
|
+
|
22
|
+
- Adding Huborg::Client\#push\_template! ([jeremyf](https://github.com/jeremyf))
|
23
|
+
|
24
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/README.md
CHANGED
@@ -45,6 +45,12 @@ client = Huborg::Client.new(org_names: ["samvera", "samvera-labs"])
|
|
45
45
|
client.push_template!(
|
46
46
|
template: "/path/to/file/on/your/system",
|
47
47
|
filename: "relative/path/in/repository"
|
48
|
+
|
49
|
+
# This will clone all repositories from samvera and samvera-labs.
|
50
|
+
# You can expect to see something in `~/git/samvera/hyrax` and
|
51
|
+
# `~/git/samvera-labs/huborg`
|
52
|
+
client.clone_and_rebase!(
|
53
|
+
directory: File.join(ENV["HOME"], "git")
|
48
54
|
)
|
49
55
|
```
|
50
56
|
|
@@ -53,27 +59,47 @@ The above will create a pull request against all repositories in
|
|
53
59
|
named `repository` (in the directory `relative/path/in`). The file's
|
54
60
|
content will be from the file `/path/to/file/on/your/system`.
|
55
61
|
|
56
|
-
|
62
|
+
```ruby
|
63
|
+
require 'huborg'
|
57
64
|
|
58
|
-
|
65
|
+
# NOTE: You will need to include GITHUB_ACCESS_TOKEN in your ENV
|
66
|
+
client = Huborg::Client.new(org_names: ["samvera", "samvera-labs"])
|
67
|
+
client.clone_and_rebase!(
|
68
|
+
directory: File.join(ENV["HOME"], "git")
|
69
|
+
)
|
70
|
+
```
|
71
|
+
|
72
|
+
The above will clone and rebase all repositories from samvera and
|
73
|
+
samvera-labs. The script will skip existing repositories. You can
|
74
|
+
expect to see [Hyrax](https://github.com/samvera/hyrax) cloned into
|
75
|
+
`~/git/samvera/hyrax` and [Huborg](https://github.com/samvera-labs/huborg)
|
76
|
+
cloned into `~/git/samvera-labs/huborg`
|
59
77
|
|
60
78
|
```ruby
|
61
79
|
require 'huborg'
|
62
|
-
|
80
|
+
# NOTE: You will need to include GITHUB_ACCESS_TOKEN in your ENV
|
81
|
+
client = Huborg::Client.new(org_names: ["samvera"])
|
82
|
+
client.audit_license
|
83
|
+
```
|
63
84
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
client.
|
71
|
-
|
72
|
-
filename: "relative/path/in/repository",
|
73
|
-
overwrite: true
|
74
|
-
)
|
85
|
+
The above script leverages Github's API to check each repository's
|
86
|
+
license. Log as an error each repository that does not have a license.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
require 'huborg'
|
90
|
+
# NOTE: You will need to include GITHUB_ACCESS_TOKEN in your ENV
|
91
|
+
client = Huborg::Client.new(org_names: ["samvera"])
|
92
|
+
client.synchronize_mailmap!(template: '/path/to/my/MAILMAP_TEMPLATE')
|
75
93
|
```
|
76
94
|
|
95
|
+
The above will take the given template (which confirms to [Git's .mailmap
|
96
|
+
file format](https://www.git-scm.com/docs/git-check-mailmap), then
|
97
|
+
iterates on all of the repositories, adding any non-duplicates, then
|
98
|
+
writing back to the template before creating pull requests against
|
99
|
+
each of the organization's non-archived repositories.
|
100
|
+
|
101
|
+
**All of the commands have several parameters, many set to default values.**
|
102
|
+
|
77
103
|
## Prerequisites
|
78
104
|
|
79
105
|
You'll want to have created a [Github OAuth Access Token](https://github.com/octokit/octokit.rb#oauth-access-tokens).
|
@@ -96,11 +122,44 @@ $ export GITHUB_ORG_NAME=their-organization
|
|
96
122
|
$ bundle exec rake test:push_template
|
97
123
|
```
|
98
124
|
|
99
|
-
##
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
125
|
+
## Documentation
|
126
|
+
|
127
|
+
The product owner encourages you to clone this repository and generate the
|
128
|
+
documentation.
|
129
|
+
|
130
|
+
- [ ] `git clone https://github.com/samvera-labs/huborg`
|
131
|
+
- [ ] `cd huborg`
|
132
|
+
- [ ] `git checkout master && git pull --rebase`
|
133
|
+
- [ ] `gem install yard`
|
134
|
+
- [ ] `yard`
|
135
|
+
|
136
|
+
The above process will generate documentation in `./doc`. Open `./doc/index.html`
|
137
|
+
in your browser. (On OSX, try `open ./doc/index.html`).
|
138
|
+
|
139
|
+
## Releasing Huborg
|
140
|
+
|
141
|
+
Huborg uses [Semantic Versioning](https://semver.org/).
|
142
|
+
|
143
|
+
Below is the checklist:
|
144
|
+
|
145
|
+
- [ ] An internet connection
|
146
|
+
- [ ] A [Github Access Token](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/)
|
147
|
+
- [ ] Your access token exported to `ENV["CHANGELOG_GITHUB_TOKEN"]`
|
148
|
+
- [ ] Verify you are a [huborg gem owner](https://rubygems.org/gems/huborg). If not, Samvera uses
|
149
|
+
[`grant_revoke_gem_authority`](https://github.com/samvera/maintenance/blob/master/script/grant_revoke_gem_authority.rb)
|
150
|
+
to manage the gem owners.
|
151
|
+
- [ ] Verify that you can push changes to https://github.com/samvera-labs/huborg
|
152
|
+
- [ ] Check that you have a clean git index
|
153
|
+
- [ ] Pull down the latest version of master
|
154
|
+
- [ ] Update the Huborg::VERSION (in ./lib/huborg/version.rb); Remember, huborg
|
155
|
+
uses [Semantic Versioning](https://semver.org). (_**NOTE:** Do not commit the version change_)
|
156
|
+
- [ ] Run `bundle exec rake changelog` to generate [CHANGELOG.md](./CHANGELOG.md)
|
157
|
+
- [ ] Review the new Huborg::VERSION CHANGELOG.md entries as they might prompt
|
158
|
+
you to consider a different version (e.g. what you thought was a bug fix
|
159
|
+
release is in fact a minor version release). Look at the changelog from
|
160
|
+
the perspective of a person curious about using this gem.
|
161
|
+
- [ ] Commit your changes with a simple message: "Bumping to v#{Huborg::VERSION}"
|
162
|
+
- [ ] Run `bundle exec rake release`
|
104
163
|
|
105
164
|
# Acknowledgments
|
106
165
|
|
data/Rakefile
CHANGED
@@ -13,4 +13,50 @@ namespace :test do
|
|
13
13
|
filename: "disposable-#{Time.now.utc.to_s.gsub(/\D+/,'')}.rake"
|
14
14
|
)
|
15
15
|
end
|
16
|
+
|
17
|
+
task :clone_and_rebase do
|
18
|
+
require 'huborg'
|
19
|
+
client = Huborg::Client.new(
|
20
|
+
github_access_token: ENV.fetch("GITHUB_ACCESS_TOKEN"),
|
21
|
+
org_names: ENV.fetch("GITHUB_ORG_NAME")
|
22
|
+
)
|
23
|
+
directory = ENV.fetch("DIRECTORY") { File.join(ENV.fetch("HOME"), "git") }
|
24
|
+
client.clone_and_rebase!(directory: directory)
|
25
|
+
end
|
26
|
+
|
27
|
+
task :audit_license do
|
28
|
+
require 'huborg'
|
29
|
+
client = Huborg::Client.new(
|
30
|
+
github_access_token: ENV.fetch("GITHUB_ACCESS_TOKEN"),
|
31
|
+
org_names: ENV.fetch("GITHUB_ORG_NAME")
|
32
|
+
)
|
33
|
+
client.audit_license
|
34
|
+
end
|
35
|
+
|
36
|
+
task :mailmap do
|
37
|
+
require 'huborg'
|
38
|
+
client = Huborg::Client.new(
|
39
|
+
github_access_token: ENV.fetch("GITHUB_ACCESS_TOKEN"),
|
40
|
+
org_names: ENV.fetch("GITHUB_ORG_NAME")
|
41
|
+
)
|
42
|
+
client.synchronize_mailmap!(template: ENV.fetch("MAILMAP_TEMPLATE_FILENAME"))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
require 'github_changelog_generator/task'
|
48
|
+
desc "Generate CHANGELOG.md based on lib/huborg/version.md (change that to the new version before you run rake changelog)"
|
49
|
+
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
|
50
|
+
begin
|
51
|
+
ENV.fetch("CHANGELOG_GITHUB_TOKEN")
|
52
|
+
rescue KeyError
|
53
|
+
$stderr.puts %(To run `rake changelog` you need to have a CHANGELOG_GITHUB_TOKEN)
|
54
|
+
$stderr.puts %(set in ENV. (`export CHANGELOG_GITHUB_TOKEN="«your-40-digit-github-token»"`))
|
55
|
+
exit!(1)
|
56
|
+
end
|
57
|
+
config.user = 'samvera-labs'
|
58
|
+
config.project = 'huborg'
|
59
|
+
config.since_tag = 'v0.1.0' # The changes before v0.1.0 were not as helpful
|
60
|
+
config.future_release = %(v#{ENV.fetch("FUTURE_RELEASE", Huborg::VERSION)})
|
61
|
+
config.base = 'CHANGELOG.md'
|
16
62
|
end
|
data/huborg.gemspec
CHANGED
@@ -24,4 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ["lib"]
|
26
26
|
spec.add_dependency "octokit", "~> 4.16"
|
27
|
+
spec.add_dependency "git", "~> 1.6"
|
28
|
+
spec.add_development_dependency "byebug"
|
29
|
+
spec.add_development_dependency "github_changelog_generator"
|
27
30
|
end
|
data/lib/huborg.rb
CHANGED
@@ -1,34 +1,66 @@
|
|
1
1
|
require "huborg/version"
|
2
2
|
require 'octokit'
|
3
|
+
require 'git'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'set'
|
3
6
|
|
7
|
+
# A module for interacting with Github organizations
|
4
8
|
module Huborg
|
9
|
+
# If there's a problem with Huborg, expect to see this error OR an
|
10
|
+
# error from an underlying library.
|
5
11
|
class Error < RuntimeError; end
|
6
12
|
|
7
|
-
# The class that interacts with organizational repositories
|
13
|
+
# The class that interacts with organizational repositories.
|
14
|
+
#
|
15
|
+
# * {#push_template!} - push a file to all repositories
|
16
|
+
# * {#clone_and_rebase!} - download all repositories for the org
|
17
|
+
# * {#audit_license} - tooling to check the licenses of your org
|
18
|
+
# * {#synchronize_mailmap!} - ensure all git .mailmap files are
|
19
|
+
# synchronized
|
8
20
|
class Client
|
9
|
-
|
10
|
-
#
|
21
|
+
# When checking repository names, this pattern will match all repositories.
|
22
|
+
# @see #initialize `#initialize` for details on the repository pattern.
|
11
23
|
DEFAULT_REPOSITORY_PATTERN = %r{\A.*\Z}
|
12
24
|
|
25
|
+
# @since v0.1.0
|
26
|
+
#
|
13
27
|
# @param logger [Logger] used in logging output of processes
|
14
28
|
# @param github_access_token [String] used to connect to the Oktokit::Client.
|
15
29
|
# The given token will need to have permission to interact with
|
16
30
|
# repositories. Defaults to ENV["GITHUB_ACCESS_TOKEN"]
|
17
|
-
# @param org_names [Array<String
|
31
|
+
# @param org_names [Array<String>, String] used as the default list of Github organizations
|
18
32
|
# in which we'll interact.
|
19
33
|
# @param repository_pattern [Regexp] limit the list of repositories to the given pattern; defaults to ALL
|
20
34
|
#
|
21
|
-
# @
|
35
|
+
# @example
|
36
|
+
# # Without configuration options. You'll want ENV["GITHUB_ACCESS_TOKEN"]
|
37
|
+
# # to be assigned.
|
38
|
+
# client = Huborg::Client.new(org_names: ["samvera", "samvera-labs"])
|
39
|
+
#
|
40
|
+
# # With explicit configuration options. Note, we'll be interacting only
|
41
|
+
# # with repositories that contain the case-insensitive word "hyrax" (case-
|
42
|
+
# # insensitivity is declared as the `i` at the end of the regular
|
43
|
+
# # expression).
|
44
|
+
# client = Huborg::Client.new(
|
45
|
+
# logger: Logger.new(STDOUT),
|
46
|
+
# github_access_token: "40-random-characters-for-your-token",
|
47
|
+
# org_names: ["samvera", "samvera-labs"],
|
48
|
+
# repository_patter: %r{hyrax}i
|
49
|
+
# )
|
50
|
+
#
|
51
|
+
# @see https://github.com/octokit/octokit.rb#oauth-access-tokens Octokit's documentation for OAuth Tokens
|
52
|
+
# @see https://developer.github.com/v3/repos/#list-organization-repositories Github's documentation for repository data structures
|
22
53
|
def initialize(logger: default_logger, github_access_token: default_access_token, org_names:, repository_pattern: DEFAULT_REPOSITORY_PATTERN)
|
23
54
|
@logger = logger
|
24
55
|
@client = Octokit::Client.new(access_token: github_access_token)
|
25
56
|
@org_names = Array(org_names)
|
26
57
|
@repository_pattern = repository_pattern
|
27
58
|
end
|
28
|
-
attr_reader :client, :logger, :org_names, :repository_pattern
|
29
59
|
|
30
60
|
private
|
31
61
|
|
62
|
+
attr_reader :client, :logger, :org_names, :repository_pattern
|
63
|
+
|
32
64
|
def default_logger
|
33
65
|
require 'logger'
|
34
66
|
Logger.new(STDOUT)
|
@@ -37,13 +69,15 @@ module Huborg
|
|
37
69
|
def default_access_token
|
38
70
|
ENV.fetch('GITHUB_ACCESS_TOKEN')
|
39
71
|
rescue KeyError => e
|
40
|
-
|
41
|
-
|
72
|
+
message = "You need to provide an OAuth Access Token.\nSee: https://github.com/octokit/octokit.rb#oauth-access-tokens"
|
73
|
+
$stderr.puts message
|
74
|
+
raise Error.new(message)
|
42
75
|
end
|
43
76
|
|
44
77
|
public
|
45
78
|
|
46
79
|
# @api public
|
80
|
+
# @since v0.1.0
|
47
81
|
#
|
48
82
|
# Responsible for pushing the given template file to all of the
|
49
83
|
# organizations repositories. As we are pushing changes to
|
@@ -61,23 +95,178 @@ module Huborg
|
|
61
95
|
# would not want to do that. In the case of a .mailmap, we
|
62
96
|
# would likely want to overwrite.
|
63
97
|
# @todo Verify that the template exists
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# client = Huborg::Client.new(
|
101
|
+
# github_access_token: ENV.fetch("GITHUB_ACCESS_TOKEN"),
|
102
|
+
# org_names: ENV.fetch("GITHUB_ORG_NAME")
|
103
|
+
# )
|
104
|
+
# client.push_template!(
|
105
|
+
# template: "/path/to/file/on/your/system",
|
106
|
+
# filename: "relative/path/in/repository"
|
107
|
+
# )
|
108
|
+
# @return [True] if successfully completed
|
64
109
|
def push_template!(template:, filename:, overwrite: false)
|
65
|
-
|
110
|
+
each_github_repository do |repo|
|
66
111
|
push_template_to!(repo: repo, template: template, filename: filename, overwrite: overwrite)
|
67
112
|
end
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
# @api public
|
117
|
+
# @since v0.2.0
|
118
|
+
#
|
119
|
+
# Responsible for logging (as an error) repositories that do not
|
120
|
+
# have a license.
|
121
|
+
#
|
122
|
+
# @param skip_private [Boolean] do not check private repositories for a
|
123
|
+
# license
|
124
|
+
# @param skip_archived [Boolean] do not check archived repositories
|
125
|
+
# for license
|
126
|
+
# @param allowed_licenses [Array<String>, :all] the licenses which are
|
127
|
+
# allowed, in all other cases, log as an error. This checks
|
128
|
+
# the :key of a license object in Github's API (see
|
129
|
+
# https://api.github.com/licenses)
|
130
|
+
#
|
131
|
+
# @see https://api.github.com/licenses Github's documentation for a list of license keys
|
132
|
+
# @return [True] the task completed without exception (there may be
|
133
|
+
# logged errors)
|
134
|
+
def audit_license(skip_private: true, skip_archived: true, allowed_licenses: :all)
|
135
|
+
license_list = Array(allowed_licenses)
|
136
|
+
each_github_repository do |repo|
|
137
|
+
next if skip_private && repo.private?
|
138
|
+
next if skip_archived && repo.archived?
|
139
|
+
if repo.license
|
140
|
+
logger.info(%(#{repo.fullname} has "#{repo.license.key}"))
|
141
|
+
next if allowed_licenses == :all
|
142
|
+
next if license_list.include?(repo.license.key)
|
143
|
+
logger.error(%(#{repo.full_name} has "#{repo.license.key}" which is not in #{license_list.inspect}))
|
144
|
+
else
|
145
|
+
logger.error("#{repo.full_name} is missing a license")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
true
|
68
149
|
end
|
69
150
|
|
70
|
-
# @api
|
71
|
-
# @
|
151
|
+
# @api public
|
152
|
+
# @since v0.2.0
|
72
153
|
#
|
154
|
+
# Responsible for taking a template that confirms to Git's .mailmap
|
155
|
+
# file format (e.g. https://github.com/samvera/maintenance/blob/master/templates/MAILMAP)
|
156
|
+
# and adding in all .mailmap entries that exist within the
|
157
|
+
# organizations repositories. This merged .mailmap is then pushed
|
158
|
+
# out, via a pull request, to all of the non-archived repositories.
|
159
|
+
#
|
160
|
+
# @param template [String] path to the source template for .mailmap
|
161
|
+
# This does assume that there were prior efforts at
|
162
|
+
# consolidating a .mailmap file. If you don't have this,
|
163
|
+
# pass an empty file.
|
164
|
+
# @param consolidated_template [String] path that we will write our
|
165
|
+
# changes, this file will be pushed to all non-archived
|
166
|
+
# repositories
|
167
|
+
# @return [True] if successfully completed
|
168
|
+
# @see https://www.git-scm.com/docs/git-check-mailmap Git's documentation
|
169
|
+
# for more on git's .mailmap file
|
170
|
+
# @todo Ensure that this doesn't create a pull request if nothing
|
171
|
+
# has changed.
|
172
|
+
def synchronize_mailmap!(template:, consolidated_template: template)
|
173
|
+
mailmap_lines = Set.new
|
174
|
+
File.read(template).split("\n").each do |line|
|
175
|
+
mailmap_lines << line unless line.empty?
|
176
|
+
end
|
177
|
+
|
178
|
+
each_github_repository do |repo|
|
179
|
+
begin
|
180
|
+
mailmap = client.contents(repo.full_name, path: '.mailmap')
|
181
|
+
lines = mailmap.rels[:download].get.data
|
182
|
+
lines.split("\n").each do |line|
|
183
|
+
mailmap_lines << line
|
184
|
+
end
|
185
|
+
rescue Octokit::NotFound
|
186
|
+
next
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Write the contents to a file
|
191
|
+
File.open(consolidated_template, "w+") do |file|
|
192
|
+
mailmap_lines.to_a.sort.each do |line|
|
193
|
+
file.puts line
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
each_github_repository do |repo|
|
198
|
+
next if repo.archived?
|
199
|
+
push_template_to!(filename: ".mailmap", template: consolidated_template, repo: repo, overwrite: true)
|
200
|
+
end
|
201
|
+
return true
|
202
|
+
end
|
203
|
+
|
204
|
+
# @api public
|
205
|
+
# @since v0.2.0
|
206
|
+
#
|
207
|
+
# Clone all repositories (that match the {#repository_pattern} for
|
208
|
+
# the given organization(s). Then and rebase any existing repositories.
|
209
|
+
#
|
210
|
+
# @param directory [String] the directory in which to clone the repositories
|
211
|
+
# (as a sub-directory)
|
212
|
+
# @param skip_dirty [Boolean] if the repository already exists there, don't
|
213
|
+
# clone or pull down changes
|
214
|
+
# @param force [Boolean] if we want to obliterate any changes in an existing
|
215
|
+
# repository
|
216
|
+
# @param shallow [Boolean] when true, instead of cloning into a
|
217
|
+
# subdirectory of `org/repo`, clone into `repo`.
|
218
|
+
# @param skip_forked [Boolean] when true, don't clone a repository that is
|
219
|
+
# a fork of some other repository.
|
220
|
+
# @param skip_archived [Boolean] when true, don't clone a repository that is
|
221
|
+
# archived on Github.
|
222
|
+
# @note The Product Owner decided to set `shallow: false` as the default, as
|
223
|
+
# other local scripts run by them made use of those directory
|
224
|
+
# structures.
|
225
|
+
#
|
226
|
+
# @example
|
227
|
+
# client = Huborg::Client.new(
|
228
|
+
# github_access_token: ENV.fetch("GITHUB_ACCESS_TOKEN"),
|
229
|
+
# org_names: ENV.fetch("GITHUB_ORG_NAME")
|
230
|
+
# )
|
231
|
+
# directory = ENV.fetch("DIRECTORY") { File.join(ENV.fetch("HOME"), "git") }
|
232
|
+
# client.clone_and_rebase!(directory: directory)
|
233
|
+
#
|
234
|
+
# Let's say we have a Github Organization "penguin" which has the
|
235
|
+
# repositories "paradigm" and "raft". In the above example, if we
|
236
|
+
# specified the `DIRECTORY` as "/Iceflow", we would end up with the
|
237
|
+
# following local directory tree within /Iceflow:
|
238
|
+
# .
|
239
|
+
# └── penguin
|
240
|
+
# ├── paradigm
|
241
|
+
# └── raft
|
242
|
+
#
|
243
|
+
# In the case of `shallow: true`, we would have the following tree within
|
244
|
+
# /Iceflow:
|
245
|
+
# .
|
246
|
+
# ├── paradigm
|
247
|
+
# └── raft
|
248
|
+
#
|
249
|
+
# @return [True] if successfully completed
|
250
|
+
def clone_and_rebase!(directory:, skip_forked: false, skip_archived: false, skip_dirty: true, force: false, shallow: false)
|
251
|
+
each_github_repository do |repo|
|
252
|
+
next if skip_archived && repo.archived?
|
253
|
+
next if skip_forked && repo.fork?
|
254
|
+
clone_and_rebase_one!(repo: repo, directory: directory, skip_dirty: skip_dirty, force: force, shallow: shallow)
|
255
|
+
end
|
256
|
+
true
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
73
261
|
# Fetch all of the repositories for the initialized :org_names that
|
74
262
|
# match the initialized :repository_pattern
|
75
263
|
#
|
76
|
-
# @yield
|
77
|
-
#
|
78
|
-
# @
|
264
|
+
# @yield [Oktokit::Repository] each repository will be yielded
|
265
|
+
# @yieldparam [Oktokit::Repository]
|
266
|
+
# @see https://developer.github.com/v3/repos/#list-organization-repositories
|
267
|
+
# for the response document
|
79
268
|
# @return [True]
|
80
|
-
def
|
269
|
+
def each_github_repository(&block)
|
81
270
|
# Collect all repositories
|
82
271
|
repos = []
|
83
272
|
org_names.each do |org_name|
|
@@ -91,11 +280,9 @@ module Huborg
|
|
91
280
|
return true
|
92
281
|
end
|
93
282
|
|
94
|
-
private
|
95
|
-
|
96
283
|
# @note Due to an implementation detail in octokit.rb, refs sometimes
|
97
|
-
#
|
98
|
-
#
|
284
|
+
# need to be "heads/master" and "refs/heads/master" as detailed
|
285
|
+
# below
|
99
286
|
# @param repo [#full_name, #archived] Likely the result of Octokit::Client#org
|
100
287
|
# @param template [String] name of the template to push out to all
|
101
288
|
# repositories
|
@@ -148,13 +335,50 @@ module Huborg
|
|
148
335
|
end
|
149
336
|
end
|
150
337
|
|
338
|
+
def clone_and_rebase_one!(repo:, directory:, skip_dirty: true, force: false, shallow: false)
|
339
|
+
repo_path = shallow ? File.join(directory, repo.name) : File.join(directory, repo.full_name)
|
340
|
+
if File.directory?(repo_path)
|
341
|
+
# We already have a directory (hopefully its a git repository)
|
342
|
+
git = Git.open(repo_path)
|
343
|
+
if force
|
344
|
+
logger.info("Forcing and reseting to a clean #{repo_path}")
|
345
|
+
# force clean and remove whole directories that are untracked
|
346
|
+
git.clean(force: true, d: true)
|
347
|
+
# reset any changed files to original state
|
348
|
+
git.reset(hard: true)
|
349
|
+
elsif git.status.changed.any? || git.status.added.any? || git.status.deleted.any?
|
350
|
+
# The repository is dirty but should we skip dirty
|
351
|
+
if skip_dirty
|
352
|
+
logger.info("Skipping #{repo.full_name} as it has a dirty git index")
|
353
|
+
return
|
354
|
+
else
|
355
|
+
logger.info("Staching changes on #{repo_path}")
|
356
|
+
# We'll offer a kindness and stash everything
|
357
|
+
git.add('.', all: true)
|
358
|
+
git.branch.stashes.save("Stashing via #{self.class}#clone_and_rebase!")
|
359
|
+
end
|
360
|
+
end
|
361
|
+
git.branch("master").checkout
|
362
|
+
logger.info("Pulling down master branch from origin for #{repo_path}")
|
363
|
+
git.pull("origin", "master")
|
364
|
+
else
|
365
|
+
parent_directory = File.dirname(repo_path)
|
366
|
+
logger.info("Creating #{parent_directory}")
|
367
|
+
FileUtils.mkdir_p(parent_directory)
|
368
|
+
# We don't have a repository in the given path, so make one
|
369
|
+
logger.info("Cloning #{repo.name} into #{parent_directory}")
|
370
|
+
Git.clone(repo.clone_url, repo.name, path: parent_directory)
|
371
|
+
logger.info("Finished cloning #{repo.name} into #{parent_directory}")
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
151
375
|
# Responsible for fetching an array of the given :rel
|
152
376
|
#
|
153
377
|
# @param rel [Symbol] The name of the related object(s) for the
|
154
378
|
# given org
|
155
379
|
# @param org [Object] An Organization object (provided by Oktokit
|
156
380
|
# object) from which this method fetchs the related :rel
|
157
|
-
|
381
|
+
#
|
158
382
|
# @return [Array<Object>]
|
159
383
|
def fetch_rel_for(rel:, org:)
|
160
384
|
# Build a list of repositories, note per Github's API, these are
|
data/lib/huborg/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: huborg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Friesen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-02-
|
11
|
+
date: 2020-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: octokit
|
@@ -24,6 +24,48 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: git
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: github_changelog_generator
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
27
69
|
description: Make changes to Organization Repositories en-masse.
|
28
70
|
email:
|
29
71
|
- jeremy.n.friesen@gmail.com
|
@@ -32,6 +74,7 @@ extensions: []
|
|
32
74
|
extra_rdoc_files: []
|
33
75
|
files:
|
34
76
|
- ".gitignore"
|
77
|
+
- CHANGELOG.md
|
35
78
|
- CODE_OF_CONDUCT.md
|
36
79
|
- CONTRIBUTING.md
|
37
80
|
- Gemfile
|