hanami-cli 2.1.1 → 2.2.0.beta2

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +29 -13
  3. data/.rubocop.yml +2 -0
  4. data/CHANGELOG.md +28 -0
  5. data/Gemfile +6 -1
  6. data/README.md +13 -7
  7. data/docker-compose.yml +14 -0
  8. data/hanami-cli.gemspec +2 -2
  9. data/lib/hanami/cli/command.rb +2 -2
  10. data/lib/hanami/cli/commands/app/command.rb +2 -16
  11. data/lib/hanami/cli/commands/app/db/command.rb +148 -0
  12. data/lib/hanami/cli/commands/app/db/create.rb +21 -11
  13. data/lib/hanami/cli/commands/app/db/drop.rb +21 -10
  14. data/lib/hanami/cli/commands/app/db/migrate.rb +50 -12
  15. data/lib/hanami/cli/commands/app/db/prepare.rb +68 -0
  16. data/lib/hanami/cli/commands/app/db/seed.rb +22 -21
  17. data/lib/hanami/cli/commands/app/db/structure/dump.rb +32 -7
  18. data/lib/hanami/cli/commands/app/db/structure/load.rb +54 -0
  19. data/lib/hanami/cli/commands/app/db/utils/database.rb +100 -75
  20. data/lib/hanami/cli/commands/app/db/utils/mysql.rb +59 -6
  21. data/lib/hanami/cli/commands/app/db/utils/postgres.rb +42 -21
  22. data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +58 -10
  23. data/lib/hanami/cli/commands/app/db/version.rb +14 -8
  24. data/lib/hanami/cli/commands/app/generate/action.rb +4 -3
  25. data/lib/hanami/cli/commands/app/generate/command.rb +60 -0
  26. data/lib/hanami/cli/commands/app/generate/component.rb +49 -0
  27. data/lib/hanami/cli/commands/app/generate/migration.rb +47 -0
  28. data/lib/hanami/cli/commands/app/generate/operation.rb +26 -0
  29. data/lib/hanami/cli/commands/app/generate/part.rb +1 -1
  30. data/lib/hanami/cli/commands/app/generate/relation.rb +29 -0
  31. data/lib/hanami/cli/commands/app/generate/repo.rb +42 -0
  32. data/lib/hanami/cli/commands/app/generate/slice.rb +20 -3
  33. data/lib/hanami/cli/commands/app/generate/struct.rb +27 -0
  34. data/lib/hanami/cli/commands/app/install.rb +1 -1
  35. data/lib/hanami/cli/commands/app/middleware.rb +1 -1
  36. data/lib/hanami/cli/commands/app/server.rb +2 -2
  37. data/lib/hanami/cli/commands/app.rb +21 -2
  38. data/lib/hanami/cli/commands/gem/new.rb +78 -14
  39. data/lib/hanami/cli/errors.rb +28 -0
  40. data/lib/hanami/cli/files.rb +22 -0
  41. data/lib/hanami/cli/generators/app/action_context.rb +5 -13
  42. data/lib/hanami/cli/generators/app/component/component.erb +8 -0
  43. data/lib/hanami/cli/generators/app/component/slice_component.erb +8 -0
  44. data/lib/hanami/cli/generators/app/component.rb +61 -0
  45. data/lib/hanami/cli/generators/app/component_context.rb +82 -0
  46. data/lib/hanami/cli/generators/app/migration.rb +66 -0
  47. data/lib/hanami/cli/generators/app/operation.rb +49 -0
  48. data/lib/hanami/cli/generators/app/part_context.rb +5 -21
  49. data/lib/hanami/cli/generators/app/relation.rb +45 -0
  50. data/lib/hanami/cli/generators/app/repo.rb +41 -0
  51. data/lib/hanami/cli/generators/app/ruby_file_writer.rb +151 -0
  52. data/lib/hanami/cli/generators/app/slice/{entities.erb → operation.erb} +1 -3
  53. data/lib/hanami/cli/generators/app/slice/relation.erb +8 -0
  54. data/lib/hanami/cli/generators/app/slice/{slice.erb → repo.erb} +3 -1
  55. data/lib/hanami/cli/generators/app/slice/struct.erb +8 -0
  56. data/lib/hanami/cli/generators/app/slice.rb +14 -6
  57. data/lib/hanami/cli/generators/app/slice_context.rb +9 -2
  58. data/lib/hanami/cli/generators/app/struct.rb +40 -0
  59. data/lib/hanami/cli/generators/app/view_context.rb +4 -16
  60. data/lib/hanami/cli/generators/constants.rb +39 -0
  61. data/lib/hanami/cli/generators/context.rb +48 -0
  62. data/lib/hanami/cli/generators/gem/app/action.erb +3 -0
  63. data/lib/hanami/cli/generators/gem/app/env.erb +4 -0
  64. data/lib/hanami/cli/generators/gem/app/gemfile.erb +11 -0
  65. data/lib/hanami/cli/generators/gem/app/gitignore.erb +4 -1
  66. data/lib/hanami/cli/generators/gem/app/operation.erb +13 -0
  67. data/lib/hanami/cli/generators/gem/app/relation.erb +10 -0
  68. data/lib/hanami/cli/generators/gem/app/repo.erb +10 -0
  69. data/lib/hanami/cli/generators/gem/app/struct.erb +10 -0
  70. data/lib/hanami/cli/generators/gem/app.rb +19 -0
  71. data/lib/hanami/cli/ruby_file_generator.rb +123 -0
  72. data/lib/hanami/cli/version.rb +1 -1
  73. metadata +39 -17
  74. data/lib/hanami/cli/commands/app/db/create_migration.rb +0 -32
  75. data/lib/hanami/cli/commands/app/db/reset.rb +0 -28
  76. data/lib/hanami/cli/commands/app/db/rollback.rb +0 -81
  77. data/lib/hanami/cli/commands/app/db/sample_data.rb +0 -42
  78. data/lib/hanami/cli/commands/app/db/setup.rb +0 -26
  79. data/lib/hanami/cli/commands/app/db/utils/database_config.rb +0 -60
  80. data/lib/hanami/cli/generators/app/slice/repository.erb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc90cef87d08c779e3936a2431b93b647f43242c43cea6f85dc3fb22269c4647
4
- data.tar.gz: 9eb4424fbdb88f535712951deaddc08874e76085b42fb8fd6d2715a3c3874af8
3
+ metadata.gz: d62ed9aeff86f86693ffdf5acafb274e6e97a535a177bf46131c8c33141fc0e9
4
+ data.tar.gz: 86da7248a20712451a83b8e5167870e34fd508f2e5f41a107d9bf3a4dea1d376
5
5
  SHA512:
6
- metadata.gz: b081dcb657e85809a9cd58e02ceeb4de28253e48e41ee030cae580c6ebb58a947255214a535bd9cf7f1061f7ee685749c8804da2e2ac2c8f203f81487ac88753
7
- data.tar.gz: c524a51060f38143552ff8601511856348b88124f3597d55d4043a54791e356a45ac45ffbbd268b14f0a04f87caf7baa63d7d56750a6b7299c0f6e34d469216e
6
+ metadata.gz: affe042dfe0daf2dc93e1252b06d2d51dd5ea605c1287ad2500371bed6ee98e902cf9d19667458f86edadabb671f923b986bcd2a76a0e7d8821f4e0b9af6ecf6
7
+ data.tar.gz: b1a097c66c10495ef31fd0783083d542ac6e7a5be7ab94f11b9e1a0d68dc865e04d6f970e08b88d33dd3939b47736cc8893989cb037d070d3c5d2ff86d35ccad
@@ -27,15 +27,9 @@ jobs:
27
27
  - "3.3"
28
28
  - "3.2"
29
29
  - "3.1"
30
- - "3.0"
30
+ env:
31
+ POSTGRES_BASE_URL: postgres://postgres:password@localhost:5432/hanami_cli_test
31
32
  steps:
32
- - uses: ravsamhq/notify-slack-action@v1
33
- if: always()
34
- with:
35
- status: ${{ job.status }}
36
- notify_when: "failure"
37
- env:
38
- SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
39
33
  - uses: actions/checkout@v1
40
34
  - name: Install package dependencies
41
35
  run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
@@ -43,10 +37,32 @@ jobs:
43
37
  uses: ruby/setup-ruby@v1
44
38
  with:
45
39
  ruby-version: ${{matrix.ruby}}
46
- - name: Install latest bundler
47
- run: |
48
- gem install bundler --no-document
49
- - name: Bundle install
50
- run: bundle install --jobs 4 --retry 3
40
+ bundler-cache: true
51
41
  - name: Run all tests
52
42
  run: bundle exec rake spec
43
+ services:
44
+ mysql:
45
+ image: mysql:latest
46
+ env:
47
+ MYSQL_ROOT_PASSWORD: password
48
+ ports:
49
+ - 3307:3306
50
+ options: >-
51
+ --health-cmd "mysqladmin ping"
52
+ --health-interval 10s
53
+ --health-timeout 5s
54
+ --health-retries 3
55
+ postgres:
56
+ # Use postgres:14 for CLI compatibility with ubuntu-latest, currently ubuntu-22.04
57
+ # See https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md
58
+ image: postgres:14
59
+ env:
60
+ POSTGRES_USER: postgres
61
+ POSTGRES_PASSWORD: password
62
+ ports:
63
+ - 5432:5432
64
+ options: >-
65
+ --health-cmd pg_isready
66
+ --health-interval 10s
67
+ --health-timeout 5s
68
+ --health-retries 5
data/.rubocop.yml CHANGED
@@ -42,3 +42,5 @@ Style/TrailingCommaInHashLiteral:
42
42
  Enabled: false
43
43
  Style/StringConcatenation:
44
44
  Enabled: false
45
+ Style/ZeroLengthPredicate:
46
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  Hanami Command Line Interface
4
4
 
5
+ ## v2.2.0.beta2 - 2024-09-25
6
+
7
+ ### Added
8
+
9
+ - [Tim Riley] MySQL support for `db` commands (#226)
10
+ - [Tim Riley] Support for multiple gateways in `db` commands (#232, #234, #237, #238)
11
+
12
+ ### Changed
13
+
14
+ - [Kyle Plump, Tim Riley] Delete `.keep` files when generating new files into previously empty directory (#224)
15
+ - [Sean Collins] Add `db/*.sqlite` to the `.gitignore` in new apps (#210)
16
+ - [Sean Collins] Print warnings for misconfigured databases when running `db` commands (#211)
17
+
18
+ ## v2.2.0.beta1 - 2024-07-16
19
+
20
+ ### Added
21
+
22
+ - [Sean Collins] Generate db files in `hanami new` and `generate slice`
23
+ - [Tim Riley] Add `db` commands: `create`, `drop`, `migrate`, `structure dump` `structure load`, `seed` `prepare`, `version`
24
+ - [Tim Riley] Support SQLite and Postgres for `db` commands
25
+ - [Sean Collins] Add `generate` commands for db components: `generate migration`, `generate relation`, `generate repo`, `generate struct`
26
+ - [Krzysztof] Add `generate component` command
27
+ - [Sean Collins] Add `generate operation` command
28
+
29
+ ### Changed
30
+
31
+ - Drop support for Ruby 3.0
32
+
5
33
  ## v2.1.1 - 2024-03-19
6
34
 
7
35
  ### Fixed
data/Gemfile CHANGED
@@ -12,13 +12,18 @@ end
12
12
  gem "hanami", github: "hanami/hanami", branch: "main"
13
13
  gem "hanami-assets", github: "hanami/assets", branch: "main"
14
14
  gem "hanami-controller", github: "hanami/controller", branch: "main"
15
+ gem "hanami-db", github: "hanami/db", branch: "main"
15
16
  gem "hanami-router", github: "hanami/router", branch: "main"
16
17
  gem "hanami-utils", github: "hanami/utils", branch: "main"
17
18
 
18
- gem "dry-files", github: "dry-rb/dry-files", branch: "main"
19
+ gem "dry-system", github: "dry-rb/dry-system", branch: "main"
19
20
 
20
21
  gem "rack"
21
22
 
23
+ gem "mysql2"
24
+ gem "pg"
25
+ gem "sqlite3"
26
+
22
27
  gem "hanami-devtools", github: "hanami/devtools", branch: "main"
23
28
 
24
29
  group :test do
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # Hanami::CLI
2
2
 
3
- CLI commands for [full-stack Hanami applications](`https://github.com/hanami/hanami`).
3
+ This library contains all of the CLI commands for [full-stack Hanami applications](`https://github.com/hanami/hanami`).
4
4
 
5
- **NOTE**: For versions 0.4 and below, there was a general purpose CLI utility library with this same name. That library has since been renamed to [dry-rb/dry-cli](https://github.com/dry-rb/dry-cli). Please update your Gemfiles accordingly.
5
+ **NOTE**: For versions 0.4 and below, there was a general purpose CLI utility library with this name.
6
+ That library has since been renamed to [dry-rb/dry-cli](https://github.com/dry-rb/dry-cli).
7
+ Please update your Gemfiles accordingly.
6
8
 
7
9
  ## Status
8
10
 
9
11
  [![Gem Version](https://badge.fury.io/rb/hanami-cli.svg)](https://badge.fury.io/rb/hanami-cli)
10
- [![CI](https://github.com/hanami/cli/workflows/ci/badge.svg?branch=main)](https://github.com/hanami/cli/actions?query=workflow%3Aci+branch%3Amain)
12
+ [![CI](https://github.com/hanami/cli/actions/workflows/ci.yml/badge.svg)](https://github.com/hanami/cli/actions?query=workflow%3Aci+branch%3Amain)
11
13
  [![Depfu](https://badges.depfu.com/badges/a8545fb67cf32a2c75b6227bc0821027/overview.svg)](https://depfu.com/github/hanami/cli?project=Bundler)
12
14
 
13
15
  ## Contact
@@ -15,12 +17,14 @@ CLI commands for [full-stack Hanami applications](`https://github.com/hanami/han
15
17
  - Home page: http://hanamirb.org
16
18
  - Mailing List: http://hanamirb.org/mailing-list
17
19
  - Bugs/Issues: https://github.com/hanami/cli/issues
18
- - Support: http://stackoverflow.com/questions/tagged/hanami
20
+ - API Doc: http://rubydoc.info/gems/hanami-cli
19
21
  - Chat: http://chat.hanamirb.org
20
22
 
21
- ## Rubies
23
+ ## Installation
22
24
 
23
- **Hanami::RSpec** supports Ruby (MRI) 3.0+
25
+ **Hanami::CLI** supports Ruby (MRI) 3.1+
26
+
27
+ This library is a dependency of the main `hanami` gem, so installing that is the best way to get and use this gem.
24
28
 
25
29
  ## Usage
26
30
 
@@ -30,6 +34,8 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
30
34
 
31
35
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
36
 
37
+ In order to run all of the tests, you should run `docker compose up` separately, to run a `postgres` server.
38
+
33
39
  ## Contributing
34
40
 
35
41
  Bug reports and pull requests are welcome on GitHub at https://github.com/hanami/cli. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/hanami/cli/blob/main/CODE_OF_CONDUCT.md).
@@ -44,4 +50,4 @@ Everyone interacting in the `Hanami::CLI` project's codebases, issue trackers, c
44
50
 
45
51
  ## Copyright
46
52
 
47
- Copyright © 2014 Hanami Team – Released under MIT License
53
+ Copyright © 2014–2024 Hanami Team – Released under MIT License
@@ -0,0 +1,14 @@
1
+ services:
2
+ mysql:
3
+ image: mysql:latest
4
+ ports:
5
+ - 3307:3306
6
+ environment:
7
+ MYSQL_ROOT_PASSWORD: password
8
+ postgres:
9
+ image: postgres:latest
10
+ ports:
11
+ - 5433:5432
12
+ environment:
13
+ POSTGRES_USER: postgres
14
+ POSTGRES_PASSWORD: password
data/hanami-cli.gemspec CHANGED
@@ -28,10 +28,10 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
30
  spec.metadata["rubygems_mfa_required"] = "true"
31
- spec.required_ruby_version = ">= 3.0"
31
+ spec.required_ruby_version = ">= 3.1"
32
32
 
33
33
  spec.add_dependency "bundler", "~> 2.1"
34
- spec.add_dependency "dry-cli", "~> 1.0", "< 2"
34
+ spec.add_dependency "dry-cli", "~> 1.0", ">= 1.1.0"
35
35
  spec.add_dependency "dry-files", "~> 1.0", ">= 1.0.2", "< 2"
36
36
  spec.add_dependency "dry-inflector", "~> 1.0", "< 2"
37
37
  spec.add_dependency "rake", "~> 13.0"
@@ -23,11 +23,11 @@ module Hanami
23
23
  def self.new(
24
24
  out: $stdout,
25
25
  err: $stderr,
26
- fs: Hanami::CLI::Files.new,
26
+ fs: Hanami::CLI::Files.new(out: out),
27
27
  inflector: Dry::Inflector.new,
28
28
  **opts
29
29
  )
30
- super(out: out, err: err, fs: fs, inflector: inflector, **opts)
30
+ super
31
31
  end
32
32
 
33
33
  # Returns a new command.
@@ -71,12 +71,12 @@ module Hanami
71
71
  #
72
72
  # @since 2.0.0
73
73
  # @api public
74
- def run_command(klass, *args)
74
+ def run_command(klass, ...)
75
75
  klass.new(
76
76
  out: out,
77
77
  inflector: app.inflector,
78
78
  fs: Hanami::CLI::Files,
79
- ).call(*args)
79
+ ).call(...)
80
80
  end
81
81
 
82
82
  # Executes a given block and prints string to the `out` stream with details of the time
@@ -111,20 +111,6 @@ module Hanami
111
111
  out.puts "!!! => #{desc.inspect} FAILED"
112
112
  end
113
113
  end
114
-
115
- # This is NOT AVAILABLE as of the 2.0.0 release.
116
- #
117
- # @api private
118
- def database
119
- @database ||= Commands::App::DB::Utils::Database[app]
120
- end
121
-
122
- # This is NOT AVAILABLE as of the 2.0.0 release.
123
- #
124
- # @api private
125
- def database_config
126
- database.config
127
- end
128
114
  end
129
115
  end
130
116
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require_relative "utils/database"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Commands
9
+ module App
10
+ module DB
11
+ # Base class for `hanami` CLI commands intended to be executed within an existing Hanami
12
+ # app.
13
+ #
14
+ # @since 2.2.0
15
+ # @api private
16
+ class Command < App::Command
17
+ option :app, required: false, type: :flag, default: false, desc: "Use app database"
18
+ option :slice, required: false, desc: "Use database for slice"
19
+
20
+ attr_reader :system_call
21
+
22
+ def initialize(
23
+ out:, err:,
24
+ system_call: SystemCall.new,
25
+ **opts
26
+ )
27
+ super(out: out, err: err, **opts)
28
+ @system_call = system_call
29
+ end
30
+
31
+ def run_command(klass, ...)
32
+ klass.new(
33
+ out: out,
34
+ inflector: inflector,
35
+ fs: fs,
36
+ system_call: system_call,
37
+ ).call(...)
38
+ end
39
+
40
+ private
41
+
42
+ def databases(app: false, slice: nil, gateway: nil)
43
+ if gateway && !app && !slice
44
+ err.puts "When specifying --gateway, an --app or --slice must also be given"
45
+ exit 1
46
+ end
47
+
48
+ databases =
49
+ if slice
50
+ [database_for_slice(slice, gateway: gateway)]
51
+ elsif app
52
+ [database_for_slice(self.app, gateway: gateway)]
53
+ else
54
+ all_databases
55
+ end
56
+
57
+ databases.flatten
58
+ end
59
+
60
+ def database_for_slice(slice, gateway: nil)
61
+ unless slice.is_a?(Class) && slice < Hanami::Slice
62
+ slice_name = inflector.underscore(Shellwords.shellescape(slice)).to_sym
63
+ slice = app.slices[slice_name]
64
+ end
65
+
66
+ ensure_database_slice slice
67
+
68
+ databases = build_databases(slice)
69
+
70
+ if gateway
71
+ databases.fetch(gateway.to_sym) do
72
+ err.puts %(No gateway "#{gateway}" in #{slice})
73
+ exit 1
74
+ end
75
+ else
76
+ databases.values
77
+ end
78
+ end
79
+
80
+ def all_databases # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
81
+ slices = [app] + app.slices.with_nested
82
+
83
+ slice_gateways_by_database_url = slices.each_with_object({}) { |slice, hsh|
84
+ db_provider_source = slice.container.providers[:db]&.source
85
+ next unless db_provider_source
86
+
87
+ db_provider_source.database_urls.each do |gateway, url|
88
+ hsh[url] ||= []
89
+ hsh[url] << {slice: slice, gateway: gateway}
90
+ end
91
+ }
92
+
93
+ slice_gateways_by_database_url.each_with_object([]) { |(url, slice_gateways), arr|
94
+ slice_gateways_with_config = slice_gateways.select {
95
+ _1[:slice].root.join("config", "db").directory?
96
+ }
97
+
98
+ db_slice_gateway = slice_gateways_with_config.first || slice_gateways.first
99
+ database = Utils::Database.database_class(url).new(
100
+ slice: db_slice_gateway.fetch(:slice),
101
+ gateway_name: db_slice_gateway.fetch(:gateway),
102
+ system_call: system_call
103
+ )
104
+
105
+ warn_on_misconfigured_database database, slice_gateways.map { _1.fetch(:slice) }
106
+
107
+ arr << database
108
+ }
109
+ end
110
+
111
+ def build_databases(slice)
112
+ Utils::Database.from_slice(slice: slice, system_call: system_call)
113
+ end
114
+
115
+ def ensure_database_slice(slice)
116
+ return if slice.container.providers[:db]
117
+
118
+ out.puts "#{slice} does not have a :db provider."
119
+ exit 1
120
+ end
121
+
122
+ def warn_on_misconfigured_database(database, slices) # rubocop:disable Metrics/AbcSize
123
+ if slices.length > 1
124
+ out.puts <<~STR
125
+ WARNING: Database #{database.name} is configured for multiple config/db/ directories:
126
+
127
+ #{slices.map { "- " + _1.root.relative_path_from(_1.app.root).join("config", "db").to_s }.join("\n")}
128
+
129
+ Migrating database using #{database.slice.slice_name.to_s.inspect} slice only.
130
+
131
+ STR
132
+ elsif !database.db_config_dir?
133
+ relative_path = database.slice.root
134
+ .relative_path_from(database.slice.app.root)
135
+ .join("config", "db").to_s
136
+
137
+ out.puts <<~STR
138
+ WARNING: Database #{database.name} expects the folder #{relative_path}/ to exist but it does not.
139
+
140
+ STR
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -1,23 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../app/command"
4
-
5
3
  module Hanami
6
4
  module CLI
7
5
  module Commands
8
6
  module App
9
7
  module DB
10
8
  # @api private
11
- class Create < App::Command
12
- desc "Create database"
9
+ class Create < DB::Command
10
+ desc "Create databases"
11
+
12
+ option :gateway, required: false, desc: "Use database for gateway"
13
+
14
+ def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **)
15
+ exit_codes = []
16
+
17
+ databases(app: app, slice: slice, gateway: gateway).each do |database|
18
+ result = database.exec_create_command
19
+ exit_codes << result.exit_code if result.respond_to?(:exit_code)
20
+
21
+ if result == true || result.successful?
22
+ out.puts "=> database #{database.name} created"
23
+ else
24
+ out.puts "=> failed to create database #{database.name}"
25
+ out.puts "#{result.err}\n"
26
+ end
27
+ end
13
28
 
14
- # @api private
15
- def call(**)
16
- if database.create_command
17
- out.puts "=> database #{database.name} created"
18
- else
19
- out.puts "=> failed to create database #{database.name}"
20
- exit $?.exitstatus
29
+ exit_codes.each do |code|
30
+ break command_exit.(code) if code > 0
21
31
  end
22
32
  end
23
33
  end
@@ -1,22 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../app/command"
4
-
5
3
  module Hanami
6
4
  module CLI
7
5
  module Commands
8
6
  module App
9
7
  module DB
10
8
  # @api private
11
- class Drop < App::Command
12
- desc "Delete database"
9
+ class Drop < DB::Command
10
+ desc "Delete databases"
11
+
12
+ option :gateway, required: false, desc: "Use database for gateway"
13
+
14
+ def call(app: false, slice: nil, gateway: nil, **)
15
+ exit_codes = []
16
+
17
+ databases(app: app, slice: slice, gateway: gateway).each do |database|
18
+ result = database.exec_drop_command
19
+ exit_codes << result.exit_code if result.respond_to?(:exit_code)
20
+
21
+ if result == true || result.successful?
22
+ out.puts "=> database #{database.name} dropped"
23
+ else
24
+ out.puts "=> failed to drop database #{database.name}"
25
+ out.puts "#{result.err}\n"
26
+ end
27
+ end
13
28
 
14
- # @api private
15
- def call(**)
16
- if database.drop_command
17
- out.puts "=> database #{database.name} dropped"
18
- else
19
- out.puts "=> failed to drop #{database.name}"
29
+ exit_codes.each do |code|
30
+ break exit code if code > 0
20
31
  end
21
32
  end
22
33
  end
@@ -1,38 +1,76 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../app/command"
4
- require_relative "structure/dump"
5
-
6
3
  module Hanami
7
4
  module CLI
8
5
  module Commands
9
6
  module App
10
7
  module DB
11
8
  # @api private
12
- class Migrate < App::Command
9
+ class Migrate < DB::Command
13
10
  desc "Migrates database"
14
11
 
12
+ option :gateway, required: false, desc: "Use database for gateway"
15
13
  option :target, desc: "Target migration number", aliases: ["-t"]
14
+ option :dump, required: false, type: :boolean, default: true,
15
+ desc: "Dump the database structure after migrating"
16
+
17
+ def call(target: nil, app: false, slice: nil, gateway: nil, dump: true, command_exit: method(:exit), **)
18
+ databases(app: app, slice: slice, gateway: gateway).each do |database|
19
+ if migrations_dir_missing?(database)
20
+ warn_on_missing_migrations_dir(database)
21
+ elsif no_migrations?(database)
22
+ warn_on_empty_migrations_dir(database)
23
+ else
24
+ migrate_database(database, target: target)
25
+ end
26
+ end
27
+
28
+ run_command(Structure::Dump, app: app, slice: slice, gateway: gateway, command_exit: command_exit) if dump
29
+ end
30
+
31
+ private
16
32
 
17
- # @api private
18
- def call(target: nil, **)
19
- return true if Dir[File.join(app.root, "db/migrate/*.rb")].empty?
33
+ def migrate_database(database, target:)
34
+ return true unless database.migrations_dir?
20
35
 
21
36
  measure "database #{database.name} migrated" do
22
37
  if target
23
- run_migrations(target: Integer(target))
38
+ database.run_migrations(target: Integer(target))
24
39
  else
25
- run_migrations
40
+ database.run_migrations
26
41
  end
27
42
 
28
43
  true
29
44
  end
30
45
  end
31
46
 
32
- private
47
+ def migrations_dir_missing?(database)
48
+ !database.migrations_dir?
49
+ end
50
+
51
+ def no_migrations?(database)
52
+ database.sequel_migrator.files.empty?
53
+ end
54
+
55
+ def warn_on_missing_migrations_dir(database)
56
+ out.puts <<~STR
57
+ WARNING: Database #{database.name} expects migrations to be located within #{relative_migrations_path(database)} but that folder does not exist.
58
+
59
+ No database migrations can be run for this database.
60
+ STR
61
+ end
62
+
63
+ def warn_on_empty_migrations_dir(database)
64
+ out.puts <<~STR
65
+ NOTE: Empty database migrations folder (#{relative_migrations_path(database)}) for #{database.name}
66
+ STR
67
+ end
33
68
 
34
- def run_migrations(**options)
35
- database.run_migrations(**options)
69
+ def relative_migrations_path(database)
70
+ database
71
+ .migrations_path
72
+ .relative_path_from(database.slice.app.root)
73
+ .to_s + "/"
36
74
  end
37
75
  end
38
76
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Commands
6
+ module App
7
+ module DB
8
+ # @api private
9
+ class Prepare < DB::Command
10
+ desc "Prepare databases"
11
+
12
+ def call(app: false, slice: nil, **)
13
+ command_exit = -> code { throw :command_exited, code }
14
+ command_exit_arg = {command_exit: command_exit}
15
+
16
+ # Since any slice may have multiple databases, we need to run the steps below in a
17
+ # particular order to satisfy our ROM/Sequel's migrator, which requires _all_ the
18
+ # databases in a slice to be created before we can use it.
19
+ #
20
+ # So before we do anything else, make sure to create/load every database first.
21
+ databases(app: app, slice: slice).each do |database|
22
+ command_args = {
23
+ **command_exit_arg,
24
+ app: database.slice.app?,
25
+ slice: database.slice,
26
+ gateway: database.gateway_name.to_s
27
+ }
28
+
29
+ exit_code = catch :command_exited do
30
+ unless database.exists?
31
+ run_command(DB::Create, **command_args)
32
+ run_command(DB::Structure::Load, **command_args)
33
+ end
34
+
35
+ nil
36
+ end
37
+
38
+ return exit exit_code if exit_code.to_i > 1
39
+ end
40
+
41
+ # Once all databases are created, the migrator will properly load for each slice, and
42
+ # we can migrate each database.
43
+ databases(app: app, slice: slice).each do |database|
44
+ command_args = {
45
+ **command_exit_arg,
46
+ app: database.slice.app?,
47
+ slice: database.slice,
48
+ gateway: database.gateway_name.to_s
49
+ }
50
+
51
+ exit_code = catch :command_exited do
52
+ run_command(DB::Migrate, **command_args)
53
+
54
+ nil
55
+ end
56
+
57
+ return exit exit_code if exit_code.to_i > 1
58
+ end
59
+
60
+ # Finally, load the seeds for the slice overall, which is a once-per-slice operation.
61
+ run_command(DB::Seed, app: app, slice: slice)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end