horatio 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.rubocop.yml +169 -0
- data/.ruby-version +1 -0
- data/.travis.yml +25 -0
- data/Gemfile +10 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +73 -0
- data/Rakefile +13 -0
- data/bin/horatio +33 -0
- data/horatio.gemspec +30 -0
- data/lib/horatio.rb +64 -0
- data/lib/horatio/detector.rb +14 -0
- data/lib/horatio/detector/docker.rb +39 -0
- data/lib/horatio/detector/dynamic.rb +33 -0
- data/lib/horatio/detector/io_handler.rb +44 -0
- data/lib/horatio/detector/json_reader.rb +15 -0
- data/lib/horatio/detector/json_writer.rb +26 -0
- data/lib/horatio/detector/maven.rb +48 -0
- data/lib/horatio/detector/node_package.rb +30 -0
- data/lib/horatio/detector/null_writer.rb +11 -0
- data/lib/horatio/detector/rubygem.rb +32 -0
- data/lib/horatio/detector/validator.rb +20 -0
- data/lib/horatio/helper/vcs.rb +12 -0
- data/lib/horatio/releaser.rb +45 -0
- data/lib/horatio/vcs.rb +11 -0
- data/lib/horatio/vcs/git.rb +41 -0
- data/lib/horatio/vcs/subversion.rb +23 -0
- data/lib/horatio/version.rb +3 -0
- data/spec/fixtures/dynamic +10 -0
- data/spec/fixtures/package.json +105 -0
- data/spec/fixtures/pom.xml +822 -0
- data/spec/fixtures/test.gemspec +32 -0
- data/spec/horatio/detector/docker_spec.rb +30 -0
- data/spec/horatio/detector/dynamic_spec.rb +19 -0
- data/spec/horatio/detector/maven_spec.rb +27 -0
- data/spec/horatio/detector/node_package_spec.rb +19 -0
- data/spec/horatio/detector/rubygem_spec.rb +28 -0
- data/spec/horatio/releaser_spec.rb +25 -0
- data/spec/horatio/vcs/git_spec.rb +22 -0
- data/spec/horatio/vcs/subversion_spec.rb +15 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/fixture_copy.rb +28 -0
- data/spec/support/reporters.rb +3 -0
- metadata +232 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
require "horatio/detector/docker"
|
2
|
+
require "horatio/detector/rubygem"
|
3
|
+
require "horatio/detector/maven"
|
4
|
+
require "horatio/detector/node_package"
|
5
|
+
require "horatio/detector/dynamic"
|
6
|
+
|
7
|
+
module Horatio
|
8
|
+
module Detector
|
9
|
+
def self.detect
|
10
|
+
detected = [Dynamic, Docker, Maven, RubyGem, NodePackage].detect { |d| d.detect }
|
11
|
+
detected ? detected.new : raise("couldn't detect a supported project type, takes docker, maven, gem, horatio")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'horatio/detector/validator'
|
4
|
+
require 'horatio/detector/json_reader'
|
5
|
+
require 'horatio/detector/json_writer'
|
6
|
+
require 'horatio/detector/io_handler'
|
7
|
+
|
8
|
+
module Horatio
|
9
|
+
module Detector
|
10
|
+
class Docker
|
11
|
+
include Validator
|
12
|
+
include IOHandler
|
13
|
+
include JSONReader
|
14
|
+
include JSONWriter
|
15
|
+
|
16
|
+
def default_input
|
17
|
+
'horatio.json'
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_output
|
21
|
+
'horatio.json'
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_commit
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def info
|
29
|
+
memo_read do
|
30
|
+
JSON.parse(File.open(input, 'r').read)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def description
|
35
|
+
'horatio.json'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'horatio/detector/validator'
|
4
|
+
require 'horatio/detector/json_reader'
|
5
|
+
require 'horatio/detector/io_handler'
|
6
|
+
require 'horatio/detector/null_writer'
|
7
|
+
|
8
|
+
module Horatio
|
9
|
+
module Detector
|
10
|
+
class Dynamic
|
11
|
+
include Validator
|
12
|
+
include IOHandler
|
13
|
+
include JSONReader
|
14
|
+
include NULLWritter
|
15
|
+
|
16
|
+
def default_input
|
17
|
+
'horatio'
|
18
|
+
end
|
19
|
+
|
20
|
+
def info
|
21
|
+
memo_read do
|
22
|
+
out = `./#{input}`
|
23
|
+
raise StandardError, "dynamic horatio exec failed: #{out}" unless out
|
24
|
+
JSON.parse(out)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def description
|
29
|
+
'dynamic Horatio data generator'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Horatio
|
2
|
+
module Detector
|
3
|
+
module IOHandler
|
4
|
+
module ClassMethods
|
5
|
+
def detect
|
6
|
+
new.detect
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :input, :output
|
11
|
+
|
12
|
+
def default_input
|
13
|
+
'horatio.json'
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_output
|
17
|
+
'horatio.json'
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(input: nil, output: nil)
|
21
|
+
@input = input || default_input
|
22
|
+
@output = output || default_output
|
23
|
+
@info = {}
|
24
|
+
@read = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def detect
|
28
|
+
File.exists?(@input) ? true : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def memo_read
|
32
|
+
return @info if @read
|
33
|
+
return unless block_given?
|
34
|
+
@info = yield
|
35
|
+
@read = true
|
36
|
+
@info
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.included(base)
|
40
|
+
base.extend(ClassMethods)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Horatio
|
4
|
+
module Detector
|
5
|
+
module JSONWriter
|
6
|
+
def increment(releaser)
|
7
|
+
increment_version
|
8
|
+
if ENV['DRY_RUN']
|
9
|
+
color { Log.info "dry run: would've saved updated #{output}" }
|
10
|
+
return
|
11
|
+
end
|
12
|
+
color { Log.info "saving updated #{output}" }
|
13
|
+
File.open(output, 'w') { |f| f.write(JSON.pretty_generate(info)) } unless ENV['DRY_RUN']
|
14
|
+
releaser.vcs.commit(output)
|
15
|
+
end
|
16
|
+
|
17
|
+
def increment_version
|
18
|
+
parts = info['version'].split('.')
|
19
|
+
parts[-1] = parts[-1].to_i + 1
|
20
|
+
version = parts.join('.')
|
21
|
+
color { Log.info "incrementing version from #{info['version']} to #{version}"}
|
22
|
+
info['version'] = version
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'nori'
|
2
|
+
|
3
|
+
require 'horatio/detector/validator'
|
4
|
+
require 'horatio/detector/io_handler'
|
5
|
+
require 'horatio/detector/null_writer'
|
6
|
+
|
7
|
+
module Horatio
|
8
|
+
module Detector
|
9
|
+
class Maven
|
10
|
+
include Validator
|
11
|
+
include IOHandler
|
12
|
+
|
13
|
+
def default_input
|
14
|
+
'pom.xml'
|
15
|
+
end
|
16
|
+
|
17
|
+
def info
|
18
|
+
memo_read do
|
19
|
+
File.open('pom.xml', 'r') { |f| Nori.new.parse(f.read) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def name
|
24
|
+
info['project']['artifactId']
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Maven version reader
|
29
|
+
#
|
30
|
+
# We need to decrease the number if we see SNAPSHOT, this is becasue
|
31
|
+
# maven increments it in anticipation for the next build
|
32
|
+
#
|
33
|
+
def version
|
34
|
+
version = info['project']['version'] || info['project']['parent']['version']
|
35
|
+
return version unless version =~ /-SNAPSHOT$/
|
36
|
+
version = version.split('-')[0]
|
37
|
+
version = version.split('.')
|
38
|
+
minor = version.last.to_i > 1 ? version.last.to_i - 1 : version.last
|
39
|
+
version[-1] = minor
|
40
|
+
version.join('.')
|
41
|
+
end
|
42
|
+
|
43
|
+
def description
|
44
|
+
'JAVA Maven pom.xml'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'horatio/detector/validator'
|
4
|
+
require 'horatio/detector/json_reader'
|
5
|
+
require 'horatio/detector/null_writer'
|
6
|
+
|
7
|
+
module Horatio
|
8
|
+
module Detector
|
9
|
+
class NodePackage
|
10
|
+
include Validator
|
11
|
+
include IOHandler
|
12
|
+
include JSONReader
|
13
|
+
include NULLWritter
|
14
|
+
|
15
|
+
def default_input
|
16
|
+
'package.json'
|
17
|
+
end
|
18
|
+
|
19
|
+
def info
|
20
|
+
memo_read do
|
21
|
+
File.open(input, 'r') { |f| JSON.parse(f.read) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def description
|
26
|
+
'Node.js package.json'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'horatio/detector/validator'
|
2
|
+
require 'horatio/detector/null_writer'
|
3
|
+
|
4
|
+
module Horatio
|
5
|
+
module Detector
|
6
|
+
class RubyGem
|
7
|
+
include Validator
|
8
|
+
include NULLWritter
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
File.open(Dir.glob('*.gemspec').first, 'r') { |f| @spec_file = f.read }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.detect
|
15
|
+
Dir.glob('*.gemspec').any?
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
@spec_file.match(/spec\.name.*$/i).to_s.split('=')[1].strip.gsub(/[\'\"\\]/, '')
|
20
|
+
end
|
21
|
+
|
22
|
+
def version
|
23
|
+
File.open(Dir.glob('lib/**/version.rb').first, 'rb') { |f| @version_file = f.read }
|
24
|
+
@version_file.match(/(\d.)+\d/).to_s.strip.gsub(/[\'\"\\]/, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
def description
|
28
|
+
'Ruby gemspec'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Horatio
|
2
|
+
module Detector
|
3
|
+
module Validator
|
4
|
+
def validate_name
|
5
|
+
color { Log.info "validating passed in name"}
|
6
|
+
name.match(/^[\w\-\.]*$/i) ? true : raise("name specified using invalid format")
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_version
|
10
|
+
color { Log.info "validating passed in version"}
|
11
|
+
version.match(/^(\d+.)+\d+/i) ? true : raise("version specified using invalid format")
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate
|
15
|
+
validate_version
|
16
|
+
validate_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Horatio
|
4
|
+
class Releaser
|
5
|
+
|
6
|
+
attr_accessor :release, :vcs
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@options = options
|
10
|
+
@registry = @options['registry']
|
11
|
+
@release = Horatio::Detector.detect
|
12
|
+
color { Log.info "detected #{@release.description}" }
|
13
|
+
@vcs = Horatio::VCS.detect(@options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_artifact
|
17
|
+
contents = JSON.pretty_generate({'name' => @release.name,
|
18
|
+
'version' => "#{@release.version}-#{@vcs.latest_revision}",
|
19
|
+
'app_version' => @release.version,
|
20
|
+
'source_revision' => @vcs.latest_revision,
|
21
|
+
'url' => "#{@registry}/#{@release.name}:#{@release.version}-#{@vcs.latest_revision}"})
|
22
|
+
|
23
|
+
File.open('docker-image.json', 'w') { |f | f.write(contents); }
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
color { Log.info "Running Docker build for #{@release.name}-#{@release.version}" }
|
28
|
+
|
29
|
+
@release.validate
|
30
|
+
|
31
|
+
color { Log.info "latest revision is #{@vcs.latest_revision }" }
|
32
|
+
|
33
|
+
no_cache = ENV['DOCKER_BUILD_NO_CACHE'] ? "--no-cache " : ""
|
34
|
+
|
35
|
+
run_sh "docker build " + no_cache + "-t #{@registry}/#{@release.name}:#{@release.version}-#{@vcs.latest_revision} ."
|
36
|
+
run_sh "docker push #{@registry}/#{@release.name}"
|
37
|
+
|
38
|
+
generate_artifact
|
39
|
+
|
40
|
+
@release.increment(self)
|
41
|
+
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/horatio/vcs.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'horatio/vcs/git'
|
2
|
+
require 'horatio/vcs/subversion'
|
3
|
+
|
4
|
+
module Horatio
|
5
|
+
module VCS
|
6
|
+
def self.detect(options={})
|
7
|
+
vcs = [Git, Subversion].detect { |d| d.detect }
|
8
|
+
vcs ? vcs.new(options) : abort('No VCS detected (git/subversion)')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "horatio/helper/vcs"
|
3
|
+
|
4
|
+
module Horatio
|
5
|
+
module VCS
|
6
|
+
class Git
|
7
|
+
|
8
|
+
include Horatio::Helper::VCS
|
9
|
+
|
10
|
+
def self.detect
|
11
|
+
sh('git rev-parse HEAD').last.exitstatus == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def latest_revision
|
15
|
+
sh('git rev-parse --short HEAD').first.strip
|
16
|
+
end
|
17
|
+
|
18
|
+
def commit(file)
|
19
|
+
color { Log.info "Adding remote before pushing verion update: #{remote_url}" }
|
20
|
+
sh "git remote rm horatio &> /dev/null"
|
21
|
+
run_sh "git remote add horatio #{remote_url}"
|
22
|
+
run_sh "git commit -m 'image release' #{file}"
|
23
|
+
run_sh "git push horatio master"
|
24
|
+
end
|
25
|
+
|
26
|
+
def remote_url
|
27
|
+
begin
|
28
|
+
url = @options.fetch('git-repo-url')
|
29
|
+
rescue => e
|
30
|
+
color(ERROR) { Log.error "Since Horatio 1.0 you must specify a remote repo URL when building a docker only / horatio build" }
|
31
|
+
color(ERROR) { Log.error "If you see this message update your build to look like the following example: 'horatio --git-repo-url http://github.com/test/test.git'" }
|
32
|
+
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
|
36
|
+
url
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'horatio/helper/vcs'
|
2
|
+
|
3
|
+
module Horatio
|
4
|
+
module VCS
|
5
|
+
class Subversion
|
6
|
+
|
7
|
+
include Horatio::Helper::VCS
|
8
|
+
|
9
|
+
def self.detect
|
10
|
+
['.svn', '.subversion'].any? { |f| File.exist?(f) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def latest_revision
|
14
|
+
sh('svnversion').first.strip.split(':')[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
def commit(file)
|
18
|
+
run_sh "svn commit -m 'image release' #{file}"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|