huborg 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7aeb15386624a5bf1430e4fd989064ce84a04f4b88d760cc274db0defa21312d
4
- data.tar.gz: 6d7b42c930fbbe776b97b022473133ea65e6faa89422532d7c5bf9559448c0fe
3
+ metadata.gz: bca5b4b64bf1da62ccbb67ce27f6cf459ca1ec65c3989ed1d8fb095cdf8bb956
4
+ data.tar.gz: 2c44b381a4cdd5cabcae0afa7f35e83dbbd643b5d5f6df2025fcdc5687033736
5
5
  SHA512:
6
- metadata.gz: dac200b5c0d7cc50d6ec0f7d1db4b4ba43bc5f5e3330520e2f7f9f7b536742e43ae56d9ed73e3f513488fc81e77b6c530d0658cacc440b008d8cfe2cfeb05dee
7
- data.tar.gz: 13c4e14c1d5c761e366c5c19715019aecbf8d8f643efde3bae158fddaa5280893e93647a5f6287ed1b9e1d1dbacca4ca887661b70056414b88e7f7b36c0d7d35
6
+ metadata.gz: e6c8b6fe6e75a86ee922a0e52dc2515aae8a47f01394b04c2c8c17ddf785c04d9f7bf547bf227d8736ae638e2ca3b25d8b53ac9b856a3123e34ce1c6c068508b
7
+ data.tar.gz: 67bdc6e7138eb137393d34c2ffd9275fe6db99979c93bac6bfb17feb0e47c15f6f1b570f36db7e605bd0e9b40bb6b8482720bde62c5551e41a09ca87626d820b
@@ -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
- ## Further Refinements
62
+ ```ruby
63
+ require 'huborg'
57
64
 
58
- This example demonstrates the full parameter options:
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
- require 'logger'
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
- client = Huborg::Client.new(
65
- org_names: ["samvera", "samvera-labs"]),
66
- logger: Logger.new(STDOUT),
67
- github_access_token: "my-super-secret-token",
68
- repository_pattern: %r{hyrax}i, # Limit to repositories with full name "hyrax"
69
- )
70
- client.push_template!(
71
- template: "/path/to/file/on/your/system",
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
- ## Todo
100
-
101
- - [ ] Add method to clone repositories
102
- - [ ] Add method to pull changes from upstream repositories
103
- - [ ] Add method to run stats against local repositories
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
@@ -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
@@ -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
- # Match all repositories
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>] used as the default list of Github organizations
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
- # @see https://github.com/octokit/octokit.rb#oauth-access-tokens
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
- $stderr.puts "You need to provide an OAuth Access Token.\nSee: https://github.com/octokit/octokit.rb#oauth-access-tokens"
41
- raise e
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
- each_repository do |repo|
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 private
71
- # @note API Private as this is not yet used.
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 if given, yield a Oktokit::Repository object for each
77
- # repository
78
- # @yieldparam A raw Oktokit::Repository object
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 each_repository(&block)
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
- # need to be "heads/master" and "refs/heads/master" as detailed
98
- # below
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
@@ -1,3 +1,5 @@
1
1
  module Huborg
2
- VERSION = "0.1.0"
2
+ # The current version of Huborg, as per Semantic Versioning
3
+ # @see https://semver.org
4
+ VERSION = "0.2.0"
3
5
  end
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.1.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-26 00:00:00.000000000 Z
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