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 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