git_compound 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/Compoundfile +4 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +291 -0
- data/Rakefile +13 -0
- data/bin/console +7 -0
- data/bin/setup +5 -0
- data/exe/gitcompound +5 -0
- data/git_compound.gemspec +33 -0
- data/lib/git_compound/builder.rb +93 -0
- data/lib/git_compound/command/options.rb +55 -0
- data/lib/git_compound/command.rb +113 -0
- data/lib/git_compound/component/destination.rb +45 -0
- data/lib/git_compound/component/source.rb +53 -0
- data/lib/git_compound/component/version/branch.rb +30 -0
- data/lib/git_compound/component/version/gem_version.rb +45 -0
- data/lib/git_compound/component/version/sha.rb +33 -0
- data/lib/git_compound/component/version/tag.rb +30 -0
- data/lib/git_compound/component/version/version_strategy.rb +45 -0
- data/lib/git_compound/component.rb +78 -0
- data/lib/git_compound/dsl/component_dsl.rb +60 -0
- data/lib/git_compound/dsl/manifest_dsl.rb +27 -0
- data/lib/git_compound/exceptions.rb +16 -0
- data/lib/git_compound/lock.rb +64 -0
- data/lib/git_compound/logger/colors.rb +123 -0
- data/lib/git_compound/logger/core_ext/string.rb +5 -0
- data/lib/git_compound/logger.rb +43 -0
- data/lib/git_compound/manifest.rb +39 -0
- data/lib/git_compound/node.rb +17 -0
- data/lib/git_compound/repository/git_command.rb +33 -0
- data/lib/git_compound/repository/git_repository.rb +79 -0
- data/lib/git_compound/repository/git_version.rb +43 -0
- data/lib/git_compound/repository/remote_file/git_archive_strategy.rb +30 -0
- data/lib/git_compound/repository/remote_file/github_strategy.rb +54 -0
- data/lib/git_compound/repository/remote_file/remote_file_strategy.rb +27 -0
- data/lib/git_compound/repository/remote_file.rb +34 -0
- data/lib/git_compound/repository/repository_local.rb +81 -0
- data/lib/git_compound/repository/repository_remote.rb +12 -0
- data/lib/git_compound/repository.rb +19 -0
- data/lib/git_compound/task/task.rb +28 -0
- data/lib/git_compound/task/task_all.rb +27 -0
- data/lib/git_compound/task/task_each.rb +18 -0
- data/lib/git_compound/task/task_single.rb +21 -0
- data/lib/git_compound/task.rb +22 -0
- data/lib/git_compound/version.rb +5 -0
- data/lib/git_compound/worker/circular_dependency_checker.rb +31 -0
- data/lib/git_compound/worker/component_builder.rb +30 -0
- data/lib/git_compound/worker/component_replacer.rb +25 -0
- data/lib/git_compound/worker/component_update_dispatcher.rb +64 -0
- data/lib/git_compound/worker/component_updater.rb +24 -0
- data/lib/git_compound/worker/components_collector.rb +20 -0
- data/lib/git_compound/worker/conflicting_dependency_checker.rb +31 -0
- data/lib/git_compound/worker/local_changes_guard.rb +47 -0
- data/lib/git_compound/worker/name_constraint_checker.rb +20 -0
- data/lib/git_compound/worker/pretty_print.rb +18 -0
- data/lib/git_compound/worker/task_runner.rb +12 -0
- data/lib/git_compound/worker/worker.rb +20 -0
- data/lib/git_compound.rb +112 -0
- metadata +193 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module GitCompound
|
4
|
+
class Component
|
5
|
+
# Component destination
|
6
|
+
#
|
7
|
+
class Destination
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def initialize(path, component)
|
11
|
+
raise CompoundSyntaxError, 'Destination cannot be empty' if
|
12
|
+
path.nil? || path.empty?
|
13
|
+
|
14
|
+
raise CompoundSyntaxError,
|
15
|
+
'Destination should contain at least one directory' unless
|
16
|
+
Pathname.new(path).each_filename.count > 0
|
17
|
+
|
18
|
+
@path = path
|
19
|
+
@component = component
|
20
|
+
end
|
21
|
+
|
22
|
+
def expanded_path
|
23
|
+
pathname = Pathname.new(@path)
|
24
|
+
|
25
|
+
unless pathname.absolute?
|
26
|
+
ancestor_paths = @component.ancestors.map(&:destination_path)
|
27
|
+
pathname = Pathname.new('.').join(*ancestor_paths) + pathname
|
28
|
+
end
|
29
|
+
|
30
|
+
Pathname.new("./#{pathname}").cleanpath.to_s + '/'
|
31
|
+
end
|
32
|
+
|
33
|
+
def exists?
|
34
|
+
FileTest.exist?(expanded_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def repository
|
38
|
+
destination_repository =
|
39
|
+
Repository::RepositoryLocal.new(expanded_path)
|
40
|
+
yield destination_repository if block_given?
|
41
|
+
destination_repository
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module GitCompound
|
4
|
+
class Component
|
5
|
+
# Component source
|
6
|
+
#
|
7
|
+
class Source
|
8
|
+
extend Forwardable
|
9
|
+
delegate [:sha, :ref] => :@version
|
10
|
+
attr_reader :origin, :repository, :version, :options
|
11
|
+
|
12
|
+
def initialize(origin, version, strategy, options, component)
|
13
|
+
raise CompoundSyntaxError, 'Source cannot be empty' if
|
14
|
+
origin.nil? || origin.empty?
|
15
|
+
|
16
|
+
@origin = origin
|
17
|
+
@strategy = strategy
|
18
|
+
@options = options
|
19
|
+
@component = component
|
20
|
+
@repository = Repository.factory(@origin)
|
21
|
+
@version = strategy.new(@repository, version)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Loads manifest from source repository
|
25
|
+
#
|
26
|
+
def manifest
|
27
|
+
raise DependencyError,
|
28
|
+
"Version #{@version} unreachable" unless @version.reachable?
|
29
|
+
|
30
|
+
contents = @repository.files_contents(Manifest::FILENAMES, @version.ref)
|
31
|
+
Manifest.new(contents, @component)
|
32
|
+
rescue FileNotFoundError
|
33
|
+
Manifest.new(nil, @component)
|
34
|
+
end
|
35
|
+
|
36
|
+
def clone(destination)
|
37
|
+
@repository.clone(destination, clone_args)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def clone_args
|
43
|
+
raise CompoundSyntaxError,
|
44
|
+
'`shallow` keyword not available for sha version strategy' if
|
45
|
+
@options.include?(:shallow) && @strategy == Component::Version::SHA
|
46
|
+
|
47
|
+
opts = []
|
48
|
+
opts << "--branch '#{@version.ref}' --depth 1" if @options.include? :shallow
|
49
|
+
opts.join(' ')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module GitCompound
|
2
|
+
class Component
|
3
|
+
module Version
|
4
|
+
# Component version indicated by branch (head of branch)
|
5
|
+
#
|
6
|
+
class Branch < VersionStrategy
|
7
|
+
def initialize(repository, branch)
|
8
|
+
@repository = repository
|
9
|
+
@branch = branch
|
10
|
+
end
|
11
|
+
|
12
|
+
def ref
|
13
|
+
@branch
|
14
|
+
end
|
15
|
+
|
16
|
+
def sha
|
17
|
+
@repository.branches[@branch]
|
18
|
+
end
|
19
|
+
|
20
|
+
def reachable?
|
21
|
+
@repository.branches.key?(@branch)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"branch: #{@branch}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module GitCompound
|
2
|
+
class Component
|
3
|
+
module Version
|
4
|
+
# Component Gem-like version
|
5
|
+
#
|
6
|
+
class GemVersion < VersionStrategy
|
7
|
+
attr_reader :requirement
|
8
|
+
|
9
|
+
def initialize(repository, requirement)
|
10
|
+
raise CompoundSyntaxError, 'Malformed version requirement string' unless
|
11
|
+
requirement =~ Gem::Requirement::PATTERN
|
12
|
+
|
13
|
+
@repository = repository
|
14
|
+
@requirement = requirement
|
15
|
+
end
|
16
|
+
|
17
|
+
def lastest_version
|
18
|
+
matches.first
|
19
|
+
end
|
20
|
+
|
21
|
+
def ref
|
22
|
+
lastest_version.tag
|
23
|
+
end
|
24
|
+
|
25
|
+
def sha
|
26
|
+
lastest_version.sha
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches
|
30
|
+
versions = @repository.versions
|
31
|
+
versions.select! { |version| version.matches?(@requirement) }
|
32
|
+
versions.sort.reverse
|
33
|
+
end
|
34
|
+
|
35
|
+
def reachable?
|
36
|
+
matches.any?
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"gem version: #{@requirement}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module GitCompound
|
2
|
+
class Component
|
3
|
+
module Version
|
4
|
+
# Component version indicated by SHA hash
|
5
|
+
#
|
6
|
+
class SHA < VersionStrategy
|
7
|
+
def initialize(repository, sha)
|
8
|
+
@repository = repository
|
9
|
+
@sha = sha
|
10
|
+
end
|
11
|
+
|
12
|
+
def ref
|
13
|
+
@sha
|
14
|
+
end
|
15
|
+
|
16
|
+
def sha # rubocop:disable Style/TrivialAccessors
|
17
|
+
@sha
|
18
|
+
end
|
19
|
+
|
20
|
+
def reachable?
|
21
|
+
# We assume that SHA is always available as we do not want
|
22
|
+
# to clone repository and check it -- this probably needs
|
23
|
+
# to be changed, so -- TODO
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"sha: #{@sha[0..7]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module GitCompound
|
2
|
+
class Component
|
3
|
+
module Version
|
4
|
+
# Component version as tag
|
5
|
+
#
|
6
|
+
class Tag < VersionStrategy
|
7
|
+
def initialize(repository, tag)
|
8
|
+
@repository = repository
|
9
|
+
@tag = tag
|
10
|
+
end
|
11
|
+
|
12
|
+
def ref
|
13
|
+
@tag
|
14
|
+
end
|
15
|
+
|
16
|
+
def sha
|
17
|
+
@repository.tags[@tag]
|
18
|
+
end
|
19
|
+
|
20
|
+
def reachable?
|
21
|
+
@repository.tags.key?(@tag)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"tag: #{@tag}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module GitCompound
|
2
|
+
class Component
|
3
|
+
module Version
|
4
|
+
# Abstraction for component versions like
|
5
|
+
# gem version, sha and branch
|
6
|
+
#
|
7
|
+
class VersionStrategy
|
8
|
+
def initialize
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
# Should return git reference (ex branch, tag or sha)
|
13
|
+
# This should not raise exception if unreachable
|
14
|
+
#
|
15
|
+
def ref
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Should return sha for specified reference
|
20
|
+
# (ex tagged commit sha or head of specified branch)
|
21
|
+
#
|
22
|
+
def sha
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
# Should return true if this reference in source repository
|
27
|
+
# is reachable
|
28
|
+
#
|
29
|
+
def reachable?
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
|
33
|
+
# String representation of this version strategy
|
34
|
+
#
|
35
|
+
def to_s
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
to_s == other.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module GitCompound
|
5
|
+
# Component
|
6
|
+
#
|
7
|
+
class Component < Node
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
attr_reader :name
|
11
|
+
attr_accessor :source, :destination
|
12
|
+
|
13
|
+
def initialize(name, parent = nil, &block)
|
14
|
+
@name = name
|
15
|
+
@parent = parent
|
16
|
+
DSL::ComponentDSL.new(self, &block) if block
|
17
|
+
raise CompoundSyntaxError, "No block given for component `#{@name}`" unless block
|
18
|
+
raise GitCompoundError, "Component `#{@name}` invalid" unless valid?
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
[@name, @source, @destination].all?
|
23
|
+
end
|
24
|
+
|
25
|
+
def process(*workers)
|
26
|
+
workers.each { |worker| worker.visit_component(self) }
|
27
|
+
@manifest.process(*workers) if manifest
|
28
|
+
end
|
29
|
+
|
30
|
+
def manifest
|
31
|
+
@manifest ||= @source.manifest
|
32
|
+
end
|
33
|
+
|
34
|
+
def build
|
35
|
+
@source.clone(destination_path)
|
36
|
+
@destination.repository do |repo|
|
37
|
+
repo.checkout(@source.ref)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def update
|
42
|
+
@destination.repository do |repo|
|
43
|
+
repo.fetch
|
44
|
+
repo.checkout(@source.ref)
|
45
|
+
repo.merge
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove!
|
50
|
+
path = destination_path
|
51
|
+
raise GitCompoundError, 'Risky directory !' if
|
52
|
+
path.start_with?('/') || path.include?('..')
|
53
|
+
raise GitCompoundError, 'Not a directory !' unless
|
54
|
+
File.directory?(path)
|
55
|
+
|
56
|
+
FileUtils.remove_entry_secure(path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def ==(other)
|
60
|
+
origin == other.origin || manifest == other.manifest
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_hash
|
64
|
+
{ name: @name,
|
65
|
+
sha: @source.sha,
|
66
|
+
source: @source.origin,
|
67
|
+
destination: @destination.expanded_path
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# delegators
|
72
|
+
|
73
|
+
delegate [:sha, :origin, :repository, :version] => :@source
|
74
|
+
def_delegator :@destination, :expanded_path, :destination_path
|
75
|
+
def_delegator :@destination, :exists?, :destination_exists?
|
76
|
+
def_delegator :@destination, :repository, :destination_repository
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module GitCompound
|
2
|
+
# Compound Domain Specific Language
|
3
|
+
#
|
4
|
+
module DSL
|
5
|
+
# DSL for Component
|
6
|
+
#
|
7
|
+
class ComponentDSL
|
8
|
+
def initialize(component, &block)
|
9
|
+
@component = component
|
10
|
+
instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Custom version strategy, also available via DSL
|
14
|
+
#
|
15
|
+
def version_strategy(version, strategy)
|
16
|
+
raise CompoundSyntaxError,
|
17
|
+
'Version strategy already set !' if @version_strategy
|
18
|
+
|
19
|
+
@version = version
|
20
|
+
@version_strategy = strategy
|
21
|
+
end
|
22
|
+
|
23
|
+
def version(component_version)
|
24
|
+
version_strategy(component_version, Component::Version::GemVersion)
|
25
|
+
end
|
26
|
+
|
27
|
+
def branch(component_branch)
|
28
|
+
version_strategy(component_branch, Component::Version::Branch)
|
29
|
+
end
|
30
|
+
|
31
|
+
def tag(component_tag)
|
32
|
+
version_strategy(component_tag, Component::Version::Tag)
|
33
|
+
end
|
34
|
+
|
35
|
+
def sha(component_sha)
|
36
|
+
raise CompoundSyntaxError, 'Invalid SHA1 format' unless
|
37
|
+
component_sha.match(/[0-9a-f]{5,40}/)
|
38
|
+
|
39
|
+
version_strategy(component_sha, Component::Version::SHA)
|
40
|
+
end
|
41
|
+
|
42
|
+
def source(component_source, *source_options)
|
43
|
+
raise CompoundSyntaxError,
|
44
|
+
'Version/branch/tag/sha needed first' unless @version
|
45
|
+
|
46
|
+
@component.source =
|
47
|
+
Component::Source.new(component_source,
|
48
|
+
@version,
|
49
|
+
@version_strategy,
|
50
|
+
source_options,
|
51
|
+
@component)
|
52
|
+
end
|
53
|
+
|
54
|
+
def destination(component_path)
|
55
|
+
@component.destination =
|
56
|
+
Component::Destination.new(component_path, @component)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module GitCompound
|
2
|
+
# Compound Domain Specific Language
|
3
|
+
#
|
4
|
+
module DSL
|
5
|
+
# DSL for Manifest
|
6
|
+
#
|
7
|
+
class ManifestDSL
|
8
|
+
def initialize(manifest, contents)
|
9
|
+
@manifest = manifest
|
10
|
+
instance_eval(contents)
|
11
|
+
end
|
12
|
+
|
13
|
+
def name(component_name)
|
14
|
+
@manifest.name = component_name.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def component(name, &block)
|
18
|
+
@manifest.components.store(name.to_sym, Component.new(name, @manifest, &block))
|
19
|
+
end
|
20
|
+
|
21
|
+
def task(name, type = nil, &block)
|
22
|
+
new_task = Task.factory(name, type, @manifest, &block)
|
23
|
+
@manifest.tasks.store(name.to_sym, new_task) if new_task
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module GitCompound
|
2
|
+
class GitCompoundError < StandardError; end
|
3
|
+
|
4
|
+
class CompoundLoadError < GitCompoundError; end
|
5
|
+
class CompoundSyntaxError < GitCompoundError; end
|
6
|
+
class FileNotFoundError < GitCompoundError; end
|
7
|
+
class FileUnreachableError < GitCompoundError; end
|
8
|
+
class RepositoryUnreachableError < GitCompoundError; end
|
9
|
+
class GitCommandError < GitCompoundError; end
|
10
|
+
class LocalRepositoryNotFoundError < GitCompoundError; end
|
11
|
+
class DependencyError < GitCompoundError; end
|
12
|
+
class CircularDependencyError < GitCompoundError; end
|
13
|
+
class ConflictingDependencyError < GitCompoundError; end
|
14
|
+
class NameConstraintError < GitCompoundError; end
|
15
|
+
class LocalChangesError < GitCompoundError; end
|
16
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module GitCompound
|
4
|
+
# Class that represents lock file
|
5
|
+
#
|
6
|
+
class Lock
|
7
|
+
FILENAME = '.gitcompound.lock'
|
8
|
+
|
9
|
+
def self.exist?
|
10
|
+
File.exist?(FILENAME)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(file = FILENAME)
|
14
|
+
@file = file
|
15
|
+
@locked = YAML.load(File.read(file)) if File.exist?(file)
|
16
|
+
clean unless @locked.is_a? Hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def clean
|
20
|
+
@locked = { manifest: '', components: [] }
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def lock_manifest(manifest)
|
25
|
+
@locked[:manifest] = manifest.md5sum
|
26
|
+
end
|
27
|
+
|
28
|
+
def lock_component(component)
|
29
|
+
@locked[:components] << component.to_hash
|
30
|
+
end
|
31
|
+
|
32
|
+
def manifest
|
33
|
+
@locked[:manifest]
|
34
|
+
end
|
35
|
+
|
36
|
+
def components
|
37
|
+
@locked[:components].to_a.map do |locked|
|
38
|
+
Component.new(locked[:name].to_sym) do
|
39
|
+
sha locked[:sha]
|
40
|
+
source locked[:source]
|
41
|
+
destination locked[:destination]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def contents
|
47
|
+
@locked
|
48
|
+
end
|
49
|
+
|
50
|
+
def find(component)
|
51
|
+
components.find do |locked_component|
|
52
|
+
locked_component.destination_path == component.destination_path
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def process(worker)
|
57
|
+
components.each { |component| worker.visit_component(component) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def write
|
61
|
+
File.open(@file, 'w') { |f| f.puts @locked.to_yaml }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module GitCompound
|
2
|
+
module Logger
|
3
|
+
# Colors module
|
4
|
+
#
|
5
|
+
module Colors
|
6
|
+
def self.included(parent_class)
|
7
|
+
parent_class.extend ClassMethods
|
8
|
+
parent_class.class_eval { create_color_methods }
|
9
|
+
end
|
10
|
+
|
11
|
+
def colorize(params)
|
12
|
+
return self if colors_unavailable?
|
13
|
+
scan_colors.inject('') do |str, match|
|
14
|
+
set_colors(match, params)
|
15
|
+
str << "\033[#{match[0]};#{match[1]};#{match[2]}m#{match[3]}\033[0m"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def colors_unavailable?
|
22
|
+
self.class.disable_colors || RUBY_VERSION < '2.0.0' || RUBY_PLATFORM =~ /win32/
|
23
|
+
end
|
24
|
+
|
25
|
+
def scan_colors
|
26
|
+
scan(/\033\[([0-9;]+)m(.+?)\033\[0m|([^\033]+)/m).map do |match|
|
27
|
+
colors = (match[0] || '').split(';')
|
28
|
+
Array.new(4).tap do |array|
|
29
|
+
array[0], array[1], array[2] = colors if colors.length == 3
|
30
|
+
array[3] = match[1] || match[2]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_colors(match, params)
|
36
|
+
default_colors(match)
|
37
|
+
|
38
|
+
mode = mode(params[:mode])
|
39
|
+
color = color(params[:color])
|
40
|
+
bgcolor = bgcolor(params[:bgcolor])
|
41
|
+
|
42
|
+
match[0] = mode if mode
|
43
|
+
match[1] = color if color
|
44
|
+
match[2] = bgcolor if bgcolor
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_colors(match)
|
48
|
+
match[0] ||= mode(:default)
|
49
|
+
match[1] ||= color(:default)
|
50
|
+
match[2] ||= bgcolor(:default)
|
51
|
+
end
|
52
|
+
|
53
|
+
def color(color)
|
54
|
+
self.class.colors[color] + 30 if self.class.colors[color]
|
55
|
+
end
|
56
|
+
|
57
|
+
def bgcolor(color)
|
58
|
+
self.class.colors[color] + 40 if self.class.colors[color]
|
59
|
+
end
|
60
|
+
|
61
|
+
def mode(mode)
|
62
|
+
self.class.modes[mode] if self.class.modes[mode]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Class methods core ext for String
|
67
|
+
#
|
68
|
+
module ClassMethods
|
69
|
+
def disable_colors=(value)
|
70
|
+
@disable_colors = value && true
|
71
|
+
end
|
72
|
+
|
73
|
+
def disable_colors
|
74
|
+
@disable_colors ||= false
|
75
|
+
end
|
76
|
+
|
77
|
+
def colors
|
78
|
+
{
|
79
|
+
black: 0,
|
80
|
+
red: 1,
|
81
|
+
green: 2,
|
82
|
+
yellow: 3,
|
83
|
+
blue: 4,
|
84
|
+
magenta: 5,
|
85
|
+
cyan: 6,
|
86
|
+
white: 7,
|
87
|
+
default: 9
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
def modes
|
92
|
+
{
|
93
|
+
default: 0,
|
94
|
+
bold: 1
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def create_color_methods
|
101
|
+
colors.keys.each do |key|
|
102
|
+
next if key == :default
|
103
|
+
|
104
|
+
define_method key do
|
105
|
+
colorize(color: key)
|
106
|
+
end
|
107
|
+
|
108
|
+
define_method "on_#{key}" do
|
109
|
+
colorize(bgcolor: key)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
modes.keys.each do |key|
|
114
|
+
next if key == :default
|
115
|
+
|
116
|
+
define_method key do
|
117
|
+
colorize(mode: key)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module GitCompound
|
2
|
+
# Logger class
|
3
|
+
#
|
4
|
+
module Logger
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def verbose=(value)
|
8
|
+
@verbose = value && true
|
9
|
+
end
|
10
|
+
|
11
|
+
def verbose
|
12
|
+
@verbose ||= false
|
13
|
+
end
|
14
|
+
|
15
|
+
def inline(inline_message)
|
16
|
+
print inline_message
|
17
|
+
inline_message
|
18
|
+
end
|
19
|
+
|
20
|
+
def debug(debug_message, *params)
|
21
|
+
log(debug_message.cyan, *params) if verbose
|
22
|
+
end
|
23
|
+
|
24
|
+
def info(information_message, *params)
|
25
|
+
log(information_message, *params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def warn(warning_message, *params)
|
29
|
+
log(warning_message.red.bold, *params)
|
30
|
+
end
|
31
|
+
|
32
|
+
def error(error_message, *params)
|
33
|
+
log(error_message.on_red.white.bold, *params)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def log(message, *params)
|
39
|
+
puts message unless params.include?(:quiet)
|
40
|
+
message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|