quaker 0.2.1 → 0.3.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
  SHA1:
3
- metadata.gz: a5506c92adcb440fad16a366dc3b67a70b62e730
4
- data.tar.gz: 8d60501e924b944e8ab2a39de9d99b2b3c32f0e6
3
+ metadata.gz: 44ce3293ce8c169eedc97c4a6433fcdf865542e3
4
+ data.tar.gz: 085a92c69d42eaa6455c61e9edbcced63597343e
5
5
  SHA512:
6
- metadata.gz: 3317bdd074ce3cabcb055f981f8367e9f154654919f30b5c2d64353cdce05e90c89b2935307689e2b414cc2bb57dfce18478577e8d7c504991c3e0b7905021ea
7
- data.tar.gz: 6e3d17d92363c43103278930c07f32a6b40e439f350004455e6a839aa8f0a5b9847977f7729bc1c32ee7e103780b090042366c885f70ede6a52e7c29f6a8d6ec
6
+ metadata.gz: 5ff78b67637e14828190df6f2e627a50813a7d0d884290aeb5e7a706701503939549f9afb94fda62e4a5a796d974e3e8282550b54a7d7301fc45e8f435a221c4
7
+ data.tar.gz: dd0f33de4a2c3830ab34385c69111d9bbcef8f9d4c80e1d98873f6500fed1a86b5bc80e1e33b1e4d6100ce6ed8fb7711c068280181f885e4ad17453a056178d5
data/.gitignore CHANGED
@@ -8,4 +8,5 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  *.gem
11
+ .DS_Store
11
12
 
data/.gitmodules CHANGED
@@ -1,3 +1,6 @@
1
1
  [submodule "examples/svc1"]
2
2
  path = examples/svc1
3
3
  url = https://github.com/igorshapiro/quaker-svc1.git
4
+ [submodule "spec/fixtures/svc1"]
5
+ path = spec/fixtures/svc1
6
+ url = https://github.com/igorshapiro/quaker-svc1
data/Gemfile CHANGED
@@ -4,3 +4,8 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'clamp'
7
+ gem 'git_clone_url'
8
+
9
+ group :test do
10
+ gem "codeclimate-test-reporter", group: :test, require: nil
11
+ end
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
1
  [![Code Climate](https://codeclimate.com/github/igorshapiro/quaker/badges/gpa.svg)](https://codeclimate.com/github/igorshapiro/quaker)
2
+ [![Gem Version](https://badge.fury.io/rb/quaker.svg)](https://badge.fury.io/rb/quaker)
3
+ [![CircleCI](https://circleci.com/gh/igorshapiro/quaker.svg?style=svg)](https://circleci.com/gh/igorshapiro/quaker)
4
+ [![Test Coverage](https://codeclimate.com/github/igorshapiro/quaker/badges/coverage.svg)](https://codeclimate.com/github/igorshapiro/quaker/coverage)
2
5
 
3
6
  # Quaker
4
7
 
data/exe/quaker CHANGED
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'quaker'
4
+
5
+ Quaker::run
@@ -1,10 +1,12 @@
1
- require 'yaml'
1
+ module Quaker
2
+ require 'yaml'
2
3
 
3
- class ComposeFile
4
- def build spec
5
- {
6
- 'version' => '2',
7
- 'services' => spec
8
- }.to_yaml
4
+ class ComposeFile
5
+ def build spec
6
+ {
7
+ 'version' => '2',
8
+ 'services' => spec
9
+ }.to_yaml
10
+ end
9
11
  end
10
12
  end
@@ -1,31 +1,48 @@
1
- require 'open3'
1
+ module Quaker
2
+ require 'open3'
3
+ require 'git_clone_url'
2
4
 
3
- class GitResolver
4
- def find_dir_for_repo repo
5
- dir = Dir.glob('*')
6
- .select {|f| File.directory? f}
7
- .select {|dir|
8
- stdin, stdout, stderr = Open3.popen3("cd #{dir} && git remote -v")
9
- out = stdout.gets
10
- out && out.include?(repo)
11
- }
12
- .first
13
- return nil unless dir
14
- "./#{dir}"
15
- end
5
+ class GitResolver
6
+ # Parses repository url to { username: ..., repository: ...}
7
+ def parse_url url
8
+ begin
9
+ uri = GitCloneUrl.parse(url)
10
+
11
+ if uri.path.match /^\/?(.*?)\/(.*?)(.git)?$/
12
+ return { username: $1, repo: $2 }
13
+ end
14
+ rescue URI::InvalidComponentError => ex
15
+ end
16
+ url
17
+ end
18
+
19
+ def find_dir_for_repo repo
20
+ dir = Dir.glob('*')
21
+ .select {|f| File.directory? f}
22
+ .select {|dir|
23
+ stdin, stdout, stderr = Open3.popen3("cd #{dir} && git remote -v | awk '{print $2}'")
24
+ stdout.each_line
25
+ .map(&:strip)
26
+ .map {|l| parse_url(l) }
27
+ .include?(parse_url(repo))
28
+ }
29
+ .first
30
+ return nil unless dir
31
+ "./#{dir}"
32
+ end
16
33
 
17
- def resolve services_map
18
- for name, spec in services_map
19
- puts spec
20
- git_repo = spec.delete("git")
21
- next unless git_repo
34
+ def resolve services_map
35
+ for name, spec in services_map
36
+ git_repo = spec.delete("git")
37
+ next unless git_repo
22
38
 
23
- dir = find_dir_for_repo git_repo
39
+ dir = find_dir_for_repo git_repo
24
40
 
25
- puts "ERROR: Unable to find dir for repo #{git_repo}" and return unless dir
41
+ $stderr.puts "ERROR: Unable to find dir for repo #{git_repo}" and return unless dir
26
42
 
27
- spec["build"] = dir
43
+ spec["build"] = dir
44
+ end
45
+ services_map
28
46
  end
29
- services_map
30
47
  end
31
48
  end
@@ -1,15 +1,17 @@
1
- require 'yaml'
1
+ module Quaker
2
+ require 'yaml'
2
3
 
3
- class Include
4
- INCLUDE_KEY = 'include'
5
- def process filepath
6
- dir = File.join(filepath, '..')
7
- spec = YAML.load(File.read(filepath))
8
- return spec unless spec.has_key?(INCLUDE_KEY)
4
+ class Include
5
+ INCLUDE_KEY = 'include'
6
+ def process filepath
7
+ dir = File.join(filepath, '..')
8
+ spec = YAML.load(File.read(filepath))
9
+ return spec unless spec.has_key?(INCLUDE_KEY)
9
10
 
10
- spec
11
- .delete(INCLUDE_KEY)
12
- .map {|file| File.expand_path(file, dir) }
13
- .inject(spec) { |acc, file| acc.merge(process file) }
11
+ spec
12
+ .delete(INCLUDE_KEY)
13
+ .map {|file| File.expand_path(file, dir) }
14
+ .inject(spec) { |acc, file| acc.merge(process file) }
15
+ end
14
16
  end
15
17
  end
@@ -0,0 +1,23 @@
1
+ module Quaker
2
+ class PathExtensions
3
+ def expand_path volumes_desc, build_path
4
+ parts = volumes_desc.split(':')
5
+ parts[0].gsub! /^\~/, build_path
6
+ parts.join(':')
7
+ end
8
+
9
+ def expand_service_volumes spec
10
+ build_path = spec['build']
11
+ return unless build_path
12
+
13
+ volumes = spec['volumes']
14
+ return unless volumes
15
+
16
+ spec['volumes'] = volumes.map{|v| expand_path(v, build_path) }
17
+ end
18
+
19
+ def expand services
20
+ services.each {|name, spec| expand_service_volumes(spec) }
21
+ end
22
+ end
23
+ end
@@ -1,32 +1,34 @@
1
- class TagFilter
2
- def dependencies services_map, name
3
- spec = services_map[name]
4
- depends_on = spec["depends_on"] || []
5
- links = (spec["links"] || []).map{|l| l.split(':')[0]}
6
- deps = links + depends_on
7
- deps + deps.map{|d| dependencies(services_map, d)}.flatten
8
- end
1
+ module Quaker
2
+ class TagFilter
3
+ def dependencies services_map, name
4
+ spec = services_map[name]
5
+ depends_on = spec["depends_on"] || []
6
+ links = (spec["links"] || []).map{|l| l.split(':')[0]}
7
+ deps = links + depends_on
8
+ deps + deps.map{|d| dependencies(services_map, d)}.flatten
9
+ end
9
10
 
10
- def is_tagged_service spec, tags_list
11
- svc_tags = spec.delete("tags") || []
11
+ def is_tagged_service spec, tags_list
12
+ svc_tags = spec.delete("tags") || []
12
13
 
13
- # Skip service if no common tags
14
- common = svc_tags & tags_list
15
- common && !common.empty?
16
- end
14
+ # Skip service if no common tags
15
+ common = svc_tags & tags_list
16
+ common && !common.empty?
17
+ end
17
18
 
18
- def filter services_map, tags_list
19
- return services_map if !tags_list || tags_list.empty?
19
+ def filter services_map, tags_list
20
+ return services_map if !tags_list || tags_list.empty?
20
21
 
21
- services_map.inject({}){|acc, (name, spec)|
22
- if is_tagged_service(spec, tags_list)
23
- acc[name] = spec
22
+ services_map.inject({}){|acc, (name, spec)|
23
+ if is_tagged_service(spec, tags_list)
24
+ acc[name] = spec
24
25
 
25
- dependencies(services_map, name)
26
- .inject(acc){|acc, d| acc.update(d => services_map[d])}
27
- end
26
+ dependencies(services_map, name)
27
+ .inject(acc){|acc, d| acc.update(d => services_map[d])}
28
+ end
28
29
 
29
- acc
30
- }
30
+ acc
31
+ }
32
+ end
31
33
  end
32
34
  end
@@ -0,0 +1,23 @@
1
+ module Quaker
2
+ module TagMatcher
3
+ def self.match tags, patterns
4
+ return true if patterns.nil? || patterns.empty?
5
+ return false if tags.nil? || tags.empty?
6
+
7
+ patterns.any? {|pattern|
8
+ tags.any? {|tag| pattern_matches_string?(tag, pattern)}
9
+ }
10
+ end
11
+
12
+ def self.pattern_matches_string? s, pattern
13
+ s == pattern || wildcard_matches?(s, pattern)
14
+ end
15
+
16
+ def self.wildcard_matches? s, pattern
17
+ return false unless pattern.include?('*')
18
+ escaped = Regexp.escape(pattern).gsub('\*','.*?')
19
+ regex = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
20
+ !!(s =~ regex)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'quaker/tag_matcher'
2
+
3
+ module Quaker
4
+ class Templates
5
+ def apply services
6
+ templates = services.select {|name, v| template?(name)}
7
+ services
8
+ .reject {|name, v| template?(name)}
9
+ .inject({}) {|acc, (name, spec)|
10
+ acc.update(name => extend_with_matchine_templates(spec, templates))
11
+ }
12
+ end
13
+
14
+ def extend_with_matchine_templates service, templates
15
+ templates
16
+ .select {|name, spec| Quaker::TagMatcher::match(service["tags"], spec["tags"])}
17
+ .inject(service) {|svc, (_, spec)|
18
+ filtered_template_content = spec
19
+ .select{|name, spec| name != 'tags' }
20
+ svc.merge(filtered_template_content)
21
+ }
22
+ end
23
+
24
+ def template?(k)
25
+ !!(k =~ /_template$/)
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Quaker
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/quaker.rb CHANGED
@@ -2,39 +2,45 @@ require "quaker/version"
2
2
  require "quaker/include"
3
3
  require "quaker/tag_filter"
4
4
  require "quaker/git_resolver"
5
+ require "quaker/templates"
6
+ require "quaker/path_extensions"
5
7
  require "quaker/compose_file"
6
8
 
7
9
  module Quaker
8
10
  require 'clamp'
9
11
 
10
- Clamp do
11
- parameter "[SPEC_FILE]", "Extended docker-compose file"
12
- option %w(--tags -t), "TAGS", "Filter services (and dependencies) by tag", multivalued: true
13
- option %w(--dir -d), "DIR", "Specify base directory"
14
-
15
- def default_spec_file
16
- File.expand_path('docker/services/all.yml', dir)
17
- end
18
-
19
- def default_dir
20
- Dir.pwd
21
- end
22
-
23
- def dump_params
24
- puts "Spec: #{spec_file}"
25
- puts "Dir: #{dir}"
26
- end
27
-
28
- def execute
29
- dump_params
30
-
31
- Dir.chdir dir
32
-
33
- spec = Include.new.process spec_file
34
- spec = TagFilter.new.filter spec, tags_list
35
- spec = GitResolver.new.resolve spec
36
- spec = ComposeFile.new.build spec
37
- puts spec
12
+ def self.run
13
+ Clamp do
14
+ parameter "[SPEC_FILE]", "Extended docker-compose file"
15
+ option %w(--tags -t), "TAGS", "Filter services (and dependencies) by tag", multivalued: true
16
+ option %w(--dir -d), "DIR", "Specify base directory"
17
+
18
+ def default_spec_file
19
+ File.expand_path('docker/services/all.yml', dir)
20
+ end
21
+
22
+ def default_dir
23
+ Dir.pwd
24
+ end
25
+
26
+ def dump_params
27
+ puts "Spec: #{spec_file}"
28
+ puts "Dir: #{dir}"
29
+ end
30
+
31
+ def execute
32
+ # dump_params
33
+
34
+ Dir.chdir dir
35
+
36
+ spec = Include.new.process spec_file
37
+ spec = Templates.new.apply spec
38
+ spec = TagFilter.new.filter spec, tags_list
39
+ spec = GitResolver.new.resolve spec
40
+ spec = PathExtensions.new.expand spec
41
+ spec = ComposeFile.new.build spec
42
+ puts spec
43
+ end
38
44
  end
39
45
  end
40
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quaker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Shapiro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-10-08 00:00:00.000000000 Z
11
+ date: 2016-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -79,7 +79,10 @@ files:
79
79
  - lib/quaker/compose_file.rb
80
80
  - lib/quaker/git_resolver.rb
81
81
  - lib/quaker/include.rb
82
+ - lib/quaker/path_extensions.rb
82
83
  - lib/quaker/tag_filter.rb
84
+ - lib/quaker/tag_matcher.rb
85
+ - lib/quaker/templates.rb
83
86
  - lib/quaker/version.rb
84
87
  - quaker.gemspec
85
88
  homepage: https://igorshapiro.github.io/quaker/