devup 0.3.0 → 0.4.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
  SHA256:
3
- metadata.gz: f3111015b0f138014ede5086a6df15c4e3e4f1b30d1bd7a199ec16a69b7eb84e
4
- data.tar.gz: afab2b2376b8137be5bbbc2a3f85d3464720f63312d1a7cefbc948abbf07168d
3
+ metadata.gz: 99a6b3579f7d44d86a42c61b5d5b55dcd32e8d7aac580c49e5bce9a95500ff02
4
+ data.tar.gz: 2a73831a4d8d1549d6150050c12d4806ab8cc41089cbb9ce2b81420d09b02c8a
5
5
  SHA512:
6
- metadata.gz: 58a825f460fce341d66aa22e3b6c257c920908967a1ae64a207f274ebf051144cfba56a19e526e8555c5061d4f2bc37b14ae86bafc3f03a35315352d78aa98ee
7
- data.tar.gz: c6897be6a6378476b21e65a0a7d59402641658a400bbc20b655f234d19ab5144e5d23b2bdd764558248519c63fdfc81eee7951c17bcadd779d4c2104377df091
6
+ metadata.gz: 709e5c38a2786273085eba92ab0fa70f70dd8fbbfe1200b631ab1bd527b072df097fe6b856bab1e57a5790952dd2476665bcca965e302a69fa8ea2d47cb0d2a4
7
+ data.tar.gz: 5c81a935db87d41f22be697fcce631d9f80262f155bbc5eac146c2782e8d1ff49a4a805a86ad0b2c14b3d91e118807c80e0f857ba8584927a1743eab3fd7d87a
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.0)
4
+ devup (0.4.0)
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(service, port)
33
+ exec("port #{service} #{port}").split(":")[-1].strip.to_i
28
34
  end
29
35
 
30
36
  def up
31
- exec "up -d --remove-orphans --renew-anon-volumes"
37
+ exec "up -d --remove-orphans"
38
+
39
+ wait_alive 3
32
40
  end
33
41
 
34
42
  def stop
@@ -39,22 +47,32 @@ 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
51
+
52
+ def wait_alive(timeout, retry_sleep: 0.3)
53
+ start = Time.now
44
54
 
45
- raise(Error) unless status
55
+ loop {
56
+ break if alive?
46
57
 
47
- output
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
51
69
 
52
- def safe_exec(cmd)
53
- output, error, status = Open3.capture3(cmd + ";")
70
+ def exec(cmd)
71
+ resp = shell.exec "docker-compose -p #{project} -f #{path} #{cmd}"
54
72
 
55
- logger.error(error) unless status.success?
73
+ raise(Error) unless resp.success?
56
74
 
57
- [output, status.success?]
75
+ resp.data
58
76
  end
59
77
 
60
78
  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,26 +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"
33
+ logger.info "up"
34
+ rescue => ex
32
35
  clear_dotenv
33
-
34
- rescue StandardError
35
- logger.info "DevUp! halted"
36
+ logger.debug ex
37
+ logger.error "halted"
38
+ raise ex
36
39
  end
37
40
 
38
41
  def down
39
- logger.info "DevUp! is shutting down services..."
42
+ logger.info "shutting down..."
40
43
  compose.stop
41
44
  compose.rm
42
45
  clear_dotenv
43
- logger.info "DevUp! is down"
44
-
45
- rescue StandardError
46
- logger.info "DevUp! halted"
46
+ logger.info "down"
47
+ rescue => ex
48
+ logger.debug ex
49
+ logger.info "halted"
47
50
  end
48
51
 
49
52
  def root
@@ -53,11 +56,8 @@ module Devup
53
56
  private
54
57
 
55
58
  def check
56
- return false if missing_config
57
-
59
+ raise if missing_config
58
60
  compose.check
59
-
60
- true
61
61
  end
62
62
 
63
63
  def missing_config
@@ -100,5 +100,14 @@ module Devup
100
100
 
101
101
  DOTENV
102
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
103
112
  end
104
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(name, 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
@@ -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.0"
2
+ VERSION = "0.4.0"
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.0
4
+ version: 0.4.0
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-06-23 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: