devup 0.1.1 → 0.3.2.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: af85549b1c01b89de550c6c7857c23d1b80cc06997e3fd6ceee8709e8587f85c
4
- data.tar.gz: 8523baac5e3d05d235feb677a66857622a97cf3432f62f9726144d86b8fa01f6
3
+ metadata.gz: 3e2a14e12cc9c1661134d64021d14f6553df35ab4021c85b3512103d1171503e
4
+ data.tar.gz: 9a63920fc33cbe3f0d312ef5dd4bd3e1b220618435ac1dab117e87aab0362267
5
5
  SHA512:
6
- metadata.gz: f3c80f5bbbed5b595e58eeb2b7089c2e5743f62fa629b850c98f559be401a5c77767b3485a3140b69ffb9158473f6dc2993919cdc2e416711433772811a4553d
7
- data.tar.gz: 15e5cc91441dc2eb5b251d25fd72d4dad66c2fa6ce43744c66ec30a518c7e5791504c70c91f059fd9211cc98dd198e0c69f88d94452b5037e77f6af8b3c0945d
6
+ metadata.gz: 8617036ce5f17bcae494dde0f333faa65c355b71fe2d1b360d9a1f6c41d09762340fe6ca4c8050f547983115e7849f87aa8818c76c396aed9ce430a0f354721f
7
+ data.tar.gz: c016d3af0f75c8c64849429ea68e3d4a156ff75cc729a286b123f78fa96d3e5c574193aa78b27e2600a9edd0bc8c883bdd3021aa27e61c14965ef2794b1364d1
data/.gitignore CHANGED
@@ -15,5 +15,6 @@
15
15
  .vimrc
16
16
 
17
17
  spec/dummy/.env.services
18
+ spec/dummy_rails/.env.services
18
19
 
19
20
  devup-*.gem
@@ -0,0 +1,2 @@
1
+ ignore:
2
+ - 'spec/dummy_rails/**/*'
@@ -1,11 +1,14 @@
1
+ env:
2
+ matrix:
3
+ - BUNDLER_VERSION=2.1.4
1
4
  language: ruby
2
5
  cache: bundler
3
6
  rvm:
4
7
  - 2.7.1
8
+ - 2.6.6
9
+ - 2.5.8
5
10
  services:
6
11
  - docker
7
- env:
8
- - BUNDLER_VERSION=2.1.4
9
12
  before_install:
10
13
  - gem install bundler -v $BUNDLER_VERSION
11
14
  bundler_args: "--jobs=3 --retry=3"
@@ -18,3 +21,21 @@ deploy:
18
21
  tags: true
19
22
  repo: sergio-fry/devup
20
23
  skip_cleanup: 'true'
24
+
25
+ jobs:
26
+ include:
27
+ - stage: test
28
+ name: RSpec
29
+ before_script:
30
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
31
+ > ./cc-test-reporter
32
+ - chmod +x ./cc-test-reporter
33
+ - "./cc-test-reporter before-build"
34
+ script:
35
+ - bundle exec rake spec
36
+ after_script:
37
+ - "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
38
+ - stage: test
39
+ name: Lint
40
+ script:
41
+ - bundle exec standardrb
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- devup (0.1.1)
4
+ devup (0.3.2.1)
5
+ dotenv
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
@@ -10,6 +11,7 @@ GEM
10
11
  byebug (11.1.3)
11
12
  diff-lcs (1.3)
12
13
  docile (1.3.2)
14
+ dotenv (2.7.5)
13
15
  jaro_winkler (1.5.4)
14
16
  json (2.3.0)
15
17
  parallel (1.19.1)
@@ -42,7 +44,7 @@ GEM
42
44
  rubocop-performance (1.5.2)
43
45
  rubocop (>= 0.71.0)
44
46
  ruby-progressbar (1.10.1)
45
- simplecov (0.17.1)
47
+ simplecov (0.16.1)
46
48
  docile (~> 1.1)
47
49
  json (>= 1.8, < 3)
48
50
  simplecov-html (~> 0.10.0)
data/README.md CHANGED
@@ -1,28 +1,105 @@
1
- # 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)
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/devup`. To experiment with that code, run `bin/console` for an interactive prompt.
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
 
5
- TODO: Delete this and the text above, and describe your gem
6
5
 
7
- ## Installation
6
+ ## Requirements
8
7
 
9
- Add this line to your application's Gemfile:
8
+ * Docker (>= 19.03.8)
9
+ * Docker Compose (>= 1.25.5)
10
+
11
+
12
+
13
+ ## Rails
14
+
15
+ Create a docker-compose.devup.yml with app dependencies like:
16
+
17
+ ```yaml
18
+ version: "3"
19
+
20
+ services:
21
+ postgres:
22
+ image: postgres:10-alpine
23
+ ports:
24
+ - "5432"
25
+ ```
26
+
27
+ Add **DevUp!** to your Gemfile
10
28
 
11
29
  ```ruby
12
- gem 'devup'
30
+ gem "devup", group: [:development, :test]
31
+ ```
32
+
33
+ and
34
+
35
+ bundle install
36
+
37
+
38
+ Update your database.yml to use ENV:
39
+
40
+ ```yaml
41
+ test:
42
+ url: <%= ENV.fetch("DATABASE_URL") %>
43
+
44
+ development:
45
+ url: <%= ENV.fetch("DATABASE_URL") %>
46
+
13
47
  ```
14
48
 
15
- And then execute:
16
49
 
17
- $ bundle install
50
+ You are ready to use rails with PostgreSQL configured
51
+
52
+ $ bundle exec rake db:create db:migrate
53
+ Creating blog_postgres_1 ... done
54
+ Created database 'blog_development'
55
+
56
+ $ bundle exec rails server
57
+
58
+
59
+ ## Without Rails
60
+
61
+ ENV vars from .env.services are loaded with dotenv automatically.
62
+
63
+
64
+ ```ruby
65
+ require "devup"
66
+ require "sequel"
67
+
68
+ DB = Sequel.connect(ENV.fetch("DATABASE_URL"))
69
+ ```
70
+
71
+
72
+ ## Without Ruby (PHP, nodejs, Java, ...)
73
+
74
+ Start up services
75
+
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
+
81
+ $ cat .env.services
82
+ export POSTGRES_HOST=0.0.0.0
83
+ export POSTGRES_PORT=32944
84
+ export POSTGRES_PORT_5432=32944
85
+ export MEMCACHED_HOST=0.0.0.0
86
+ export MEMCACHED_PORT=32943
87
+ ...
88
+
89
+ Use your favourite dotenv extension to load vars from .env.services ([node-dotenv](https://www.npmjs.com/package/dotenv), [python-dotenv](https://pypi.org/project/python-dotenv/), [phpdotenv](https://github.com/vlucas/phpdotenv), ...)
90
+
91
+ Or load ENV vars manually
92
+
93
+ $ source .env.services
94
+
95
+ Now you can run app
18
96
 
19
- Or install it yourself as:
97
+ ## How To
20
98
 
21
- $ gem install devup
99
+ ### Disable **DevUp!**
22
100
 
23
- ## Usage
101
+ 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.
24
102
 
25
- TODO: Write usage instructions here
26
103
 
27
104
  ## Development
28
105
 
@@ -30,4 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.bindir = "exe"
31
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
32
  spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "dotenv"
33
35
  end
data/exe/devup CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "devup"
3
+ require "devup/environment"
4
4
 
5
5
  devup = Devup::Environment.new pwd: `pwd`
6
6
 
@@ -3,9 +3,7 @@ require "devup/environment"
3
3
 
4
4
  module Devup
5
5
  class Error < StandardError; end
6
- # Your code goes here...
7
-
8
- def self.root
9
- Pathname.new File.dirname(__dir__)
10
- end
11
6
  end
7
+
8
+ require "devup/boot"
9
+ require "devup/dotenv"
@@ -0,0 +1,12 @@
1
+ if ENV.fetch("DEVUP_ENABLED", "true") == "true"
2
+ devup = Devup::Environment.new pwd: `pwd`
3
+ devup.up
4
+
5
+ begin
6
+ require "spring/commands"
7
+
8
+ Spring.watch devup.root.join("docker-compose.yml")
9
+ rescue LoadError, ArgumentError
10
+ # Spring is not available
11
+ end
12
+ end
@@ -1,12 +1,22 @@
1
1
  require "yaml"
2
+ require "open3"
2
3
 
3
4
  module Devup
4
5
  class Compose
5
- attr_reader :path, :project
6
+ attr_reader :path, :project, :logger
6
7
 
7
- def initialize(path, project: "devup")
8
+ class Error < StandardError; end
9
+
10
+ def initialize(path, project: "devup", logger:)
8
11
  @path = path
9
12
  @project = project
13
+ @logger = logger
14
+ end
15
+
16
+ def check
17
+ _output, status = safe_exec("docker-compose -v")
18
+
19
+ status
10
20
  end
11
21
 
12
22
  def services
@@ -30,11 +40,23 @@ module Devup
30
40
  end
31
41
 
32
42
  def exec(cmd)
33
- `docker-compose -p #{project} -f #{path} #{cmd}`
43
+ output, status = safe_exec "docker-compose -p #{project} -f #{path} #{cmd}"
44
+
45
+ raise(Error) unless status
46
+
47
+ output
34
48
  end
35
49
 
36
50
  private
37
51
 
52
+ def safe_exec(cmd)
53
+ output, error, status = Open3.capture3(cmd + ";")
54
+
55
+ logger.error(error) unless status.success?
56
+
57
+ [output, status.success?]
58
+ end
59
+
38
60
  def config
39
61
  YAML.safe_load(config_content)
40
62
  end
@@ -0,0 +1,2 @@
1
+ require "dotenv"
2
+ Dotenv.load ".env.services"
@@ -1,48 +1,47 @@
1
1
  require "yaml"
2
2
 
3
+ require "devup/logger"
3
4
  require "devup/compose"
4
5
  require "devup/service"
6
+ require "devup/service_presenter"
5
7
 
6
8
  module Devup
7
9
  class Environment
8
- attr_reader :pwd, :compose
10
+ attr_reader :pwd, :compose, :logger
9
11
 
10
- def initialize(pwd:, compose: nil)
12
+ def initialize(pwd:, compose: nil, logger: Logger.default)
11
13
  @pwd = pwd.to_s.strip
12
- @compose = compose || Compose.new(root.join("docker-compose.yml"), project: project)
14
+ @compose = compose || Compose.new(root.join("docker-compose.devup.yml"), project: project, logger: logger)
15
+ @logger = logger
13
16
  end
14
17
 
15
18
  def project
16
19
  pwd.split("/")[-1].strip
17
20
  end
18
21
 
19
- def vars
20
- compose.services.map { |name| Service.new(compose, name) }.map { |service|
21
- res = []
22
-
23
- res << {"#{service.name}_HOST".upcase => "0.0.0.0"}
24
-
25
- if service.ports.size > 0
26
- res << {"#{service.name}_PORT".upcase => service.ports.first.to}
27
-
28
- service.ports.each do |port|
29
- res << {"#{service.name}_PORT_#{port.from}".upcase => port.to}
30
- end
31
- end
32
-
33
- res
34
- }.flatten
22
+ def env
23
+ services.map { |s| service_env(s) }.join("\n\n")
35
24
  end
36
25
 
37
26
  def up
27
+ logger.info "DevUp! is starting up services..."
28
+ check
38
29
  compose.up
39
30
  write_dotenv
31
+ logger.info "DevUp! is up"
32
+ rescue
33
+ clear_dotenv
34
+ logger.info "DevUp! halted"
40
35
  end
41
36
 
42
37
  def down
38
+ logger.info "DevUp! is shutting down services..."
43
39
  compose.stop
44
40
  compose.rm
45
41
  clear_dotenv
42
+ logger.info "DevUp! is down"
43
+ rescue
44
+ logger.info "DevUp! halted"
46
45
  end
47
46
 
48
47
  def root
@@ -51,6 +50,32 @@ module Devup
51
50
 
52
51
  private
53
52
 
53
+ def check
54
+ return false if missing_config
55
+
56
+ compose.check
57
+
58
+ true
59
+ end
60
+
61
+ def missing_config
62
+ if File.exist?(compose.path)
63
+ false
64
+ else
65
+ logger.error "missing #{compose.path}"
66
+
67
+ true
68
+ end
69
+ end
70
+
71
+ def services
72
+ @services ||= compose.services.map { |name| Service.new(compose, name) }
73
+ end
74
+
75
+ def service_env(service)
76
+ ServicePresenter.new(service, project: project).call
77
+ end
78
+
54
79
  def write_dotenv
55
80
  File.open(root.join(".env.services"), "w") { |f| f.write dotenv }
56
81
  end
@@ -61,11 +86,14 @@ module Devup
61
86
 
62
87
  def dotenv
63
88
  <<~DOTENV
64
- ############################################
65
- # This file is generated by devup command. #
66
- ############################################
89
+ ####################################################
90
+ # This file is generated by devup command. #
91
+ # Home: https://github.com/sergio-fry/devup #
92
+ ####################################################
67
93
  # START
68
- #{vars.reduce({}, :merge).map { |k, v| "#{k}=#{v}" }.join("\n")}
94
+
95
+ #{env}
96
+
69
97
  # END
70
98
 
71
99
  DOTENV
@@ -0,0 +1,10 @@
1
+ require "logger"
2
+
3
+ module Devup
4
+ class Logger < ::Logger
5
+ def self.default
6
+ formatter = ->(_severity, _time, _progname, msg) { "#{msg}\n" }
7
+ new(STDOUT, formatter: formatter)
8
+ end
9
+ end
10
+ end
@@ -10,10 +10,16 @@ module Devup
10
10
  end
11
11
 
12
12
  def ports
13
+ @ports ||= fetch_ports
14
+ end
15
+
16
+ private
17
+
18
+ def fetch_ports
13
19
  compose.service_ports(name).map { |el| el.to_s.split(":")[-1] }.map do |from|
14
20
  OpenStruct.new(
15
- from: from,
16
- to: compose.exec("port #{name} #{from}").split(":")[-1].strip
21
+ from: from.to_i,
22
+ to: compose.exec("port #{name} #{from}").split(":")[-1].strip.to_i
17
23
  )
18
24
  end
19
25
  end
@@ -0,0 +1,88 @@
1
+ module Devup
2
+ class ServicePresenter
3
+ attr_reader :service, :project
4
+
5
+ def initialize(service, project: nil)
6
+ @service = service
7
+ @project = project
8
+ end
9
+
10
+ def call
11
+ res = []
12
+
13
+ res << "# #{service.name}"
14
+ res << magic if magic?
15
+
16
+ if has_ports?
17
+ res << host_env
18
+ res << ports_env
19
+ else
20
+ res << "# no exposed ports"
21
+ end
22
+
23
+ res.join "\n"
24
+ end
25
+
26
+ private
27
+
28
+ def host_env
29
+ "export #{service.name.upcase}_HOST=0.0.0.0"
30
+ end
31
+
32
+ def ports_env
33
+ res = []
34
+
35
+ res << port_env(to: service.ports.first.to) if service.ports.size == 1
36
+
37
+ service.ports.each do |port|
38
+ res << port_env(from: port.from, to: port.to)
39
+ end
40
+
41
+ res.join "\n"
42
+ end
43
+
44
+ def port_env(from: nil, to:)
45
+ if from.nil?
46
+ "export #{service.name.upcase}_PORT=#{to}"
47
+ else
48
+ "export #{service.name.upcase}_PORT_#{from}=#{to}"
49
+ end
50
+ end
51
+
52
+ def has_ports?
53
+ service.ports.size > 0
54
+ end
55
+
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
+ def port_to(from)
74
+ service.ports.find { |p| p.from == from }&.to
75
+ end
76
+
77
+ def database_name
78
+ if defined? Rails
79
+ [
80
+ project,
81
+ Rails.env
82
+ ].join("_")
83
+ else
84
+ "db"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,3 +1,3 @@
1
1
  module Devup
2
- VERSION = "0.1.1"
2
+ VERSION = "0.3.2.1"
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.2.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-03 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-06-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dotenv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description:
14
28
  email:
15
29
  - sergei@udalovs.ru
@@ -21,6 +35,7 @@ files:
21
35
  - ".gitignore"
22
36
  - ".rspec"
23
37
  - ".ruby-version"
38
+ - ".standard.yml"
24
39
  - ".travis.yml"
25
40
  - ".vimrc"
26
41
  - CODE_OF_CONDUCT.md
@@ -34,9 +49,13 @@ files:
34
49
  - devup.gemspec
35
50
  - exe/devup
36
51
  - lib/devup.rb
52
+ - lib/devup/boot.rb
37
53
  - lib/devup/compose.rb
54
+ - lib/devup/dotenv.rb
38
55
  - lib/devup/environment.rb
56
+ - lib/devup/logger.rb
39
57
  - lib/devup/service.rb
58
+ - lib/devup/service_presenter.rb
40
59
  - lib/devup/version.rb
41
60
  homepage: https://github.com/sergio-fry/devup
42
61
  licenses: