omnirepo 0.5.0.0.pre

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0de193035ffbc63293ac558163fa9bc6519e0597
4
+ data.tar.gz: 5597b8bfa5d0ac31564a58fc2444884ed1c24ff3
5
+ SHA512:
6
+ metadata.gz: 98f40124f15c5be9e75017e11446788d1cc1882f396b228218065b1412e3ee57defdac1efb33f5ddd476707729ed923852d6a9fc11a90e4d5ab6d9188b050912
7
+ data.tar.gz: ebb92830746b0dbd4dceae34bcd51ef6a57abfcc637be28b349e2371a20f2eb7c66ecfdef4f1a84d3c9de731ad9dd3dafc61d016fa678be5d1ca8e2e634e28fc
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ *pkg
data/CHANGES.md ADDED
@@ -0,0 +1,7 @@
1
+ HEAD
2
+ -----
3
+ * Initial release, put together for [celluloid/core](https://github.com/celluloid/core).
4
+ * Added `.omnirepo` configuration file support. YAML file with these options:
5
+ * [required, if using .omnirepo] `repositories` array, of individual repositories.
6
+ * [optional] `submodules` if `remove` will remove submodules from repositories.
7
+ * [optional] `merging` can be `skip` and it will just prepare but not merge.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011-2014 Donovan Keme
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # omnirepo
2
+
3
+ Create a new omnibus repository out of a collection of existing GitHub repositories.
4
+
5
+
6
+ ## Installation:
7
+
8
+ ```
9
+ gem install omnirepo
10
+ ```
11
+
12
+ ## What exactly does it do with repositories?!
13
+
14
+ 0. Checks for the destination omnibus directory.
15
+ 0. Clones source repositories into a temporary directory.
16
+ 0. It uses mirroring, to pull in all branches and tags.
17
+ 0. It immediately removes any remote `origin` detected.
18
+ 0. Preserves the git history for each repository.
19
+ 0. (optional) If desired, it removes any submodules present first.
20
+ 0. It also renames all branches and tags to be `<repository>/<name>`.
21
+ 0. Does garbage collection on the repository being migrated.
22
+ 0. Imports each source repository as a sub-directory of the omnibus repository.
23
+ 0. Does garbage collection on the new omnibus repository.
24
+ 0. **Does not push the new omnibus repository live.**
25
+
26
+ ### System Requirements:
27
+
28
+ * Unix-based operating system, with `ruby` and `git` preinstalled.
29
+ * Prefers `tmpfs` partition located at `/dev/shm` for temporary storage.
30
+ * Read access to all remote repositories involed.
31
+
32
+ ### Usage:
33
+
34
+ ```sh
35
+ omnirepo <username/organization> <destination-omnibus> <source-repository> [...]
36
+ ```
37
+
38
+ ### Example:
39
+
40
+ If these self-contained repositories on GitHub need to be merged together into one repository:
41
+
42
+ * `org9/repoA`
43
+ * `org9/repoB`
44
+ * `org9/repoC`
45
+
46
+ And the directory housing the future location of the omnibus repository on GitHub is:
47
+
48
+ * `org9/repo0`
49
+
50
+ This would be your command:
51
+
52
+ ```sh
53
+ omnirepo org9 repo0 repoA repoB repoC
54
+ ```
55
+ # Use with a configuration file:
56
+
57
+ For the scenario above, you can create a configuration file as follows:
58
+
59
+ ```yml
60
+ repositories:
61
+ - repoA
62
+ - repoB
63
+ - repoC
64
+ ```
65
+
66
+ Save that file as `repo0/.omnirepo`
67
+
68
+ Then you can run this command:
69
+
70
+ ```sh
71
+ omnirepo org9 repo0
72
+ ```
73
+
74
+
75
+ ## Future Features
76
+
77
+ - [x] Read repositories from a configuration file.
78
+ - [ ] Bring together source repositories from multiple possible organizations.
79
+ - [ ] ...missing something? [Request it](https://github.com/digitalextremist/omnirepo/issues/new)...
80
+
81
+ ## Contributing
82
+
83
+ * Fork this repository on GitHub.
84
+ * Make your changes and send a pull request.
85
+
86
+ ## License
87
+
88
+ Copyright (c) 2015 Donovan Keme.
89
+
90
+ Distributed under the MIT License. See [LICENSE.txt](https://github.com/digitalextremist/omnirepo/LICENSE.txt) for further details.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/omnirepo ADDED
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ start = Time.now #de Not monotonic, but bah.
4
+ puts "Omnibus repository unification tool..."
5
+
6
+ def omnirepo?
7
+ if File.exists?(File.join("#{Dir.pwd}/#{ARGV[1]}", ".omnirepo"))
8
+ require 'psych'
9
+ return true
10
+ end
11
+ false
12
+ end
13
+
14
+ def urandom_id
15
+ `cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1`
16
+ end
17
+
18
+ def success?(results)
19
+ return false unless results.is_a?(Array)
20
+ results.uniq!
21
+ return false unless results.one?
22
+ results.first == true
23
+ end
24
+
25
+ begin
26
+
27
+ #de TODO: Validate owner.
28
+ #de TODO: Allow use of literal paths, vs. relative.
29
+ #de TODO: Check permissions/writeability, etc.
30
+
31
+ if ARGV.length == 2 && omnirepo?
32
+ OWNER = ARGV.shift
33
+ OMNIBUS = "#{Dir.pwd}/#{ARGV.shift}"
34
+ CONFIG = Psych.load_file(File.join(OMNIBUS, ".omnirepo"))
35
+ unless CONFIG.is_a?(Hash) && CONFIG["repositories"].is_a?(Array)
36
+ puts "Onmirepo configuration file invalid."
37
+ exit!
38
+ end
39
+ REPOSITORIES = CONFIG['repositories'].uniq
40
+ GIT = CONFIG["git"] if CONFIG["git"].is_a?(String)
41
+ elsif ARGV.length <= 3
42
+ puts "Usage: omnirepo <owner org/user> <destination-omnibus-dir> <source-repo1> <source-repo2> [...]"
43
+ puts "No owner organization/username supplied." unless ARGV.any?
44
+ puts "No destination omnibus directory supplied." unless ARGV.length >= 2
45
+ puts "No source repositories supplied." unless ARGV.length == 3
46
+ puts "Only one source repository supplied." if ARGV.length == 3
47
+ exit!
48
+ else
49
+ OWNER = ARGV.shift
50
+ OMNIBUS = "#{Dir.pwd}/#{ARGV.shift}"
51
+ REPOSITORIES = ARGV.uniq
52
+ CONFIG = {}
53
+ end
54
+
55
+ GIT = "git@github.com".freeze unless defined? GIT
56
+
57
+ tmp='/dev/shm'
58
+ tmp='/tmp' unless Dir.exists?(tmp)
59
+ TMP="#{tmp}/omnirepo.#{urandom_id}".chomp.strip
60
+
61
+ unless Dir.mkdir(TMP)
62
+ STDERR.puts "ERROR: Unable to make the temporary working directory: #{TMP}"
63
+ exit!
64
+ end
65
+
66
+ puts LINE = "-----------------------------------------------------------------------------------".freeze
67
+
68
+ unless Dir.exists?(OMNIBUS)
69
+ puts "Destination omnibus directory does not exist: #{OMNIBUS}"
70
+ exit!
71
+ end
72
+
73
+ puts "Owner organization/user: #{OWNER}"
74
+ puts "Destination omnibus: #{OMNIBUS}"
75
+ puts "Working directory: #{TMP}"
76
+ puts LINE
77
+
78
+ Dir.chdir(OMNIBUS)
79
+
80
+ if `git show 2> /dev/null | grep "commit" | wc -l`.to_i != 1
81
+ puts LINE
82
+ STDERR.puts "ERROR: Destination omnibus directory is not a git repository, or not ready."
83
+ exit!
84
+ end
85
+
86
+ #de TODO: Further validate omnibus repository.
87
+
88
+ failures = []
89
+
90
+ REPOSITORIES.each { |repo|
91
+
92
+ puts "\n\n\n#{LINE}"
93
+ puts "Adding the #{repo} repository..."
94
+ puts "Source: #{GIT}:#{OWNER}/#{repo}.git"
95
+ puts LINE
96
+
97
+ unless Dir.chdir(TMP)
98
+ STDERR.puts "ERROR: Failure to change to the temporary working directory."
99
+ exit!
100
+ end
101
+
102
+ unless system("git clone --mirror '#{GIT}:#{OWNER}/#{repo}.git' #{repo}")
103
+ puts "FERROR: ailed to clone: #{repo}"
104
+ exit!
105
+ end
106
+
107
+ unless Dir.chdir(repo_bare = File.join(TMP, repo))
108
+ STDERR.puts "ERROR: Failed to mirror the #{repo} repository."
109
+ exit!
110
+ end
111
+
112
+ if `git remote | grep origin | wc -l`.to_i > 0
113
+ `git remote rm origin > /dev/null 2> /dev/null`
114
+ end
115
+
116
+ branches = []
117
+ `git branch | grep -v master | grep -v "refs/"`.split("\n").each { |branch|
118
+ begin
119
+ result = nil
120
+ branches << (branch = branch.strip.chomp)
121
+ result = system("git branch -m '#{branch}' '#{repo}/#{branch}")
122
+ rescue => ex
123
+ STDERR.puts "WARN: Could not migrate the #{branch} branch.\nReason: #{ex}"
124
+ ensure
125
+ failures << "#{repo} / branch: #{branch}" unless result == true
126
+ end
127
+ }
128
+
129
+ repo_clone = "../#{repo}.clone"
130
+ unless cloned = system("git clone . #{repo_clone} > /dev/null")
131
+ STDERR.puts "ERROR: Failed to make local clone of #{repo}."
132
+ failures << "#{repo} / locally clone"
133
+ next
134
+ end
135
+
136
+ Dir.chdir(repo_clone)
137
+
138
+ begin
139
+ if CONFIG['submodules'] == 'remove'
140
+ if File.exists?(module_config = File.join(repo_bare, repo_clone, ".gitmodules"))
141
+ submodules = File.read(module_config)
142
+ .split("\n")
143
+ .map { |line|
144
+ if line.include?("path =")
145
+ submodule = line.sub("path =", "").chomp.strip
146
+ unless submodule.empty?
147
+ `git rm -rf ./#{submodule}`
148
+ submodule
149
+ end
150
+ else
151
+ nil
152
+ end
153
+ }.compact
154
+ result = []
155
+ result << system("git rm -rf .gitmodules")
156
+ result << system("git commit -am '[omnirepo] removing submodules: #{submodules.join(', ')}'")
157
+ result << system("git push")
158
+ failures << "#{repo} / submodules: remove .gitmodules, then commit/push" unless success?(result)
159
+ puts "#{LINE}\nRemoved submodules first: #{submodules.join(', ')}"
160
+ end
161
+ end
162
+ rescue => ex
163
+ STDERR.puts "WARN: Could not remove associated submodules.\nReason: #{ex}"
164
+ failures << "#{repo} / remove submodules"
165
+ ensure
166
+ Dir.chdir(repo_bare)
167
+ end
168
+
169
+ puts "#{LINE}\nPreserving change history and files, but making the repository a sub-directory..."
170
+ print "... please wait: "
171
+
172
+ unless system("git filter-branch --tree-filter " +
173
+ "'mkdir #{repo}; " +
174
+ "find -maxdepth 1 " +
175
+ "-not -name . " +
176
+ "-not -name .git " +
177
+ "-not -name #{repo} " +
178
+ "| xargs -I{} mv {} #{repo}' " +
179
+ "-d #{TMP}/#{urandom_id} -- --all")
180
+ failures << "#{repo} / repository tree-filter"
181
+ end
182
+
183
+ unless system("git commit -am '[omnirepo] merging the rearranged file structure'")
184
+ failures << "#{repo} / merge rearranged file structure"
185
+ STDERR.puts "ERROR: Could not merge-in rearranged file structure."
186
+ next
187
+ end
188
+
189
+ tags = []
190
+ `git tag`.split("\n").each { |tag|
191
+ begin
192
+ result = []
193
+ tags << (tag = tag.chomp.strip)
194
+ result << system("git tag '#{repo}/#{tag}' '#{tag}' > /dev/null")
195
+ result << system("git tag -d '#{tag}' > /dev/null")
196
+ rescue => ex
197
+ STDERR.puts "WARN: Could not migrate the #{tag} tag.\nReason: #{ex}"
198
+ result << false
199
+ ensure
200
+ failures << "#{repo} / tag: #{tag}" unless success?(result)
201
+ end
202
+ }
203
+
204
+ puts "#{LINE}\nGarbage collection of source repository...\n#{LINE}"
205
+ failures << "#{repo} / garbage collection" unless `git gc --aggressive`
206
+
207
+ if CONFIG['merging'] == 'skip'
208
+ puts "Skipping actual merge into omnibus..."
209
+ next
210
+ end
211
+
212
+ puts "#{LINE}\nMerging the prepared repository into the omnibus...\n#{LINE}"
213
+
214
+ result = []
215
+ Dir.chdir(OMNIBUS)
216
+ result << system("git remote add #{repo} #{repo_bare}")
217
+ result << system("git fetch #{repo}")
218
+ result << system("git merge --no-ff #{repo}/master --commit '[omnirepo] merging in the #{repo} repository.'")
219
+ result << system("git remote rm #{repo}")
220
+
221
+ unless success?(result)
222
+ failures << "#{repo} / merge into omnibus"
223
+ next
224
+ end
225
+
226
+ if branches.any?
227
+ puts "#{LINE}\nThese branches were migrated:\n#{LINE}"
228
+ branches.each { |branch| puts "#{branch.rjust(35)} is now #{repo}/#{branch}\n" }
229
+ end
230
+
231
+ if tags.any?
232
+ puts "#{LINE}\nThese tags were migrated:\n#{LINE}"
233
+ tags.each { |tag| puts "#{tag.rjust(35)} is now #{repo}/#{tag}\n" }
234
+ end
235
+
236
+ }
237
+
238
+ puts "\n\n\n"
239
+ #de TODO: Delete working directory, or at least give config or command line option to do so.
240
+ puts "#{LINE}\nThere were the following failures:\n#{LINE}\n#{failures.join("\n")}" if failures.any?
241
+ puts "\n\n\n#{LINE}"
242
+ puts "Omnibus unification of repositories finished in #{"%0.4f" % (Time.now.to_f - start.to_f)} seconds."
243
+ puts "#{LINE}\nRemove your temporary working directory when you are ready:"
244
+ puts " #{TMP}"
245
+ puts "#{LINE}\nWARNING: Be sure to check over the new omnibus repository before pushing it!"
246
+ puts " Each repository has been committed into it."
247
+ puts LINE
248
+
249
+ rescue => ex
250
+ puts "Exiting prematurely. Re-run to make an intact omnibus repository."
251
+ STDERR.puts "Error: #{ex}" unless ex.is_a?(Interrupt)
252
+ exit(false)
253
+ end
data/omnirepo.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Donovan Keme"]
5
+ gem.email = ["code@extremist.digital"]
6
+ gem.description = "Create a new omnibus repository out of collections of existing GitHub repositories."
7
+ gem.summary = "Unify GitHub repositories as one repository containing them all as sub-directories."
8
+ gem.homepage = "https://github.com/digitalextremist/omnirepo"
9
+
10
+ gem.files = `git ls-files`.split($\)
11
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "omnirepo"
14
+ gem.version = '0.5.0.0.pre'
15
+ gem.licenses = ['MIT']
16
+ gem.required_ruby_version = ">= 1.9.2"
17
+ gem.required_rubygems_version = ">= 1.3.6"
18
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omnirepo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Donovan Keme
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Create a new omnibus repository out of collections of existing GitHub
14
+ repositories.
15
+ email:
16
+ - code@extremist.digital
17
+ executables:
18
+ - omnirepo
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - ".gitignore"
23
+ - CHANGES.md
24
+ - Gemfile
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - bin/omnirepo
29
+ - omnirepo.gemspec
30
+ homepage: https://github.com/digitalextremist/omnirepo
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.9.2
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.6
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 2.4.8
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Unify GitHub repositories as one repository containing them all as sub-directories.
54
+ test_files: []