git_compound 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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