dip 8.2.6 → 8.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: b2024ed1872b11b03b134cef94a5e15bd0525f93ab6e61de7bf6cfe971b43ebd
4
- data.tar.gz: 13100418400bb01c4a1843559b7349aae4468a802ed9ea0ba2536840f11a9eb8
3
+ metadata.gz: eae945780bd009e844b733abddfbc4529204ff443a4b112b39e13dfdc4f8e987
4
+ data.tar.gz: 60790bd09dda4ebda2c18a08c774b5deb9e52336c51723fa153dc034376c8806
5
5
  SHA512:
6
- metadata.gz: d018d6a84f98cd8778a9672f6764ca80ffdd590738b4f3649c1176f586f2dcf8679cb365576211fb094d008410a69ff9160642354de396973392761498d694ba
7
- data.tar.gz: f2eb077654301b798ef2ff43a93b610c3ddea668e437cc016c22944b8acb3a546fe259f42cd1f3366cca114c799e8c939cd670f140f8163e578c535413f5c064
6
+ metadata.gz: 4e881c360bfb42ef5e6e3ca150e656b0cca36adbbf171424ca4a7fc78771bd15b6572a7438aae53009a73123b541ab47af1afd873f1734263a3bfde6d400f959
7
+ data.tar.gz: 93fadd1ead85482d4b9b7a128cfbd5e75de0df7e2d575f9d9ba766c86a7221c4b3ec32bbb999e6041bf2418b58cade580cb3c9e9e0160d2f4dc797e139c15974
data/README.md CHANGED
@@ -20,6 +20,14 @@ The dip is a CLI dev–tool that provides native-like interaction with a Dockeri
20
20
 
21
21
  [![asciicast](https://asciinema.org/a/210236.svg)](https://asciinema.org/a/210236)
22
22
 
23
+ ## Quick start with AI
24
+
25
+ [dip-skill](https://github.com/kalashnikovisme/dip-skill) helps you quickly configure a `dip` environment for your project. It detects the tech stack and bootstraps `dip.yml`, compose files, and Dockerfiles.
26
+
27
+ ```sh
28
+ npx skills add kalashnikovisme/dip-skill
29
+ ```
30
+
23
31
  ## Installation
24
32
 
25
33
  ```sh
@@ -28,12 +36,34 @@ gem install dip
28
36
 
29
37
  ### Integration with shell
30
38
 
31
- Dip can be injected into the current shell (ZSH or Bash).
39
+ Dip can be injected into the current shell so that interaction commands (and `compose`, `up`, `stop`, `down`, `build`, `provision`) become available without the `dip` prefix. **Bash**, **ZSH**, and **Fish** are supported.
40
+
41
+ Add the matching line to your shell startup file so the integration is loaded in every session:
42
+
43
+ ```sh
44
+ # Bash — ~/.bashrc or ~/.bash_profile
45
+ eval "$(dip console)"
46
+ ```
32
47
 
33
48
  ```sh
49
+ # ZSH — ~/.zshrc
34
50
  eval "$(dip console)"
35
51
  ```
36
52
 
53
+ ```fish
54
+ # Fish — ~/.config/fish/config.fish
55
+ dip console | source
56
+ ```
57
+
58
+ The target shell is autodetected from the `$SHELL` environment variable, so the snippets above work as-is. If autodetection is wrong (for example, you run Fish but `$SHELL` still points to Bash), force the dialect explicitly:
59
+
60
+ ```sh
61
+ eval "$(dip console --shell zsh)" # Bash / ZSH
62
+ dip console --shell fish | source # Fish
63
+ ```
64
+
65
+ `--shell` accepts `bash`, `zsh`, or `fish` (`bash` and `zsh` produce the same POSIX output).
66
+
37
67
  **IMPORTANT**: Beware of possible collisions with local tools. One particular example is supporting both local and Docker frontend build tools, such as Yarn. If you want some developer to run `yarn` locally and other to use Docker for that, you should either avoid adding the `yarn` command to the `dip.yml` or avoid using the shell integration for hybrid development.
38
68
 
39
69
  After that we can type commands without `dip` prefix. For example:
@@ -46,16 +76,27 @@ ktl *any-kubectl-arg
46
76
  provision
47
77
  ```
48
78
 
49
- When we change the current directory, all shell aliases will be automatically removed. But when we enter back into a directory with a `dip.yml` file, then shell aliases will be renewed.
79
+ When we change the current directory, all shell aliases are automatically removed. When we enter a directory that has a `dip.yml` file (in it or in a parent), the aliases are renewed. This is wired through the shell's directory-change hook `chpwd_functions` on ZSH, a `cd`/`pushd`/`popd` wrapper on Bash, and a `--on-variable PWD` handler on Fish. The hook resolves the applicable `dip.yml` itself (no `dip` process involved) and only actually reloads when that path changes, so `cd`ing around inside the same project doesn't re-run `dip` on every prompt.
50
80
 
51
- Also, in shell mode Dip is trying to determine manually passed environment variables. For example:
81
+ Also, in shell mode Dip tries to determine manually passed environment variables. For example:
52
82
 
53
83
  ```sh
54
84
  VERSION=20180515103400 rails db:migrate:down
55
85
  ```
56
86
 
57
- You could add this `eval` at the end of your `~/.zshrc`, or `~/.bashrc`, or `~/.bash_profile`.
58
- After that, it will be automatically applied when you open your preferred terminal.
87
+ Once the startup snippet is in place, the integration is applied automatically every time you open your terminal.
88
+
89
+ #### How it works
90
+
91
+ `dip console` prints a bootstrap script that defines three helpers:
92
+
93
+ - `dip_inject` — evaluates `dip console inject`, which emits one shell function per interaction command plus the built-in `compose`/`up`/`stop`/`down`/`build`/`provision` wrappers. An interaction command whose name collides with a shell builtin/keyword (`jobs`, `test`, `read`, `set`, …) is skipped, with a warning on stderr — run it as `dip <name>` instead, or rename it.
94
+ - `dip_clear` — removes the functions previously injected (regenerated on every inject so it always matches the current `dip.yml`).
95
+ - `dip_reload` — runs `dip_clear` then `dip_inject`; also bound to the directory-change hook, which calls it only when the resolved `dip.yml` path actually changed.
96
+
97
+ The bootstrap also exports `DIP_SHELL=1`, `DIP_EARLY_ENVS` (the list of variables present at load time, used to detect manually passed env vars), and `DIP_PROMPT_TEXT` (`ⅆ`). On ZSH with the `agnoster` theme, `DIP_PROMPT_TEXT` is added as a prompt segment; other shells and themes are left untouched.
98
+
99
+ You can force a reload at any time by running `dip_reload` (useful after editing `dip.yml`).
59
100
 
60
101
  ## Usage
61
102
 
@@ -178,8 +219,16 @@ provision:
178
219
  - dip clean_cache
179
220
  - dip compose up -d pg redis
180
221
  - dip bash -c ./bin/setup
222
+
223
+ preflight:
224
+ - ./bin/check-credentials
225
+ - test -f .env
181
226
  ```
182
227
 
228
+ `preflight` is an array of shell commands that dip runs before any command that touches a running container or cluster: `dip compose ...` (and its aliases `dip up`, `dip build`, `dip stop`, `dip down`), `dip ktl ...`, and `dip run ...` (including the interaction shorthand `dip <name>`). If any command exits non-zero, dip aborts before the underlying operation runs. Useful for verifying credentials are present, required files exist, env vars are set, etc.
229
+
230
+ Not triggered by `dip down --all` (cross-project teardown), `dip ssh`, `dip infra`, or `dip provision`.
231
+
183
232
  ### Predefined environment variables
184
233
 
185
234
  #### $DIP_OS
@@ -493,6 +542,19 @@ You can skip validation by setting `DIP_SKIP_VALIDATION` environment variable.
493
542
 
494
543
  Add `# yaml-language-server: $schema=https://raw.githubusercontent.com/bibendi/dip/refs/heads/master/schema.json` to the top of your dip.yml to get schema validation in VSCode. Read more about [YAML Language Server](https://github.com/redhat-developer/vscode-yaml?tab=readme-ov-file#associating-schemas).
495
544
 
545
+ ### dip console
546
+
547
+ Prints the shell integration script for the current shell. See [Integration with shell](#integration-with-shell) for the full setup.
548
+
549
+ ```sh
550
+ dip console [--shell bash|zsh|fish] # bootstrap script (default subcommand)
551
+ dip console inject [--shell bash|zsh|fish] # just the command aliases
552
+ ```
553
+
554
+ - `--shell` (`-s`) selects the dialect: `bash`, `zsh`, or `fish`. When omitted, it is autodetected from `$SHELL`, falling back to POSIX (Bash/ZSH) output.
555
+ - `dip console` is meant to be evaluated by your shell: `eval "$(dip console)"` for Bash/ZSH, `dip console | source` for Fish.
556
+ - `dip console inject` is called internally by the bootstrap script (via `dip_reload`); you normally don't run it by hand. It only needs the interaction command names, so it skips full `dip.yml` schema validation — run `dip validate` (or any other `dip` command) to check the file against the schema.
557
+
496
558
  ## Changelog
497
559
 
498
560
  See [CHANGELOG.md](CHANGELOG.md).
@@ -7,14 +7,21 @@ require_relative "../commands/console"
7
7
  module Dip
8
8
  class CLI
9
9
  class Console < Base
10
+ SHELL_OPTION = [
11
+ :shell,
12
+ {aliases: "-s", type: :string, enum: %w[bash zsh fish posix],
13
+ desc: "Target shell dialect (autodetected from $SHELL by default)"}
14
+ ].freeze
15
+
10
16
  desc "start", "Integrate Dip into current shell"
11
17
  method_option :help, aliases: "-h", type: :boolean,
12
18
  desc: "Display usage information"
19
+ method_option(*SHELL_OPTION)
13
20
  def start
14
21
  if options[:help]
15
22
  invoke :help, ["start"]
16
23
  else
17
- Dip::Commands::Console::Start.new.execute
24
+ Dip::Commands::Console::Start.new(shell: options[:shell]).execute
18
25
  end
19
26
  end
20
27
 
@@ -23,11 +30,12 @@ module Dip
23
30
  desc "inject", "Inject aliases"
24
31
  method_option :help, aliases: "-h", type: :boolean,
25
32
  desc: "Display usage information"
33
+ method_option(*SHELL_OPTION)
26
34
  def inject
27
35
  if options[:help]
28
36
  invoke :help, ["inject"]
29
37
  else
30
- Dip::Commands::Console::Inject.new.execute
38
+ Dip::Commands::Console::Inject.new(shell: options[:shell]).execute
31
39
  end
32
40
  end
33
41
  end
data/lib/dip/cli.rb CHANGED
@@ -49,7 +49,10 @@ module Dip
49
49
 
50
50
  desc "compose CMD [OPTIONS]", "Run Docker Compose commands"
51
51
  def compose(*argv)
52
+ require_relative "commands/preflight"
52
53
  require_relative "commands/compose"
54
+
55
+ Dip::Commands::Preflight.new.execute
53
56
  Dip::Commands::Compose.new(*argv).execute
54
57
  end
55
58
 
@@ -84,7 +87,10 @@ module Dip
84
87
 
85
88
  desc "ktl CMD [OPTIONS]", "Run kubectl commands"
86
89
  def ktl(*argv)
90
+ require_relative "commands/preflight"
87
91
  require_relative "commands/kubectl"
92
+
93
+ Dip::Commands::Preflight.new.execute
88
94
  Dip::Commands::Kubectl.new(*argv).execute
89
95
  end
90
96
 
@@ -96,8 +102,10 @@ module Dip
96
102
  if argv.empty? || options[:help]
97
103
  invoke :help, ["run"]
98
104
  else
105
+ require_relative "commands/preflight"
99
106
  require_relative "commands/run"
100
107
 
108
+ Dip::Commands::Preflight.new.execute
101
109
  Dip::Commands::Run.new(
102
110
  *argv,
103
111
  **options.to_h.transform_keys!(&:to_sym)
@@ -135,7 +143,7 @@ module Dip
135
143
  subcommand :infra, Dip::CLI::Infra
136
144
 
137
145
  require_relative "cli/console"
138
- desc "console", "Integrate Dip commands into shell (only ZSH and Bash are supported)"
146
+ desc "console", "Integrate Dip commands into shell (Bash, ZSH and Fish are supported)"
139
147
  subcommand :console, Dip::CLI::Console
140
148
  end
141
149
  end
@@ -5,14 +5,50 @@ require_relative "../command"
5
5
  module Dip
6
6
  module Commands
7
7
  module Console
8
+ # Interaction command names come straight from `dip.yml`, and `inject`
9
+ # turns each one into a same-named shell function. A name that collides
10
+ # with a shell builtin/keyword silently shadows it for the rest of the
11
+ # session — e.g. an interaction called `jobs` breaks `jobs`, which
12
+ # things like Starship's prompt call on every render, turning every
13
+ # keystroke into a `dip jobs` invocation. Skip those instead of
14
+ # aliasing over them.
15
+ RESERVED_NAMES = %w[
16
+ cd pwd test read set jobs type command builtin history alias unalias
17
+ source eval exec exit return break continue trap kill wait bg fg
18
+ export unset declare typeset readonly local shift times true false
19
+ echo printf time status functions function end
20
+ ].freeze
21
+
22
+ # Figure out which shell dialect to generate integration code for.
23
+ #
24
+ # An explicit value (from `--shell`) always wins. Otherwise we guess from
25
+ # the `$SHELL` environment variable and fall back to the POSIX flavour
26
+ # (bash/zsh) which was the only supported one historically.
27
+ def self.detect_shell(explicit = nil)
28
+ name = (explicit || File.basename(ENV["SHELL"].to_s)).to_s.downcase
29
+ name.include?("fish") ? :fish : :posix
30
+ end
31
+
8
32
  class Start < Dip::Command
33
+ def initialize(shell: nil)
34
+ @shell = Console.detect_shell(shell)
35
+ end
36
+
9
37
  def execute
10
- puts script
38
+ puts fish? ? fish_script : posix_script
11
39
  end
12
40
 
13
41
  private
14
42
 
15
- def script
43
+ def fish?
44
+ @shell == :fish
45
+ end
46
+
47
+ def inject_command
48
+ "#{Dip.bin_path} console inject --shell #{@shell}"
49
+ end
50
+
51
+ def posix_script
16
52
  <<-SH.gsub(/^ {12}/, "")
17
53
  export DIP_SHELL=1
18
54
  export DIP_EARLY_ENVS=#{ENV.keys.join(",")}
@@ -24,7 +60,7 @@ module Dip
24
60
  }
25
61
 
26
62
  function dip_inject() {
27
- eval "$(#{Dip.bin_path} console inject)"
63
+ eval "$(#{inject_command})"
28
64
  }
29
65
 
30
66
  function dip_reload() {
@@ -32,6 +68,36 @@ module Dip
32
68
  dip_inject
33
69
  }
34
70
 
71
+ # Resolves the dip.yml that applies to $PWD (or $DIP_FILE), without
72
+ # spawning a `dip` process — used to skip redundant reloads below.
73
+ function __dip_config_path() {
74
+ if [ -n "${DIP_FILE:-}" ]; then
75
+ printf '%s\\n' "$DIP_FILE"
76
+ return
77
+ fi
78
+
79
+ \\typeset __dip_dir="$PWD"
80
+ while :; do
81
+ if [ -e "$__dip_dir/dip.yml" ]; then
82
+ printf '%s\\n' "$__dip_dir/dip.yml"
83
+ return
84
+ fi
85
+ [ "$__dip_dir" = "/" ] && return
86
+ __dip_dir=$(dirname "$__dip_dir")
87
+ done
88
+ }
89
+
90
+ # Only reload aliases on `cd` when the resolved dip.yml actually
91
+ # changed — most `cd`s stay within the same project and would
92
+ # otherwise re-spawn `dip` (Ruby boot + schema validation) for nothing.
93
+ function __dip_auto_reload() {
94
+ \\typeset __dip_new_config_path
95
+ __dip_new_config_path="$(__dip_config_path)"
96
+ [ "$__dip_new_config_path" = "${__DIP_CONFIG_PATH:-}" ] && return
97
+ __DIP_CONFIG_PATH="$__dip_new_config_path"
98
+ dip_reload
99
+ }
100
+
35
101
  # Inspired by RVM
36
102
  function __zsh_like_cd() {
37
103
  \\typeset __zsh_like_cd_hook
@@ -58,7 +124,7 @@ module Dip
58
124
  }
59
125
 
60
126
  export -a chpwd_functions
61
- [[ " ${chpwd_functions[*]} " == *" dip_reload "* ]] || chpwd_functions+=(dip_reload)
127
+ [[ " ${chpwd_functions[*]} " == *" __dip_auto_reload "* ]] || chpwd_functions+=(__dip_auto_reload)
62
128
 
63
129
  if [[ "$ZSH_THEME" = "agnoster" ]]; then
64
130
  eval "`declare -f prompt_end | sed '1s/.*/_&/'`"
@@ -73,22 +139,87 @@ module Dip
73
139
  fi
74
140
 
75
141
  dip_reload
142
+ __DIP_CONFIG_PATH="$(__dip_config_path)"
76
143
  SH
77
144
  end
145
+
146
+ def fish_script
147
+ <<-FISH.gsub(/^ {12}/, "")
148
+ set -gx DIP_SHELL 1
149
+ set -gx DIP_EARLY_ENVS "#{ENV.keys.join(",")}"
150
+ set -gx DIP_PROMPT_TEXT "ⅆ"
151
+
152
+ function dip_clear
153
+ # just stub, will be redefined after injecting aliases
154
+ true
155
+ end
156
+
157
+ function dip_inject
158
+ #{inject_command} | source
159
+ end
160
+
161
+ function dip_reload
162
+ dip_clear
163
+ dip_inject
164
+ end
165
+
166
+ # Resolves the dip.yml that applies to $PWD (or $DIP_FILE), without
167
+ # spawning a `dip` process — used to skip redundant reloads below.
168
+ function __dip_config_path
169
+ if set -q DIP_FILE
170
+ echo "$DIP_FILE"
171
+ return
172
+ end
173
+
174
+ set -l __dip_dir "$PWD"
175
+ while true
176
+ if test -e "$__dip_dir/dip.yml"
177
+ echo "$__dip_dir/dip.yml"
178
+ return
179
+ end
180
+ if test "$__dip_dir" = "/"
181
+ return
182
+ end
183
+ set __dip_dir (dirname "$__dip_dir")
184
+ end
185
+ end
186
+
187
+ # Renew aliases whenever the working directory changes — but only
188
+ # when the resolved dip.yml actually changed. Most `cd`s stay within
189
+ # the same project and would otherwise re-spawn `dip` (Ruby boot +
190
+ # schema validation) for nothing.
191
+ function __dip_chpwd --on-variable PWD
192
+ set -l __dip_new_config_path (__dip_config_path)
193
+ if test "$__dip_new_config_path" != "$__dip_config_path_cache"
194
+ set -g __dip_config_path_cache "$__dip_new_config_path"
195
+ dip_reload
196
+ end
197
+ end
198
+
199
+ dip_reload
200
+ set -g __dip_config_path_cache (__dip_config_path)
201
+ FISH
202
+ end
78
203
  end
79
204
 
80
205
  class Inject < Dip::Command
81
206
  attr_reader :out, :aliases
82
207
 
83
- def initialize
208
+ def initialize(shell: nil)
209
+ @shell = Console.detect_shell(shell)
84
210
  @aliases = []
85
211
  @out = []
86
212
  end
87
213
 
88
214
  def execute
89
- if Dip.config.exist?
90
- add_aliases(*Dip.config.interaction.keys) if Dip.config.interaction
91
- add_aliases("compose", "up", "stop", "down", "provision", "build")
215
+ # Runs on every automatic shell reload (e.g. on `cd`), so it only needs
216
+ # the interaction command names, not full schema conformance — skip the
217
+ # `json-schema` require and validation pass that every other command pays.
218
+ with_validation_skipped do
219
+ if Dip.config.exist?
220
+ add_aliases(*Dip.config.interaction.keys) if Dip.config.interaction
221
+ add_aliases("compose", "up", "stop", "down", "provision", "build")
222
+ end
92
223
  end
93
224
 
94
225
  clear_aliases
@@ -98,17 +229,49 @@ module Dip
98
229
 
99
230
  private
100
231
 
232
+ def with_validation_skipped
233
+ had_key = ENV.key?("DIP_SKIP_VALIDATION")
234
+ previous = ENV["DIP_SKIP_VALIDATION"]
235
+ ENV["DIP_SKIP_VALIDATION"] = "1"
236
+ yield
237
+ ensure
238
+ if had_key
239
+ ENV["DIP_SKIP_VALIDATION"] = previous
240
+ else
241
+ ENV.delete("DIP_SKIP_VALIDATION")
242
+ end
243
+ end
244
+
245
+ def fish?
246
+ @shell == :fish
247
+ end
248
+
101
249
  def add_aliases(*names)
102
250
  names.each do |name|
251
+ if Console::RESERVED_NAMES.include?(name.to_s)
252
+ warn "dip: skipping alias `#{name}` — it would shadow the `#{name}` shell builtin. " \
253
+ "Run it as `#{Dip.bin_path} #{name}`, or rename it in dip.yml."
254
+ next
255
+ end
256
+
103
257
  aliases << name
104
- out << "function #{name}() { #{Dip.bin_path} #{name} $@; }"
258
+ out << if fish?
259
+ "function #{name}; #{Dip.bin_path} #{name} $argv; end"
260
+ else
261
+ "function #{name}() { #{Dip.bin_path} #{name} $@; }"
262
+ end
105
263
  end
106
264
  end
107
265
 
108
266
  def clear_aliases
109
- out << "function dip_clear() { \n" \
110
- "#{aliases.any? ? aliases.map { |a| " unset -f #{a}" }.join("\n") : "true"} " \
111
- "\n}"
267
+ out << if fish?
268
+ body = aliases.any? ? "functions -e #{aliases.join(" ")}" : "true"
269
+ "function dip_clear; #{body}; end"
270
+ else
271
+ "function dip_clear() { \n" \
272
+ "#{aliases.any? ? aliases.map { |a| " unset -f #{a}" }.join("\n") : "true"} " \
273
+ "\n}"
274
+ end
112
275
  end
113
276
  end
114
277
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../command"
4
+
5
+ module Dip
6
+ module Commands
7
+ class Preflight < Dip::Command
8
+ def execute
9
+ Dip.config.preflight.each do |command|
10
+ exec_subprocess(command)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/dip/config.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "yaml"
4
4
  require "erb"
5
5
  require "pathname"
6
- require "json-schema"
6
+ require "json"
7
7
 
8
8
  require "dip/version"
9
9
  require "dip/ext/hash"
@@ -20,10 +20,11 @@ module Dip
20
20
  kubectl: {},
21
21
  infra: {},
22
22
  interaction: {},
23
- provision: []
23
+ provision: [],
24
+ preflight: []
24
25
  }.freeze
25
26
 
26
- TOP_LEVEL_KEYS = %i[environment compose kubectl infra interaction provision].freeze
27
+ TOP_LEVEL_KEYS = %i[environment compose kubectl infra interaction provision preflight].freeze
27
28
 
28
29
  ConfigKeyMissingError = Class.new(ArgumentError)
29
30
 
@@ -113,15 +114,22 @@ module Dip
113
114
  end
114
115
  end
115
116
 
116
- def validate
117
+ # `data`, when given, is the already-parsed raw config (as produced by
118
+ # `load_yaml`) so callers that parsed the file themselves don't pay for
119
+ # parsing it a second time here.
120
+ def validate(data = nil)
117
121
  raise Dip::Error, "Config file path is not set" if file_path.nil?
118
122
  raise Dip::Error, "Config file not found: #{file_path}" unless File.exist?(file_path)
119
123
 
120
124
  schema_path = File.join(File.dirname(__FILE__), "../../schema.json")
121
125
  raise Dip::Error, "Schema file not found: #{schema_path}" unless File.exist?(schema_path)
122
126
 
123
- data = self.class.load_yaml(file_path)
124
- schema = JSON::Validator.parse(File.read(schema_path))
127
+ require "json-schema"
128
+
129
+ data ||= self.class.load_yaml(file_path)
130
+ # Parse with the stdlib rather than JSON::Validator.parse: the latter passes
131
+ # `quirks_mode:` to JSON.parse, which the json gem removed in 3.0 (Ruby 3.5+).
132
+ schema = JSON.parse(File.read(schema_path))
125
133
  JSON::Validator.validate!(schema, data)
126
134
  rescue Psych::SyntaxError => e
127
135
  raise Dip::Error, "Invalid YAML syntax in config file: #{e.message}"
@@ -129,7 +137,7 @@ module Dip
129
137
  data_display = data ? data.to_yaml.gsub("\n", "\n ") : "nil"
130
138
  error_message = "Schema validation failed: #{e.message}\nInput data:\n #{data_display}"
131
139
  raise Dip::Error, error_message
132
- rescue JSON::Schema::JsonParseError => e
140
+ rescue JSON::Schema::JsonParseError, JSON::ParserError => e
133
141
  raise Dip::Error, "Error parsing schema file: #{e.message}"
134
142
  end
135
143
 
@@ -178,7 +186,7 @@ module Dip
178
186
  @config = CONFIG_DEFAULTS.merge(base_config)
179
187
 
180
188
  unless ENV.key?("DIP_SKIP_VALIDATION")
181
- validate
189
+ validate(config)
182
190
  end
183
191
 
184
192
  @config
data/lib/dip/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dip
4
- VERSION = "8.2.6"
4
+ VERSION = "8.4.0"
5
5
  end
data/schema.json CHANGED
@@ -175,6 +175,16 @@
175
175
  ]
176
176
  ]
177
177
  },
178
+ "preflight": {
179
+ "type": "array",
180
+ "items": {
181
+ "type": "string"
182
+ },
183
+ "description": "Shell commands to run before any `dip compose` subcommand. A non-zero exit aborts the command.",
184
+ "examples": [
185
+ ["./bin/check-credentials", "test -f .env"]
186
+ ]
187
+ },
178
188
  "environment": {
179
189
  "$ref": "#/definitions/environment_vars"
180
190
  },
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dip
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.2.6
4
+ version: 8.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bibendi
@@ -231,6 +231,7 @@ files:
231
231
  - lib/dip/commands/infra/service.rb
232
232
  - lib/dip/commands/kubectl.rb
233
233
  - lib/dip/commands/list.rb
234
+ - lib/dip/commands/preflight.rb
234
235
  - lib/dip/commands/provision.rb
235
236
  - lib/dip/commands/run.rb
236
237
  - lib/dip/commands/runners/base.rb