devup 0.3.2 → 0.4.3.1

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
  SHA256:
3
- metadata.gz: d56bfb5d2d4fd21f7326151031d7bb37c4228f6956c48a796f18c7fc623798e8
4
- data.tar.gz: bde24f19bd8a2e2b8abbf449d20169b5ee2bca39d68a8c4f438d396793585288
3
+ metadata.gz: 96dfaf1740d9680c2a7060ac68cec0e442df06c459a802f96509429b19ff7ac7
4
+ data.tar.gz: 01423f04ef9c067798d12d595421ba51ef75410ebfe42c55c66f16dbf265aef2
5
5
  SHA512:
6
- metadata.gz: 6d31aa4918137722bcfe843398d1c72fa5c07445b2a2e0b283b386b01f4d5e21a8ed7e4a4bd8b9ceb0c36a0558ffd6daabf39862dff51394b8c7706cfc3015e2
7
- data.tar.gz: fa38296b3c063c39d7850e7962007324884548b34e82994467ab0f1c1cc4f6d8ce63129ac39d4b9379d8c71ef85524949900f46b37af95ab9ddd3e5667de6551
6
+ metadata.gz: d33c8f40d01c63d1c1d3738e095b7975d78cd7e08a5abecfdc6f5d2349f06216d993e382bf94ccafe117c4e750b5e5973723228ec028d4a86aa2b3212dd2bfad
7
+ data.tar.gz: c43fb7fd47119bbfe821f26e06d6b081fcfcb93f31310a9e89bf0d7e61544822d5d5051db26b3574f3b42e02504464ef39733fedbc6f5f3eb8eb82cf73f1e990
data/.gitignore CHANGED
@@ -14,6 +14,7 @@
14
14
  # Editor config
15
15
  .vimrc
16
16
 
17
+ .env.services
17
18
  spec/dummy/.env.services
18
19
  spec/dummy_rails/.env.services
19
20
 
data/Gemfile CHANGED
@@ -8,3 +8,4 @@ gem "rspec", "~> 3.0"
8
8
  gem "standard"
9
9
  gem "simplecov", require: false
10
10
  gem "byebug"
11
+ gem "lefthook"
@@ -1,19 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- devup (0.3.2)
4
+ devup (0.4.3.1)
5
5
  dotenv
6
+ dry-cli
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
11
  ast (2.4.0)
11
12
  byebug (11.1.3)
13
+ concurrent-ruby (1.1.6)
12
14
  diff-lcs (1.3)
13
15
  docile (1.3.2)
14
16
  dotenv (2.7.5)
17
+ dry-cli (0.6.0)
18
+ concurrent-ruby (~> 1.0)
15
19
  jaro_winkler (1.5.4)
16
20
  json (2.3.0)
21
+ lefthook (0.7.2)
17
22
  parallel (1.19.1)
18
23
  parser (2.7.1.0)
19
24
  ast (~> 2.4.0)
@@ -60,6 +65,7 @@ PLATFORMS
60
65
  DEPENDENCIES
61
66
  byebug
62
67
  devup!
68
+ lefthook
63
69
  rake (~> 12.0)
64
70
  rspec (~> 3.0)
65
71
  simplecov
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # DevUp! [![Travis (.com) branch](https://img.shields.io/travis/com/sergio-fry/devup/master)](https://travis-ci.com/github/sergio-fry/devup) [![Gem](https://img.shields.io/gem/v/devup)](https://rubygems.org/gems/devup) [![Code Climate coverage](https://img.shields.io/codeclimate/coverage/sergio-fry/devup)](https://codeclimate.com/github/sergio-fry/devup) [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/sergio-fry/devup)](https://codeclimate.com/github/sergio-fry/devup) [![Gem](https://img.shields.io/gem/dt/devup)](https://rubygems.org/gems/devup)
1
+ # DevUp! [![Travis (.com) branch](https://img.shields.io/travis/com/sergio-fry/devup/master)](https://travis-ci.com/github/sergio-fry/devup) [![Gem](https://img.shields.io/gem/v/devup)](https://rubygems.org/gems/devup) [![Code Climate coverage](https://img.shields.io/codeclimate/coverage/sergio-fry/devup)](https://codeclimate.com/github/sergio-fry/devup) [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/sergio-fry/devup)](https://codeclimate.com/github/sergio-fry/devup) [![Gem](https://img.shields.io/gem/dt/devup)](https://rubygems.org/gems/devup) [![Hits-of-Code](https://hitsofcode.com/github/sergio-fry/devup)](https://hitsofcode.com/view/github/sergio-fry/devup)
2
2
 
3
3
  Describe development dependencies with docker-compose. It is not required to remember any fancy command to start docker. Just start developing your app. Rails is a first-class citizen, but could be used without ruby.
4
4
 
@@ -32,7 +32,7 @@ gem "devup", group: [:development, :test]
32
32
 
33
33
  and
34
34
 
35
- bundle install
35
+ $ bundle install
36
36
 
37
37
 
38
38
  Update your database.yml to use ENV:
@@ -49,11 +49,11 @@ development:
49
49
 
50
50
  You are ready to use rails with PostgreSQL configured
51
51
 
52
- $ bundle exec rake db:create db:migrate
53
- Creating blog_postgres_1 ... done
54
- Created database 'blog_development'
52
+ $ RAILS_ENV=test bundle exec rake db:create
53
+ DevUp! INFO starting up...
54
+ DevUp! INFO up
55
55
 
56
- $ bundle exec rails server
56
+ $ Created database 'dummy_rails_test'
57
57
 
58
58
 
59
59
  ## Without Rails
@@ -71,12 +71,15 @@ DB = Sequel.connect(ENV.fetch("DATABASE_URL"))
71
71
 
72
72
  ## Without Ruby (PHP, nodejs, Java, ...)
73
73
 
74
+ Install DevUp!
75
+
76
+ $ gem install devup
77
+
74
78
  Start up services
75
79
 
76
- $ devup
77
- dummy_rails_postgres_1 is up-to-date
78
- dummy_rails_redis_1 is up-to-date
79
- dummy_rails_memcached_1 is up-to-date
80
+ $ devup up
81
+ DevUp! INFO starting up...
82
+ DevUp! INFO up
80
83
 
81
84
  $ cat .env.services
82
85
  export POSTGRES_HOST=0.0.0.0
@@ -100,6 +103,10 @@ Now you can run app
100
103
 
101
104
  If you don't want devup to setup your dev services, you can disable it by using `DEVUP_ENABLED=false`. Just add it to .env.local file.
102
105
 
106
+ ### Debug
107
+
108
+ $ export DEVUP_LOG_LEVEL=debug
109
+ $ devup up
103
110
 
104
111
  ## Development
105
112
 
@@ -32,4 +32,5 @@ Gem::Specification.new do |spec|
32
32
  spec.require_paths = ["lib"]
33
33
 
34
34
  spec.add_dependency "dotenv"
35
+ spec.add_dependency "dry-cli"
35
36
  end
@@ -0,0 +1,11 @@
1
+ version: '3'
2
+
3
+ services:
4
+ postgres:
5
+ image: postgres:11
6
+ ports:
7
+ - "5432"
8
+
9
+ web:
10
+ image: nginx
11
+
data/exe/devup CHANGED
@@ -1,13 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "devup/environment"
3
+ require "devup/cli"
4
4
 
5
- devup = Devup::Environment.new pwd: `pwd`
6
-
7
- # TODO: use dry-cli here
8
- case ARGV[0]
9
- when "down"
10
- devup.down
11
- else
12
- devup.up
13
- end
5
+ Dry::CLI.new(Devup::CLI::Commands).call
@@ -0,0 +1,7 @@
1
+ pre-commit:
2
+ parallel: true
3
+ commands:
4
+ lint:
5
+ files: git diff --name-only --staged
6
+ glob: "*.rb"
7
+ run: standardrb --force-exclusion {files}
@@ -1,5 +1,8 @@
1
1
  if ENV.fetch("DEVUP_ENABLED", "true") == "true"
2
- devup = Devup::Environment.new pwd: `pwd`
2
+ devup = Devup::Environment.new(
3
+ pwd: `pwd`,
4
+ logger: Devup::Logger.build(ENV.fetch("DEVUP_LOG_LEVEL", "info"))
5
+ )
3
6
  devup.up
4
7
 
5
8
  begin
@@ -0,0 +1,22 @@
1
+ require "dry/cli"
2
+
3
+ require "devup/cli/commands/up"
4
+ require "devup/cli/commands/down"
5
+
6
+ module Devup
7
+ module CLI
8
+ module Commands
9
+ extend Dry::CLI::Registry
10
+
11
+ class Version < Dry::CLI::Command
12
+ def call(*)
13
+ puts Devup::VERSION
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ Devup::CLI::Commands.register "version", Devup::CLI::Commands::Version, aliases: ["v", "-v", "--version"]
21
+ Devup::CLI::Commands.register "up", Devup::CLI::Commands::Up
22
+ Devup::CLI::Commands.register "down", Devup::CLI::Commands::Down
@@ -0,0 +1,38 @@
1
+ require "devup/logger"
2
+
3
+ module Devup
4
+ module CLI
5
+ module Commands
6
+ class Command < Dry::CLI::Command
7
+ def call(**options)
8
+ @opts = options
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :opts
14
+
15
+ def logger
16
+ @logger ||= Devup::Logger.build(log_level)
17
+ end
18
+
19
+ def log_level
20
+ if opts.fetch(:verbose)
21
+ :debug
22
+ else
23
+ :info
24
+ end
25
+ end
26
+
27
+ def devup
28
+ require "devup/environment"
29
+
30
+ @devup ||= Devup::Environment.new(
31
+ pwd: `pwd`,
32
+ logger: logger
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Devup
2
+ module CLI
3
+ module Commands
4
+ class Down < Command
5
+ desc "Stop dev services"
6
+
7
+ option :verbose, type: :boolean, default: false, desc: "Verbose"
8
+
9
+ def call(**options)
10
+ super
11
+
12
+ devup.down
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ require "devup/cli/commands/command"
2
+ module Devup
3
+ module CLI
4
+ module Commands
5
+ class Up < Command
6
+ desc "Run dev services"
7
+
8
+ option :verbose, type: :boolean, default: false, desc: "Verbose"
9
+
10
+ def call(**options)
11
+ super
12
+
13
+ devup.up
14
+ print_info
15
+ end
16
+
17
+ private
18
+
19
+ def print_info
20
+ puts <<~INFO
21
+
22
+ Now you are ready to use services. All variables are available
23
+ in a .env.services file. Just start you ruby application if
24
+ gem "devup" is used. Or load variable manually with
25
+
26
+ $ source .env.services
27
+
28
+ INFO
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,22 +1,22 @@
1
1
  require "yaml"
2
- require "open3"
2
+
3
+ require "devup/compose/ps"
3
4
 
4
5
  module Devup
5
6
  class Compose
6
- attr_reader :path, :project, :logger
7
+ attr_reader :path, :project, :logger, :shell
7
8
 
8
9
  class Error < StandardError; end
9
10
 
10
- def initialize(path, project: "devup", logger:)
11
+ def initialize(path, project: "devup", logger:, shell:)
11
12
  @path = path
12
13
  @project = project
13
14
  @logger = logger
15
+ @shell = shell
14
16
  end
15
17
 
16
18
  def check
17
- _output, status = safe_exec("docker-compose -v")
18
-
19
- status
19
+ true
20
20
  end
21
21
 
22
22
  def services
@@ -24,11 +24,19 @@ module Devup
24
24
  end
25
25
 
26
26
  def service_ports(name)
27
- config["services"][name]["ports"]
27
+ return [] if config["services"][name]["ports"].nil?
28
+
29
+ config["services"][name]["ports"].map { |el| el.to_s.split(":")[-1].to_i }
30
+ end
31
+
32
+ def port_mapping(port)
33
+ ComposeHelpers::Ps.new(exec_ps_cached).port_mapping(port)
28
34
  end
29
35
 
30
36
  def up
31
37
  exec "up -d --remove-orphans"
38
+
39
+ wait_alive 3
32
40
  end
33
41
 
34
42
  def stop
@@ -39,22 +47,40 @@ module Devup
39
47
  exec "rm -f"
40
48
  end
41
49
 
42
- def exec(cmd)
43
- output, status = safe_exec "docker-compose -p #{project} -f #{path} #{cmd}"
50
+ private
44
51
 
45
- raise(Error) unless status
52
+ def wait_alive(timeout, retry_sleep: 0.3)
53
+ start = Time.now
46
54
 
47
- output
55
+ loop {
56
+ break if alive?
57
+
58
+ if Time.now - start > timeout
59
+ logger.error "can't run services"
60
+ break
61
+ end
62
+ sleep retry_sleep
63
+ }
48
64
  end
49
65
 
50
- private
66
+ def alive?
67
+ ComposeHelpers::Ps.new(exec_ps).up?
68
+ end
69
+
70
+ def exec_ps
71
+ @exec_ps_output = exec("ps")
72
+ end
73
+
74
+ def exec_ps_cached
75
+ @exec_ps_output ||= exec("ps")
76
+ end
51
77
 
52
- def safe_exec(cmd)
53
- output, error, status = Open3.capture3(cmd + ";")
78
+ def exec(cmd)
79
+ resp = shell.exec "docker-compose -p #{project} -f #{path} #{cmd}"
54
80
 
55
- logger.error(error) unless status.success?
81
+ raise(Error) unless resp.success?
56
82
 
57
- [output, status.success?]
83
+ resp.data
58
84
  end
59
85
 
60
86
  def config
@@ -0,0 +1,30 @@
1
+ module Devup
2
+ module ComposeHelpers
3
+ class Ps
4
+ attr_reader :output
5
+ def initialize(output)
6
+ @output = output
7
+ end
8
+
9
+ def up?
10
+ service_lines.map { |line|
11
+ line.match(/Up/) && !line.match(/Exit/)
12
+ }.all?
13
+ end
14
+
15
+ def port_mapping(port)
16
+ m = output.match(/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}:(\d+)->#{port}\/tcp/)
17
+
18
+ return if m.nil?
19
+
20
+ m[1].to_i
21
+ end
22
+
23
+ private
24
+
25
+ def service_lines
26
+ output.split("\n")[2..-1]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,15 +4,17 @@ require "devup/logger"
4
4
  require "devup/compose"
5
5
  require "devup/service"
6
6
  require "devup/service_presenter"
7
+ require "devup/shell"
7
8
 
8
9
  module Devup
9
10
  class Environment
10
- attr_reader :pwd, :compose, :logger
11
+ attr_reader :pwd, :logger, :shell
11
12
 
12
- def initialize(pwd:, compose: nil, logger: Logger.default)
13
+ def initialize(pwd:, compose: nil, logger: Logger.build, shell: Shell.new(pwd: pwd, logger: logger))
13
14
  @pwd = pwd.to_s.strip
14
- @compose = compose || Compose.new(root.join("docker-compose.devup.yml"), project: project, logger: logger)
15
+ @compose = compose
15
16
  @logger = logger
17
+ @shell = shell
16
18
  end
17
19
 
18
20
  def project
@@ -24,24 +26,27 @@ module Devup
24
26
  end
25
27
 
26
28
  def up
27
- logger.info "DevUp! is starting up services..."
29
+ logger.info "starting up..."
28
30
  check
29
31
  compose.up
30
32
  write_dotenv
31
- logger.info "DevUp! is up"
32
- rescue
33
+ logger.info "up"
34
+ rescue => ex
33
35
  clear_dotenv
34
- logger.info "DevUp! halted"
36
+ logger.debug ex
37
+ logger.error "halted"
38
+ raise ex
35
39
  end
36
40
 
37
41
  def down
38
- logger.info "DevUp! is shutting down services..."
42
+ logger.info "shutting down..."
39
43
  compose.stop
40
44
  compose.rm
41
45
  clear_dotenv
42
- logger.info "DevUp! is down"
43
- rescue
44
- logger.info "DevUp! halted"
46
+ logger.info "down"
47
+ rescue => ex
48
+ logger.debug ex
49
+ logger.info "halted"
45
50
  end
46
51
 
47
52
  def root
@@ -51,11 +56,8 @@ module Devup
51
56
  private
52
57
 
53
58
  def check
54
- return false if missing_config
55
-
59
+ raise if missing_config
56
60
  compose.check
57
-
58
- true
59
61
  end
60
62
 
61
63
  def missing_config
@@ -98,5 +100,14 @@ module Devup
98
100
 
99
101
  DOTENV
100
102
  end
103
+
104
+ def compose
105
+ @compose ||= begin
106
+ Compose.new(
107
+ root.join("docker-compose.devup.yml"),
108
+ project: project, logger: logger, shell: shell
109
+ )
110
+ end
111
+ end
101
112
  end
102
113
  end
@@ -2,9 +2,13 @@ require "logger"
2
2
 
3
3
  module Devup
4
4
  class Logger < ::Logger
5
- def self.default
6
- formatter = ->(_severity, _time, _progname, msg) { "#{msg}\n" }
7
- new(STDOUT, formatter: formatter)
5
+ def self.build(level = :info)
6
+ formatter = ->(severity, _time, _progname, msg) { "DevUp! #{severity} #{msg}\n" }
7
+ logger = new(STDOUT, formatter: formatter)
8
+
9
+ logger.level = level
10
+
11
+ logger
8
12
  end
9
13
  end
10
14
  end
@@ -0,0 +1,16 @@
1
+ require "socket"
2
+ require "timeout"
3
+
4
+ module Devup
5
+ class PortChecker
6
+ def call(port)
7
+ s = TCPSocket.new("0.0.0.0", port)
8
+ s.write "1"
9
+ s.close
10
+
11
+ true
12
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
13
+ false
14
+ end
15
+ end
16
+ end
@@ -16,10 +16,10 @@ module Devup
16
16
  private
17
17
 
18
18
  def fetch_ports
19
- compose.service_ports(name).map { |el| el.to_s.split(":")[-1] }.map do |from|
19
+ compose.service_ports(name).map do |from|
20
20
  OpenStruct.new(
21
- from: from.to_i,
22
- to: compose.exec("port #{name} #{from}").split(":")[-1].strip.to_i
21
+ from: from,
22
+ to: compose.port_mapping(from)
23
23
  )
24
24
  end
25
25
  end
@@ -11,7 +11,6 @@ module Devup
11
11
  res = []
12
12
 
13
13
  res << "# #{service.name}"
14
- res << magic if magic?
15
14
 
16
15
  if has_ports?
17
16
  res << host_env
@@ -32,7 +31,7 @@ module Devup
32
31
  def ports_env
33
32
  res = []
34
33
 
35
- res << port_env(to: service.ports.first.to) if service.ports.size == 1
34
+ res << port_env(to: service.ports.first.to)
36
35
 
37
36
  service.ports.each do |port|
38
37
  res << port_env(from: port.from, to: port.to)
@@ -53,36 +52,12 @@ module Devup
53
52
  service.ports.size > 0
54
53
  end
55
54
 
56
- def magic?
57
- %w[postgres redis mysql memcached].include? service.name
58
- end
59
-
60
- def magic
61
- case service.name
62
- when "postgres"
63
- "export DATABASE_URL=postgres://postgres@0.0.0.0:#{port_to(5432)}/#{database_name}"
64
- when "mysql"
65
- "export DATABASE_URL=mysql2://root@0.0.0.0:#{port_to(3306)}/#{database_name}"
66
- when "redis"
67
- "export REDIS_URL=redis://0.0.0.0:#{port_to(6379)}"
68
- when "memcached"
69
- "export MEMCACHE_SERVERS=0.0.0.0:#{port_to(11211)}"
70
- end
71
- end
72
-
73
55
  def port_to(from)
74
56
  service.ports.find { |p| p.from == from }&.to
75
57
  end
76
58
 
77
59
  def database_name
78
- if defined? Rails
79
- [
80
- project,
81
- Rails.env
82
- ].join("_")
83
- else
84
- "db"
85
- end
60
+ "db"
86
61
  end
87
62
  end
88
63
  end
@@ -0,0 +1,28 @@
1
+ require "open3"
2
+
3
+ module Devup
4
+ class Shell
5
+ attr_reader :pwd, :logger
6
+
7
+ def initialize(pwd:, logger:)
8
+ @pwd = pwd
9
+ @logger = logger
10
+ end
11
+
12
+ Result = Struct.new(:data, :status) {
13
+ def success?
14
+ status
15
+ end
16
+ }
17
+
18
+ def exec(cmd)
19
+ logger.debug "$ #{cmd}"
20
+
21
+ output, error, status = Open3.capture3(cmd + ";")
22
+
23
+ logger.error(error) unless status.success?
24
+
25
+ Result.new(output, status.success?)
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Devup
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.3.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergei O. Udalov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-09 00:00:00.000000000 Z
11
+ date: 2020-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-cli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description:
28
42
  email:
29
43
  - sergei@udalovs.ru
@@ -47,15 +61,24 @@ files:
47
61
  - bin/console
48
62
  - bin/setup
49
63
  - devup.gemspec
64
+ - docker-compose.devup.yml
50
65
  - exe/devup
66
+ - lefthook.yml
51
67
  - lib/devup.rb
52
68
  - lib/devup/boot.rb
69
+ - lib/devup/cli.rb
70
+ - lib/devup/cli/commands/command.rb
71
+ - lib/devup/cli/commands/down.rb
72
+ - lib/devup/cli/commands/up.rb
53
73
  - lib/devup/compose.rb
74
+ - lib/devup/compose/ps.rb
54
75
  - lib/devup/dotenv.rb
55
76
  - lib/devup/environment.rb
56
77
  - lib/devup/logger.rb
78
+ - lib/devup/port_checker.rb
57
79
  - lib/devup/service.rb
58
80
  - lib/devup/service_presenter.rb
81
+ - lib/devup/shell.rb
59
82
  - lib/devup/version.rb
60
83
  homepage: https://github.com/sergio-fry/devup
61
84
  licenses: