git_compound 0.0.9

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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +8 -0
  5. data/Compoundfile +4 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +21 -0
  8. data/README.md +291 -0
  9. data/Rakefile +13 -0
  10. data/bin/console +7 -0
  11. data/bin/setup +5 -0
  12. data/exe/gitcompound +5 -0
  13. data/git_compound.gemspec +33 -0
  14. data/lib/git_compound/builder.rb +93 -0
  15. data/lib/git_compound/command/options.rb +55 -0
  16. data/lib/git_compound/command.rb +113 -0
  17. data/lib/git_compound/component/destination.rb +45 -0
  18. data/lib/git_compound/component/source.rb +53 -0
  19. data/lib/git_compound/component/version/branch.rb +30 -0
  20. data/lib/git_compound/component/version/gem_version.rb +45 -0
  21. data/lib/git_compound/component/version/sha.rb +33 -0
  22. data/lib/git_compound/component/version/tag.rb +30 -0
  23. data/lib/git_compound/component/version/version_strategy.rb +45 -0
  24. data/lib/git_compound/component.rb +78 -0
  25. data/lib/git_compound/dsl/component_dsl.rb +60 -0
  26. data/lib/git_compound/dsl/manifest_dsl.rb +27 -0
  27. data/lib/git_compound/exceptions.rb +16 -0
  28. data/lib/git_compound/lock.rb +64 -0
  29. data/lib/git_compound/logger/colors.rb +123 -0
  30. data/lib/git_compound/logger/core_ext/string.rb +5 -0
  31. data/lib/git_compound/logger.rb +43 -0
  32. data/lib/git_compound/manifest.rb +39 -0
  33. data/lib/git_compound/node.rb +17 -0
  34. data/lib/git_compound/repository/git_command.rb +33 -0
  35. data/lib/git_compound/repository/git_repository.rb +79 -0
  36. data/lib/git_compound/repository/git_version.rb +43 -0
  37. data/lib/git_compound/repository/remote_file/git_archive_strategy.rb +30 -0
  38. data/lib/git_compound/repository/remote_file/github_strategy.rb +54 -0
  39. data/lib/git_compound/repository/remote_file/remote_file_strategy.rb +27 -0
  40. data/lib/git_compound/repository/remote_file.rb +34 -0
  41. data/lib/git_compound/repository/repository_local.rb +81 -0
  42. data/lib/git_compound/repository/repository_remote.rb +12 -0
  43. data/lib/git_compound/repository.rb +19 -0
  44. data/lib/git_compound/task/task.rb +28 -0
  45. data/lib/git_compound/task/task_all.rb +27 -0
  46. data/lib/git_compound/task/task_each.rb +18 -0
  47. data/lib/git_compound/task/task_single.rb +21 -0
  48. data/lib/git_compound/task.rb +22 -0
  49. data/lib/git_compound/version.rb +5 -0
  50. data/lib/git_compound/worker/circular_dependency_checker.rb +31 -0
  51. data/lib/git_compound/worker/component_builder.rb +30 -0
  52. data/lib/git_compound/worker/component_replacer.rb +25 -0
  53. data/lib/git_compound/worker/component_update_dispatcher.rb +64 -0
  54. data/lib/git_compound/worker/component_updater.rb +24 -0
  55. data/lib/git_compound/worker/components_collector.rb +20 -0
  56. data/lib/git_compound/worker/conflicting_dependency_checker.rb +31 -0
  57. data/lib/git_compound/worker/local_changes_guard.rb +47 -0
  58. data/lib/git_compound/worker/name_constraint_checker.rb +20 -0
  59. data/lib/git_compound/worker/pretty_print.rb +18 -0
  60. data/lib/git_compound/worker/task_runner.rb +12 -0
  61. data/lib/git_compound/worker/worker.rb +20 -0
  62. data/lib/git_compound.rb +112 -0
  63. metadata +193 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 612c6d116fee9bcaaee021484a7679932bb3e675
4
+ data.tar.gz: cdd8dc429c4481348cbbea8a98f9faeb103c463c
5
+ SHA512:
6
+ metadata.gz: e7dbe4c61f12ccd94571ef2e44c55fce8b35f7c32ac0e9a05bc586d68708728a43157f7441c1d47bfb713e1c19cac367f1229a0177454495ad1a8619749d5fa5
7
+ data.tar.gz: 8ba2126a8ea301f628bab615f0af411b4b13a1cecc1951cf321eb906d95e8a1ce556ba7175a19a674470641be8635e6fc35acb90b24b28ab92f43907963117d6
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /todo
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require 'spec_helper'
2
+ --color
3
+ --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ Metrics/LineLength:
2
+ Max: 90
3
+ Metrics/MethodLength:
4
+ Max: 18
5
+ Style/SignalException:
6
+ Enabled: false
7
+ Style/ModuleFunction:
8
+ Enabled: false
data/Compoundfile ADDED
@@ -0,0 +1,4 @@
1
+ # GitCompound manifest
2
+ #
3
+
4
+ name :git_compound
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Grzegorz Bizon <grzegorz.bizon@ntsn.pl>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,291 @@
1
+ # Git Compound
2
+
3
+ Compose your project using git repositories and ruby tasks
4
+
5
+ ## Status
6
+
7
+ This project is in alpha phase
8
+
9
+ ## Overview
10
+
11
+ Create `Compoundfile` or `.gitcompound` manifest:
12
+
13
+ ```ruby
14
+ name :base_component
15
+
16
+ component :component_1 do
17
+ version '~>1.1'
18
+ source 'git@github.com:/user/repository'
19
+ destination 'src/component_1'
20
+ end
21
+
22
+ component :component_2 do
23
+ version '>=2.0'
24
+ source 'git@github.com:/user/repository_2'
25
+ destination 'src/component_2'
26
+ end
27
+
28
+ component :component_3 do
29
+ branch 'feature/new-feature'
30
+ source '/my/component_3/repository'
31
+ destination 'src/component_3'
32
+ end
33
+
34
+ task 'add components to gitignore', :each do |component_dir|
35
+ File.open('.gitignore', 'a') { |f| f.write "#{component_dir}\n" }
36
+ end
37
+ ```
38
+
39
+ GitCompound will also process similar manifests found in required components in hierarchical way.
40
+
41
+ Then run `gitcompound build`.
42
+
43
+
44
+ ## Core features
45
+
46
+ `GitCompound` is more a distributed packaging system than alternative to Git submodules.
47
+
48
+ `GitCompound` makes sure your project composition is the same on all machines,
49
+ but you can have different composition depending on manifest you are using.
50
+
51
+ It has been created with Docker compatiblity in mind.
52
+
53
+ Core features:
54
+
55
+ * `GitCompound` introduces Domain Specific Language desgined for writing manifests.
56
+
57
+ * It is possible to create multiple manifest files (`Compoundfile`, `.gitcompound` or something else)
58
+ and build them when necessary.
59
+
60
+ * Building manifest create lockfile (locking components on specific commit SHA). When lockfile is present if will
61
+ be used each time `build` command is invoked.
62
+
63
+ * Manifests can declare dependencies on different versions of components using different version strategies
64
+ (Rubygems-like version, tag, branch or explicit SHA).
65
+
66
+ * Manifests will be processed in hierarchical way. Manifest that `gitcompound` command is run against
67
+ is root manifest. `GitCompound` processes all subsequent manifests found in dependencies using depth-first
68
+ search of dependency graph.
69
+
70
+ * Root manifest and manifest of each subsequent component can declare Ruby tasks that will be executed
71
+ when component is built or updated.
72
+
73
+ * It is possible to install dependencies in root directory (`Dir.pwd` where `gitcompound` command is invoked).
74
+ Each subsequent component can install their dependencies into root directory (see overview for **destination** DSL method).
75
+
76
+ * Build process creates lockfile in `.gitcompound.lock`. It locks components on specific commit SHAs.
77
+ It is then possible to build components directly, depending on versions (SHAs) from lockfile.
78
+
79
+ ## Commands
80
+
81
+ ### build
82
+
83
+ `gitcompound build [manifest]` -- builds project from manifest or lockfile
84
+
85
+ If manifest is not supplied, it uses `Compoundfile` or `.gitcompound`
86
+
87
+ ### update
88
+
89
+ `gitcompound update [manifest]` -- updates project and lockfile
90
+
91
+ ### check
92
+
93
+ `gitcompound check [manifest]` -- checks for circular dependencies, conflicting dependencies
94
+ and name constraints
95
+
96
+ ### show
97
+
98
+ `gitcompound show [manifest]` -- prints project structure
99
+
100
+ ### help
101
+
102
+ `gitcompound help` -- prints help message
103
+
104
+ ## Global options
105
+
106
+ 1. `--verbose` -- turns debug mode on
107
+ 2. `--disable-colors` -- disables ANSI colors in output
108
+
109
+ ## Details
110
+
111
+ 1. Use `name` method to specify **name** of manifest or component that this manifest is included in.
112
+
113
+ 2. Add dependency to required **component** using `component` method.
114
+
115
+ This method takes two parameters -- name of component (as symbol) and implicit or explicit block.
116
+
117
+ Beware that `GitCompound` checks name constraints, so if you rely on component
118
+ with name `:component_1`, and this component has `Compoundfile` inside that declares
119
+ it's name as something else than `:component_1` -- `GitCompound` will raise exception.
120
+
121
+ 3. Components can use following **version strategies**:
122
+
123
+ * `version` -- Rubygems-like **version** strategy
124
+
125
+ This strategy uses Git tags to determine available component versions.
126
+ If tag matches `/^v?#{Gem::Version::VERSION_PATTERN}$/` then it is considered to
127
+ be version tag and therefore it can be used with Rubygems syntax
128
+ (like pessimistic version constraint operator)
129
+
130
+ * `tag` -- use component sources specified by **tag**
131
+
132
+ * `branch` -- use HEAD of given **branch**
133
+
134
+ * `sha` -- use explicitly set commit **SHA**
135
+
136
+ 4. Provide path to **source** repository using `source` method of manifest domain specific language.
137
+
138
+ It will be used as source to clone repository into destination directory.
139
+
140
+ This can take `:shallow` parameter. When `:shallow` is set, shallow clone will be
141
+ performed (`--branch #{ref} --depth 1`):
142
+
143
+ ```ruby
144
+ component :bootstrap do
145
+ version '~>3.3.5'
146
+ source 'git@github.com:twbs/bootstrap', :shallow
147
+ destination '/vendor/bootstrap
148
+ end
149
+ ```
150
+
151
+ However using it is not recommended at all.
152
+
153
+ It can be helpful when required component is big and you need to build your project
154
+ only once, but it will cause issues with update. You will not be able to update it properly.
155
+ Use it with caution !
156
+
157
+
158
+ 5. Use `destination` method to specify **destination** path where component will be cloned into.
159
+
160
+ This should be relative path in most cases.
161
+
162
+ Relative path is always relative to parent component directory. So if you define
163
+ `component_1` with destination path `component_1/`, and this component will
164
+ depend on `component_2` with destination path set to `src/component_2`, you will
165
+ end with `./component_1/src/component_2` directory after building components.
166
+
167
+ When path is absolute -- it will be always relative to `Dir.pwd` where
168
+ you invoke `gitcompound *` commands. So if you have `component_1` with destination path
169
+ set to `/component_1` and `component_1` which has manifest that depends on `component_2` with
170
+ destination path set to `/component_2`, then `GitCompound` will create two separate
171
+ directories in `Dir.pwd`: `./component_1` and `./component_2`.
172
+
173
+ Use absolute paths with caution as it affects how parent projects will be built.
174
+ Ceraintly components that are libraries should not use it at all. If component is project --
175
+ it can take benefit from using absolute paths in component destination.
176
+
177
+ 6. Running tasks
178
+
179
+ It is possible to use `task` method to define new **task**. `task` method takes 2 or 3 arguments.
180
+ First one is task name (symbol). Second one is optional task type that define how, and
181
+ in which context, task will be executed. Third one is block that will be excuted.
182
+
183
+ Currently there are three task types:
184
+
185
+ * `:manifest` type (this is default) -- run task in context of current manifest
186
+
187
+ Task will be executed only once, in context of current manifest this task is
188
+ defined in. Example:
189
+
190
+ ```ruby
191
+ task :print_manifest_name do |dir, manifest|
192
+ puts "Current manifest name is #{manifest.name} and dir is: #{dir}"
193
+ end
194
+ ```
195
+
196
+ * `:each` type -- run task for each component defined in current manifest
197
+
198
+ This executes task for all components that are explicitly defined in current manifest.
199
+ Example:
200
+
201
+ ```ruby
202
+ task :print_component_name, :each do |dir, component|
203
+ puts "Current component name: #{component.name}"
204
+ puts "Current component source: #{component.origin}
205
+ puts "Current component destination: #{component.destination_path}
206
+
207
+ puts "Component directory: #{dir}"
208
+ end
209
+ ```
210
+
211
+ Note that `dir` here is the same as `component.destination_path`.
212
+
213
+ * `:all` type -- run task for all child components of this manifest
214
+
215
+ Task for `:all` components will be executed in context of every component
216
+ this manifest is parent of.
217
+
218
+ It will be executed for all components defined in manifest itself and for all
219
+ components that are included in child manifests (obviously child manifests are
220
+ manifests included in components defined in parent manifest).
221
+
222
+ Example:
223
+
224
+ ```ruby
225
+ task :print_all_component_names, :all do |dir, component|
226
+ puts "Component #{component.name} destination dir: #{dir}"
227
+ end
228
+ ```
229
+
230
+ By default `GitCompound` executes only tasks defined in root manifest.
231
+
232
+ This is default behaviour dictated by security reasons. Since all tasks (also those defined
233
+ in child component) are visited in reverse order it is possible to execute them too.
234
+
235
+ If you know what you are doing and it is your conscious decision to run all tasks in project
236
+ pass `--allow-nested-subtasks` options to `build` or `update` command.
237
+
238
+ It can be beneficial approach, but it has to be done with caution.
239
+
240
+ ## Other concepts
241
+
242
+ 1. Accessing manifests
243
+
244
+ `GitCompound` tries to be as fast as possible. To achieve this it tries to access
245
+ manifest of required component without cloning it first. Is uses different strategies to
246
+ achieve this. Currently only two strategies are available:
247
+
248
+ * `GitArchiveStrategy` -- it uses `git archive` command to access single file in remote
249
+ repository,
250
+ * `GithubStrategy` -- it uses http request to `raw.githubusercontent.com` to access
251
+ manifest file at GitHub.
252
+
253
+ It is possible to create new strategies by implementing new strategy base on
254
+ `GitCompound::Repository::RemoteFile::RemoteFileStrategy` abstraction.
255
+
256
+ 2. Using lockfile (`.gitcompound.lock`)
257
+
258
+ If lockfile is present, `gitcompound build` command will ignore manifest, and build
259
+ components, with versions locked in commit SHA. You can always be sure that your
260
+ environment will be unchange when building from lockfile.
261
+
262
+ 3. Building manifest
263
+
264
+ Behavior of `build` command depends on whether lockfile exists or not.
265
+
266
+ * When lockfile does not exist -- components will be built using versions stored in manifest.
267
+
268
+ Then lockfile `.gitcompound.lock` will be created, locking each component on specific commit SHA
269
+ that component has been checkout on during build process.
270
+
271
+ * If lockfile is present it will be used as source for update process (see "Updating manifest" below)
272
+
273
+ 4. Updating manifest
274
+
275
+ When using `gitcompound update` command each subsequent component will be either
276
+ built, updated or replaced:
277
+
278
+ * built (cloned) -- if destination directory does not exist
279
+ * updated (fetch & checkout) -- if component exists, matches origin and existing SHA != new SHA
280
+ * replaced (remove & clone) -- if component exists but doesn't match origin remote
281
+
282
+ When invoking update process via `gitcompound update` command, versions specified in manifest file
283
+ will be use.
284
+
285
+ ## Roadmap
286
+
287
+ Take a look at issues at GitHub.
288
+
289
+ ## License
290
+
291
+ This is free software licensed under MIT license
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+ RuboCop::RakeTask.new
7
+
8
+ task :test do
9
+ Rake::Task['rubocop'].invoke
10
+ Rake::Task['spec'].invoke
11
+ end
12
+
13
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'git_compound'
5
+ require 'pry'
6
+
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
data/exe/gitcompound ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'git_compound'
5
+ GitCompound.run(*GitCompound::Command::Options.new(ARGV).parse)
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git_compound/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'git_compound'
8
+ spec.version = GitCompound::VERSION
9
+ spec.authors = ['Grzegorz Bizon']
10
+ spec.email = ['grzegorz.bizon@ntsn.pl']
11
+ spec.summary = 'Compose you project using git repositories and ruby tasks'
12
+ spec.homepage = 'https://github.com/grzesiek/git_compound'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^spec/) }
16
+ spec.bindir = 'exe'
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ['lib']
19
+
20
+ # rubocop:disable Style/SingleSpaceBeforeFirstArg
21
+ spec.add_development_dependency 'bundler', '~> 1.8'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rubocop', '~> 0.31.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.2.0'
25
+ spec.add_development_dependency 'pry', '~> 0.10.1'
26
+ spec.add_development_dependency 'simplecov', '~> 0.10.0'
27
+ # rubocop:enable Style/SingleSpaceBeforeFirstArg
28
+
29
+ spec.requirements << 'git scm version > 2'
30
+ spec.requirements << 'gnu tar'
31
+
32
+ spec.required_ruby_version = '>= 2.2'
33
+ end
@@ -0,0 +1,93 @@
1
+ module GitCompound
2
+ # Builder class, responsible for building project
3
+ # from manifest or lockfile
4
+ #
5
+ class Builder
6
+ def initialize(manifest, lock, opts)
7
+ @manifest = manifest
8
+ @lock = lock
9
+ @opts = opts
10
+ end
11
+
12
+ def manifest_build
13
+ Logger.info 'Building components ...'
14
+ @manifest.process(Worker::ComponentBuilder.new(@lock))
15
+ self
16
+ end
17
+
18
+ def manifest_update
19
+ Logger.info 'Updating components ...'
20
+ @lock_new = Lock.new.clean
21
+ @manifest.process(Worker::ComponentUpdateDispatcher.new(@lock_new))
22
+ self
23
+ end
24
+
25
+ def manifest_lock
26
+ lock = @lock_new ? @lock_new : @lock
27
+ lock.lock_manifest(@manifest)
28
+ lock.write
29
+ self
30
+ end
31
+
32
+ def dependencies_check
33
+ Logger.info 'Checking dependencies ...'
34
+
35
+ @manifest.process(
36
+ Worker::CircularDependencyChecker.new,
37
+ Worker::NameConstraintChecker.new,
38
+ Worker::ConflictingDependencyChecker.new)
39
+ self
40
+ end
41
+
42
+ def components_show
43
+ Logger.info 'Processing components list ...'
44
+ @manifest.process(
45
+ Worker::CircularDependencyChecker.new,
46
+ Worker::PrettyPrint.new)
47
+ self
48
+ end
49
+
50
+ def tasks_execute
51
+ Logger.info 'Running tasks ...'
52
+
53
+ if @opts.include?(:allow_nested_subtasks)
54
+ @manifest.process(Worker::TaskRunner.new)
55
+ else
56
+ @manifest.tasks.each_value(&:execute)
57
+ end
58
+ self
59
+ end
60
+
61
+ def locked_manifest_verify
62
+ return self if @manifest.md5sum == @lock.manifest
63
+ raise GitCompoundError,
64
+ 'Manifest md5sum has changed ! Use `update` command.'
65
+ end
66
+
67
+ def locked_components_build
68
+ Logger.info 'Building components from lockfile ...'
69
+ @lock.process(Worker::ComponentUpdateDispatcher.new(@lock))
70
+ self
71
+ end
72
+
73
+ def locked_components_guard
74
+ @lock.process(Worker::LocalChangesGuard.new(@lock))
75
+ self
76
+ end
77
+
78
+ def locked_dormant_components_remove
79
+ raise GitCompoundError, 'No new lockfile !' unless @lock_new
80
+
81
+ dormant_components = @lock.components.reject do |component|
82
+ @lock_new.find(component) ? true : false
83
+ end
84
+
85
+ dormant_components.each do |component|
86
+ Logger.warn "Removing dormant component `#{component.name}` " \
87
+ "from `#{component.destination_path}` !"
88
+
89
+ component.remove!
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,55 @@
1
+ module GitCompound
2
+ module Command
3
+ # Class that parses command arguments
4
+ #
5
+ class Options
6
+ GLOBAL_OPTIONS = [:verbose, :disable_colors]
7
+
8
+ def initialize(args)
9
+ @command, @args = parse_options(args)
10
+
11
+ set_global_options
12
+ end
13
+
14
+ def global_options
15
+ @args & GLOBAL_OPTIONS
16
+ end
17
+
18
+ def command_options
19
+ @args - GLOBAL_OPTIONS
20
+ end
21
+
22
+ def command
23
+ @command || 'help'
24
+ end
25
+
26
+ def parse
27
+ [command, command_options]
28
+ end
29
+
30
+ def self.verbose=(mode)
31
+ GitCompound::Logger.verbose = mode
32
+ end
33
+
34
+ def self.disable_colors=(mode)
35
+ String.disable_colors = mode
36
+ end
37
+
38
+ private
39
+
40
+ def parse_options(args)
41
+ opts_dash = args.select { |opt| opt.start_with?('--') }
42
+ opts_string = args - opts_dash
43
+ command = opts_string.shift
44
+ opts_sym = opts_dash.collect { |opt| opt.sub(/^--/, '').gsub('-', '_').to_sym }
45
+ [command, opts_string + opts_sym]
46
+ end
47
+
48
+ def set_global_options
49
+ global_options.each do |option|
50
+ self.class.public_send("#{option}=", true)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,113 @@
1
+ module GitCompound
2
+ # GitCompount command facade
3
+ #
4
+ module Command
5
+ def build(*args)
6
+ if Lock.exist?
7
+ builder(args)
8
+ .locked_manifest_verify
9
+ .locked_components_build
10
+ .tasks_execute
11
+ else
12
+ builder(args)
13
+ .dependencies_check
14
+ .manifest_build
15
+ .tasks_execute
16
+ .manifest_lock
17
+ end
18
+ end
19
+
20
+ def update(*args)
21
+ raise GitCompoundError,
22
+ "Lockfile `#{Lock::FILENAME}` does not exist ! " \
23
+ 'You should use `build` command.' unless Lock.exist?
24
+
25
+ builder(args)
26
+ .locked_components_guard
27
+ .dependencies_check
28
+ .manifest_update
29
+ .tasks_execute
30
+ .manifest_lock
31
+ .locked_dormant_components_remove
32
+ end
33
+
34
+ def check(*args)
35
+ builder(args).dependencies_check
36
+ Logger.info 'OK'
37
+ end
38
+
39
+ def show(*args)
40
+ builder(args).components_show
41
+ end
42
+
43
+ def help(*_args)
44
+ Logger.info(usage)
45
+ end
46
+
47
+ def run(command, args)
48
+ abort(usage) unless methods.include?(command.to_sym)
49
+
50
+ Logger.debug("GitCompound v#{GitCompound::VERSION}")
51
+ Logger.debug("Running command '#{command}'")
52
+
53
+ public_send(command, *args)
54
+ rescue GitCompoundError => e
55
+ abort Logger.error("Error: #{e.message}", :quiet)
56
+ end
57
+
58
+ private
59
+
60
+ def builder(args)
61
+ Builder.new(manifest(args.first), Lock.new, args)
62
+ end
63
+
64
+ def manifest(filename)
65
+ files = filename ? [filename] : Manifest::FILENAMES
66
+ found = files.select { |file| File.exist?(file) }
67
+
68
+ raise GitCompoundError,
69
+ "Manifest `#{filename || files.inspect}` not found !" if found.empty?
70
+
71
+ contents = File.read(found.first)
72
+ Manifest.new(contents)
73
+ end
74
+
75
+ # rubocop:disable Metrics/AbcSize
76
+ def usage
77
+ <<-EOS
78
+ #{'GitCompound version'.bold.yellow} #{GitCompound::VERSION.bold}
79
+
80
+ Usage: #{'gitcompound'.bold.green} #{
81
+ '[options]'.green} #{'command'.bold} #{'[manifest_file]'.green}
82
+
83
+ Commands:
84
+ #{'build'.bold}
85
+ builds project from manifest (or lockfile if present)
86
+
87
+ If manifest is not specified it uses one of
88
+ #{Manifest::FILENAMES.inspect}
89
+
90
+ #{'update'.bold}
91
+ updates project
92
+
93
+ #{'check'.bold}
94
+ detects circular depenencies, conflicting dependencies
95
+ and checks for name contraints
96
+
97
+ #{'show'.bold}
98
+ prints structure of project
99
+
100
+ #{'help'.bold}
101
+ prints this help
102
+
103
+ Options:'
104
+ #{'--verbose'.bold}
105
+ prints verbose log info
106
+
107
+ #{'--disable-colors'.bold}
108
+ disable ANSI colors in output
109
+ EOS
110
+ end
111
+ # rubocop:enable Metrics/AbcSize
112
+ end
113
+ end