jade_systems_toolbox 0.1.4 → 0.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: 8bb1d901493c286300b53d74441705dc7402263497e998f5490ca14beac572a5
4
- data.tar.gz: bc12fcce57604c61fe617eac640c3ab38e6204da916e73124162df78c519e803
3
+ metadata.gz: 6e5e58dc71de22b037aab057b8a9205361a239590b6b844f438b59fdd771441f
4
+ data.tar.gz: 1b6a2766065ad815318eb62d463c3d5113ac3ed4b6e293c6aa883f6b3e174845
5
5
  SHA512:
6
- metadata.gz: 58b7e44b6210955e1b50fc2601cc018a2434c92c105436bf923ab818577448c8f11cc90ed81128bfa6290c86f455b6aeb4e444682d4966ebc04c7cab1f382c37
7
- data.tar.gz: 91636de5259a810060040aa51d3cc0fc1bb914358c679e2dc12dad59e639dafcbf3869a0a1f6886228e302154596195503315c92f9ed567cd6394ca708c1d2b1
6
+ metadata.gz: 257d930928d5186d49415b1cc25b28122e5dada2753610ef3346ef39aa6174b01c89a43dfa567464caca71ea0c23982d9bb876e544e1fd4ae2fed4c4b29548e7
7
+ data.tar.gz: 51ea308a86ef16a12d8d2cb43dbe300558e3b97ed4aa0fe009834ad6c226121c58cc3e287639132f425f0d50ae2da3b907b9698349e9ec9775df551dd9eb28b6
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 3.2
2
+ TargetRubyVersion: 3.3
3
3
 
4
4
  # Omakase Ruby styling for Rails
5
5
  inherit_gem:
@@ -66,5 +66,6 @@
66
66
  "files.trimTrailingWhitespace": true,
67
67
  "[sql]": {
68
68
  "editor.defaultFormatter": "adpyke.vscode-sql-formatter"
69
- }
69
+ },
70
+ "window.title": "jade_systems_toolbox"
70
71
  }
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  This gem installs a program to help do container-based development. It provides functions to:
4
4
 
5
5
  * Prepare a directory for development in containers, including some files to configure VScode.
6
- * Bring up the container(s).
6
+ * Start the container(s).
7
7
  * Open VScode on the code in the container.
8
8
  * Open a browser window on the application-under-development's web server.
9
9
  * Open a terminal session on the application container.
@@ -29,19 +29,21 @@ gem install --user-install jade_systems_toolbox
29
29
 
30
30
  ```terminal
31
31
  Commands:
32
- tool down # docker compose down. Deletes the container. You probably want stop.
32
+ tool down -- [COMPOSE_ARGS] # docker compose down. Deletes the container. You probably want stop.
33
33
  tool edit # devcontainer open
34
+ tool exec COMMAND [ARGS] # Run a command in the container and exit
34
35
  tool help [COMMAND] # Describe available commands or one specific command
35
36
  tool init # Initialize compose files and devcontainer.json
36
37
  tool initialize_docker # Initialize compose files
37
- tool initilize_vscode # Initialize devcontainer.json for vscode
38
- tool open # Open a page on the services's first port
38
+ tool initialize_vscode # Initialize devcontainer.json for vscode
39
+ tool open # Open a page on the services's first port or specified port
39
40
  tool port [CONTAINER_PORT] # Get the host port for the CONTAINER_PORT's container port CONTAINER_PORT
40
41
  tool ports # Get the host ports for the container ports defined in `compose.yml`
41
42
  tool server [COMMAND] # Run the server in the container
43
+ tool start [COMPOSE_ARGS] # docker compose up -d (aliased as `up`)
42
44
  tool stop # Stop the service(s) but preserve the container(s)
43
45
  tool terminal # Run a shell in the container
44
- tool up # docker compose up -d
46
+ tool tree # Print a tree of all available commands
45
47
  tool version # Show the version number of the tool.
46
48
 
47
49
  Options:
@@ -56,7 +58,7 @@ The broad steps to start a brand new project are:
56
58
  1. Create a directory for the project, and change directory into the new directory.
57
59
  1. Decide which container(s) you want to use.
58
60
  1. Initialize the container(s).
59
- 1. Bring up the container(s).
61
+ 1. Start the container(s).
60
62
  1. Open a terminal session and create the application (e.g. `rails new` if you're doing a Rails project.)
61
63
  1. Run the editor and otherwise do what you need to do to create your project.
62
64
  1. When the web server is ready to run (if you're developing a web app), run the server.
@@ -83,7 +85,9 @@ tool init
83
85
 
84
86
  The initialization installs
85
87
 
86
- * A `compose.yml` file for Rails development with a SQLite database, using Ruby 3.4, and the `bookworm` version of Debian.
88
+ * A `compose.yml` file for Rails development with a SQLite database, using the latest released version of Ruby,
89
+ and the latest stable version of Debian.
90
+ (Give or take. We don't track the releases so closely. Raise and issue if you need an upgrade.)
87
91
  * On Linux, a `compose.override.yml` file.
88
92
  * A `.devcontainer.json` file, so VScode can edit without the context of the container.
89
93
  * `.vscode/settings.json` and `vscode/extensions.json` so developers have a consistent experience in VScode (mostly linting and autoformatting).
@@ -102,19 +106,37 @@ To choose a different ruby version and Debian version:
102
106
  tool init --ruby-version 3.2 --distro-version bullseye
103
107
  ```
104
108
 
109
+ A fairly common variant is to get a `compose.yml` that also has a Playwright container:
110
+
111
+ ```bash
112
+ tool init --end-to-end-tool playwright
113
+ ```
114
+
115
+ This retrieves the compose file, and puts it in your local `compose.yml`.
116
+
105
117
  You can choose a currently supported Ruby version, and a Debian version that supports that Ruby version, from the containers built by https://github.com/lcreid/docker.
106
118
 
107
- You're free to modify the `compose.yml` (and `compose.override.yml` on Linux). If you run `tool init` again, it will over-write your changes.
119
+ You're free to modify the `compose.yml` (and `compose.override.yml` on Linux). Note that if you run `tool init` again, it will over-write your changes.
108
120
 
109
121
  #### Start the Containers
110
122
 
111
123
  ```bash
112
- tool up
124
+ tool start
113
125
  ```
114
126
 
115
127
  This takes a while to run, the first time you run it when the image isn't already downloaded to your computer.
116
128
 
117
- ### Open a Terminal Session
129
+ You can add additional Docker Compose arguments to the command line. For example:
130
+
131
+ ```bash
132
+ tool start --remove-orphans
133
+ ```
134
+
135
+ If any of the Docker Compose arguments are the same as any of `tool`'s arguments,
136
+ separate the `start` command from the rest of the command line with `--`,
137
+ like this: `tool start -- -v`.
138
+
139
+ #### Open a Terminal Session
118
140
 
119
141
  ```bash
120
142
  tool terminal
@@ -126,6 +148,14 @@ This connects to the `web` service defined in the `compose.yml` file. If you wan
126
148
  tool terminal --service mysql
127
149
  ```
128
150
 
151
+ Set some environment variables in the container (the `docker compose --env` flag):
152
+
153
+ ```bash
154
+ tool terminal -e RAILS_ENV=test
155
+ ```
156
+
157
+ `tool terminal` supports the `--mapping` argument as described under [Run the Server](run-the-server).
158
+
129
159
  #### Start VScode
130
160
 
131
161
  ```bash
@@ -138,10 +168,22 @@ tool edit
138
168
  tool server
139
169
  ```
140
170
 
171
+ By default, the container has environment variables set for port mapping.
172
+ The container environment will have variables named `HOST_PORT_xxxx`,
173
+ and `xxxx` is the container port (e.g. 3000).
174
+ The _value_ of each environment variable
175
+ is the host's port number that the container port is mapped to.
176
+
177
+ If you don't want the mapping, you can turn it off:
178
+
179
+ ```bash
180
+ tool server --no-mapping
181
+ ```
182
+
141
183
  This runs `bin/dev`. To run something else, e.g. a bare Puma so you can debug it:
142
184
 
143
185
  ```bash
144
- tool server "bin/rails s -b 0.0.0.0"
186
+ tool server bin/rails s -b 0.0.0.0
145
187
  ```
146
188
 
147
189
  If you have other services running servers, or the server is in a sub-directory of your project:
@@ -152,6 +194,8 @@ tool server --work-dir test/app --service monitor
152
194
 
153
195
  To terminate the server, type Control-C.
154
196
 
197
+ `tool server` supports the `--env` argument as described under [Open a Terminal Session](open-a-terminal-session).
198
+
155
199
  #### Open a Browser Window
156
200
 
157
201
  To connect to the server running on service `web` on port 3000:
@@ -184,6 +228,33 @@ tool open --path letter_opener
184
228
  tool stop
185
229
  ```
186
230
 
231
+ You can add additional Docker Compose arguments to the command line. For example:
232
+
233
+ ```bash
234
+ tool stop --remove-orphans
235
+ ```
236
+
237
+ If any of the Docker Compose arguments are the same as any of `tool`'s arguments,
238
+ separate the `exec` command from the rest of the command line with `--`,
239
+ like this: `tool stop -- -v`.
240
+
241
+ #### Run a Single Command in a Container
242
+
243
+ ```bash
244
+ tool exec ls -l ..
245
+ ```
246
+
247
+ Some things to watch for:
248
+
249
+ - If any of the Docker Compose arguments are the same as any of `tool`'s arguments,
250
+ separate the `exec` command from the rest of the command line with `--`,
251
+ like this: `tool exec -- ls -l -t ..`.
252
+ - The shell on the host processes the command line,
253
+ so you need to quote or escape the command if you want variable expansion
254
+ and other shell features to happen in the container.
255
+ For example: `tool exec ls -l '$HOME'` to show the home directory in the container, not your home directory
256
+ (which probably doesn't exist in the container).
257
+
187
258
  #### Bring Down the Containers
188
259
 
189
260
  This destroys the containers, which will likely destroy all your gems.
@@ -192,6 +263,16 @@ This destroys the containers, which will likely destroy all your gems.
192
263
  tool down
193
264
  ```
194
265
 
266
+ You can add additional Docker Compose arguments to the command line. For example: NOTE THAT THE FOLLOWING COMMAND IS VERY DESTRUCTIVE. It destroys the volume where you might have your development and test databases, or other important data:
267
+
268
+ ```bash
269
+ tool down --volumes --remove-orphans
270
+ ```
271
+
272
+ If any of the Docker Compose arguments are the same as any of `tool`'s arguments,
273
+ separate the `exec` command from the rest of the command line with `--`,
274
+ like this: `tool down -- -v`.
275
+
195
276
  Skip the confirmation mesage with:
196
277
 
197
278
  ```bash
@@ -214,7 +295,7 @@ To run in another directory, use the path from the directory you're in, to the d
214
295
  ruby -I ../jade_systems_toolbox/lib ../jade_systems_toolbox/exe/tool [COMMAND]
215
296
  ```
216
297
 
217
- To install this gem onto your local machine, build it: `gem build jade_systems_toolbox.gemspec`, and install it: `gem install -l jade_systems_toolbox-0.1.4.gem`. (Change the version number to match the current version number.)
298
+ To install this gem onto your local machine, build it: `gem build jade_systems_toolbox.gemspec`, and install it: `gem install -l jade_systems_toolbox-0.2.0.gem`. (Change the version number to match the current version number.)
218
299
 
219
300
  ## Release
220
301
 
@@ -2,10 +2,10 @@
2
2
 
3
3
  module JadeSystemsToolbox
4
4
  class Cli < Thor
5
- # This and other improvements from Matt: https://mattbrictson.com/blog/fixing-thor-cli-behavior
6
- check_unknown_options!
5
+ stop_on_unknown_option! :down
7
6
 
8
7
  class << self
8
+ # This and other improvements from Matt: https://mattbrictson.com/blog/fixing-thor-cli-behavior
9
9
  def exit_on_failure? = true
10
10
  end
11
11
 
@@ -14,13 +14,13 @@ module JadeSystemsToolbox
14
14
 
15
15
  class_option :verbose, type: :boolean, default: false, aliases: "-v"
16
16
 
17
- desc "down", "docker compose down. Deletes the container. You probably want stop."
17
+ desc "down -- [COMPOSE_ARGS]", "docker compose down. Deletes the container. You probably want stop."
18
18
  option :force, default: "n", aliases: "-f"
19
- def down
20
- force = ask("tool down destroys the containers. Are you sure?").downcase unless options[:force] == "y"
19
+ def down(*compose_args)
20
+ force = ask("tool down destroys the containers. Are you sure [y/N]?").downcase unless options[:force] == "y"
21
21
  exit(1) if force[0] != "y"
22
22
 
23
- system("docker compose down")
23
+ system(([ "docker compose down" ] + compose_args).join(" "))
24
24
  end
25
25
 
26
26
  desc "edit", "devcontainer open"
@@ -28,57 +28,64 @@ module JadeSystemsToolbox
28
28
  `devcontainer open`
29
29
  end
30
30
 
31
+ desc "exec COMMAND [ARGS]", "Run a command in the container and exit"
32
+ option :env, repeatable: true, default: [], aliases: "-e", banner: "VAR=value"
33
+ option :mapping, type: :boolean, default: true, aliases: "-m"
34
+ option :service, default: "web", aliases: "-s"
35
+ option :work_dir, aliases: "-w"
36
+ def exec(command_for_container, *args)
37
+ service = options[:service]
38
+ workdir = "-w #{options[:work_dir]} " unless options[:work_dir].nil?
39
+ environment_variables = options[:env].map { "-e #{_1}" }.join(" ")
40
+ port_map_environment_variables = port_map_for_environment(service) if options[:mapping]
41
+ shell_command = [ command_for_container, *args ].join(" ")
42
+ command = [
43
+ "docker compose exec",
44
+ environment_variables,
45
+ port_map_environment_variables,
46
+ workdir,
47
+ "-it",
48
+ service,
49
+ "sh -c",
50
+ "'#{shell_command}'",
51
+ ].compact.join(" ")
52
+
53
+ system(command)
54
+ end
55
+
31
56
  desc "init", "Initialize compose files and devcontainer.json"
32
- option :compose_file, default: "compose.yml"
33
57
  option :database, default: "sqlite", aliases: "-d"
34
- option :ruby_version, default: "3.4", aliases: "-r"
35
- option :distro_version, default: "bookworm", aliases: "-t" # For Toy Story.
58
+ option :ruby_version, default: "4.0", aliases: "-r"
59
+ option :distro_version, default: "trixie", aliases: "-t" # For Toy Story.
36
60
  def init
37
61
  invoke :initialize_docker
38
62
  invoke :initialize_vscode, [], {}
39
63
  end
40
64
 
41
65
  desc "initialize_docker", "Initialize compose files"
42
- option :compose_file, default: "compose.yml"
43
66
  option :database, default: "sqlite", aliases: "-d"
44
- option :ruby_version, default: "3.4", aliases: "-r"
45
- option :distro_version, default: "bookworm", aliases: "-t" # For Toy Story.
67
+ option :ruby_version, default: "4.0", aliases: "-r"
68
+ option :distro_version, default: "trixie", aliases: "-t" # For Toy Story.
69
+ method_option :end_to_end_tool,
70
+ type: :string,
71
+ enum: [ "playwright", "selenium" ],
72
+ desc: "Use an end-to-end (browser) testing tool",
73
+ aliases: "-e"
46
74
  def initialize_docker
47
- get_and_save_file(
48
- "https://github.com/lcreid/docker/raw/refs/heads/main/rails-app-sqlite/compose.yml",
49
- ) do |file_contents|
50
- file_contents.gsub!(
51
- /jade:rails-app-[0-9]+\.[0-9]+-\w+-\w+$/,
52
- "jade:rails-app-#{options[:ruby_version] || "3.4"}" \
53
- "-#{options[:database] || "sqlite"}" \
54
- "-#{options[:distro_version] || "bookworm"}",
55
- )
56
- end
57
-
58
- case Gem::Platform.local.os
59
- when "linux"
60
- get_and_save_file("https://github.com/lcreid/docker/raw/refs/heads/main/Linux/compose.override.yml")
61
- else
62
- raise Error.new("Not implemented for #{Gem::Platform.local.os}", options[:verbose])
63
- end
75
+ DockerInitializer.new(**options.transform_keys(&:to_sym).except(:verbose)).get_and_save_compose_files
64
76
  end
65
77
 
66
- desc "initilize_vscode", "Initialize devcontainer.json for vscode"
78
+ desc "initialize_vscode", "Initialize devcontainer.json for vscode"
67
79
  def initialize_vscode
68
- get_and_save_file("https://github.com/lcreid/docker/raw/refs/heads/main/.devcontainer.json") do |file_contents|
69
- file_contents.gsub!(/, "compose.override.yml"/, "") unless Gem::Platform.local.os == "linux"
70
- end
71
-
72
- Dir.mkdir(".vscode") unless Dir.exist?(".vscode")
73
- [
74
- "https://github.com/lcreid/jade_systems_toolbox/raw/refs/heads/main/templates/extensions.json",
75
- "https://github.com/lcreid/jade_systems_toolbox/raw/refs/heads/main/templates/settings.json",
76
- ].each { get_and_save_file(_1, target_directory: File.join(".", ".vscode")) }
80
+ initializer = Vscode.new
81
+ initializer.write_devcontainer_json
82
+ initializer.write_extensions_json
83
+ initializer.write_settings_json
77
84
  end
78
85
 
79
- desc "open", "Open a page on the services's first port"
80
- option :service, default: "web"
81
- option :path, default: "/"
86
+ desc "open", "Open a page on the services's first port or specified port"
87
+ option :service, default: "web", aliases: "-s"
88
+ option :path, default: "/", alias: "-p"
82
89
  option :container_port, default: 3000, type: :numeric
83
90
  option :protocol, default: "http"
84
91
  def open
@@ -87,14 +94,23 @@ module JadeSystemsToolbox
87
94
  container_port = options[:container_port]
88
95
  protocol = options[:protocol]
89
96
 
90
- container_ports = compose_yaml.dig("services", "service", "ports")
97
+ container_ports = compose_yaml.dig("services", service, "ports")
91
98
  container_port = container_ports&.[](0) || container_port
92
99
  path = "/#{path}" if path[0] != "/"
93
- `open "#{protocol}://localhost:#{host_port_from_container_port(service:, container_port:)}#{path}"`
100
+ url = "#{protocol}://localhost:#{host_port_from_container_port(service:, container_port:)}#{path}"
101
+ if `command -v "open"`
102
+ # Real Unix :-)
103
+ system "open #{url}"
104
+ elsif `command -v "cmd.exe"`
105
+ # WSL because this opens a URL in the browser, which will be on Windows.
106
+ system "cmd.exe /c start #{url}"
107
+ else
108
+ raise ArgumentError, "no suitable command available to open app."
109
+ end
94
110
  end
95
111
 
96
112
  desc "port [CONTAINER_PORT]", "Get the host port for the CONTAINER_PORT's container port CONTAINER_PORT"
97
- option :service, default: "web"
113
+ option :service, default: "web", aliases: "-s"
98
114
  def port(container_port = 3000)
99
115
  service = options[:service]
100
116
  puts host_port_from_container_port(service:, container_port:)
@@ -113,14 +129,35 @@ module JadeSystemsToolbox
113
129
  end
114
130
  end
115
131
 
116
- desc "server [COMMAND]", "Run the server in the container"
117
- option :service, default: "web"
132
+ desc "server [COMMAND] [ARGS]", "Run the server in the container"
133
+ option :env, repeatable: true, default: [], aliases: "-e", banner: "VAR=value"
134
+ option :mapping, type: :boolean, default: true, aliases: "-m"
135
+ option :service, default: "web", aliases: "-s"
118
136
  option :work_dir, aliases: "-w"
119
- def server(command = "bin/dev")
137
+ def server(command = "bin/dev", *args)
120
138
  service = options[:service]
121
139
  workdir = "-w #{options[:work_dir]} " unless options[:work_dir].nil?
122
- system("docker compose exec #{workdir}#{service} #{command}")
140
+ environment_variables = options[:env].map { "-e #{_1}" }.join(" ")
141
+ port_map_environment_variables = port_map_for_environment(service) if options[:mapping]
142
+ shell_command = [ command, *args ].join(" ")
143
+ compose_command = [
144
+ "docker compose exec",
145
+ environment_variables,
146
+ port_map_environment_variables,
147
+ workdir,
148
+ service,
149
+ "sh -c",
150
+ "'#{shell_command}'",
151
+ ].compact.join(" ")
152
+
153
+ system(compose_command)
154
+ end
155
+
156
+ desc "start [COMPOSE_ARGS]", "docker compose up -d (aliased as `up`)"
157
+ def start(*compose_args)
158
+ system(([ "docker compose up -d" ] + compose_args).join(" "))
123
159
  end
160
+ map "up" => :start
124
161
 
125
162
  desc "stop", "Stop the service(s) but preserve the container(s)"
126
163
  def stop
@@ -128,15 +165,22 @@ module JadeSystemsToolbox
128
165
  end
129
166
 
130
167
  desc "terminal", "Run a shell in the container"
131
- option :service, default: "web"
168
+ option :env, repeatable: true, default: [], aliases: "-e", banner: "VAR=value"
169
+ option :mapping, type: :boolean, default: true, aliases: "-m"
170
+ option :service, default: "web", aliases: "-s"
132
171
  def terminal
133
172
  service = options[:service]
134
- system("docker compose exec -it #{service} '/bin/bash'")
135
- end
136
-
137
- desc "up", "docker compose up -d"
138
- def up
139
- system("docker compose up -d")
173
+ environment_variables = options[:env].map { "-e #{_1}" }.join(" ")
174
+ port_map_environment_variables = port_map_for_environment(service) if options[:mapping]
175
+ command = [
176
+ "docker compose exec",
177
+ environment_variables,
178
+ port_map_environment_variables,
179
+ "-it",
180
+ service,
181
+ "'/bin/bash'",
182
+ ].join(" ")
183
+ system(command)
140
184
  end
141
185
 
142
186
  desc "version", "Show the version number of the tool."
@@ -148,32 +192,14 @@ module JadeSystemsToolbox
148
192
 
149
193
  def compose_yaml = @compose_yaml ||= YAML.load_file(options[:compose_file] || "compose.yml")
150
194
 
151
- def get_and_save_file(url, target_directory: nil)
152
- file_contents = get_file_from_internet(url:)
195
+ def get_and_save_file(url, target_directory: nil, target_file_name: nil)
196
+ file_contents = Http.get_file(url:)
153
197
  yield file_contents if block_given?
154
- file_name = Pathname.new(url).basename.to_s
198
+ file_name = target_file_name || Pathname.new(url).basename.to_s
155
199
  file_name = Pathname.new(target_directory) + file_name unless target_directory.nil?
156
200
  File.write(file_name, file_contents)
157
201
  end
158
202
 
159
- def get_file_from_internet(url:, redirects: 10)
160
- raise Errorl.new("Too many redirects", options[:verbose]) if redirects.zero?
161
-
162
- uri = URI.parse(url)
163
- request = Net::HTTP.new(uri.host, uri.port)
164
- request.use_ssl = true
165
- response = request.get(uri.path)
166
-
167
- case response
168
- when Net::HTTPSuccess
169
- response.body
170
- when Net::HTTPRedirection
171
- get_file_from_internet(redirects: redirects - 1, url: response["location"])
172
- else
173
- response.error!
174
- end
175
- end
176
-
177
203
  def host_port_from_container_port(container_port:, service: "web")
178
204
  ports = container_port.to_s.match(/([0-9]+:){0,1}([0-9]+$)/)
179
205
  if !ports[1].nil?
@@ -183,5 +209,31 @@ module JadeSystemsToolbox
183
209
  output.match(/[0-9]+$/)
184
210
  end
185
211
  end
212
+
213
+ def port_map(service)
214
+ service_ports = compose_yaml.dig("services", service, "ports")
215
+ service_ports&.reduce({}) do |acc, port|
216
+ acc[port] = host_port_from_container_port(container_port: port, service:).to_s
217
+ acc
218
+ end
219
+ end
220
+
221
+ def port_map_for_environment(service)
222
+ port_map(service).map do |container_port, host_port|
223
+ "-e HOST_PORT_#{container_port}=#{host_port}"
224
+ end.join(" ")
225
+ end
226
+
227
+ def handle_argument_error(command, error, args, arity)
228
+ # Check if the error is due to an unknown option or incorrect invocation
229
+ if (error.is_a?(Thor::UnknownArgumentError) || error.message.include?("was called with arguments")) &&
230
+ [ "down" ].include?(command)
231
+ # Return to let Thor proceed instead of raising the error
232
+ nil
233
+ else
234
+ # Fallback to standard Thor behavior for actual code bugs
235
+ super
236
+ end
237
+ end
186
238
  end
187
239
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JadeSystemsToolbox
4
+ class DockerInitializer
5
+ def initialize(compose_file: nil, database: nil, ruby_version: nil, distro_version: nil, end_to_end_tool: nil)
6
+ @end_to_end_tool = end_to_end_tool&.downcase&.strip
7
+ database = database.to_s.downcase.strip
8
+ @database = database unless %w[false none nil ""].include?(database)
9
+ @ruby_version = ruby_version&.downcase&.strip
10
+ @distro_version = distro_version&.downcase&.strip
11
+ end
12
+
13
+ def compose_file_path
14
+ base_dir = [ "rails-app", database ].compact.join("-")
15
+ Pathname("compose-files").join(*([ base_dir, end_to_end_tool, "compose.yml" ].compact))
16
+ end
17
+
18
+ def get_and_save_compose_files
19
+ compose_file = Http.get_file(url: Http.url(path: compose_file_path))
20
+ customize_compose_file!(compose_file)
21
+ File.write("compose.yml", compose_file)
22
+
23
+ case Gem::Platform.local.os
24
+ when "linux"
25
+ compose_override_file = Http.get_file(url: Http.url(path: "compose-files/Linux/compose.override.yml"))
26
+ File.write("compose.override.yml", compose_override_file)
27
+ end
28
+ end
29
+
30
+ def customize_compose_file!(base_file)
31
+ base_file.gsub!("${RUBY_VERSION:-4.0}", ruby_version) unless ruby_version.nil?
32
+ base_file.gsub!("${DISTRO_VERSION:-trixie}", distro_version) unless distro_version.nil?
33
+ base_file
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :end_to_end_tool
39
+ attr_reader :database
40
+ attr_reader :ruby_version
41
+ attr_reader :distro_version
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JadeSystemsToolbox
4
+ class Http
5
+ class << self
6
+ def url(path:)
7
+ base_url + path.to_s
8
+ end
9
+
10
+ def get_file(url:, redirects: 10)
11
+ raise Error.new("Too many redirects", options[:verbose]) if redirects.zero?
12
+
13
+ uri = URI.parse(url)
14
+ request = Net::HTTP.new(uri.host, uri.port)
15
+ request.use_ssl = true
16
+ response = request.get(uri.path)
17
+
18
+ case response
19
+ when Net::HTTPSuccess
20
+ response.body
21
+ when Net::HTTPRedirection
22
+ get_file(redirects: redirects - 1, url: response["location"])
23
+ else
24
+ response.value
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def base_url = "https://github.com/lcreid/jade_systems_toolbox/raw/refs/heads/main/"
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JadeSystemsToolbox
4
- VERSION = "0.1.4"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module JadeSystemsToolbox
6
+ class Vscode
7
+ def write_settings_json
8
+ target_file = target_directory.join("settings.json")
9
+ settings = if !target_file.exist? || target_file.empty?
10
+ {}
11
+ else
12
+ JSON.load_file(target_file)
13
+ end
14
+
15
+ settings["window.title"] = Pathname.pwd.basename
16
+ File.write(target_file, JSON.pretty_generate(settings))
17
+ end
18
+
19
+ def write_devcontainer_json
20
+ devcontainer_text = Http.get_file(url: Http.url(path: "templates/.devcontainer.json"))
21
+ devcontainer_json = JSON.parse(devcontainer_text, allow_comments: true)
22
+ unless Gem::Platform.local.os == "linux"
23
+ devcontainer_json["dockerComposeFile"] = [ "compose.yml" ]
24
+ end
25
+ Pathname(".devcontainer.json").write(JSON.pretty_generate(devcontainer_json))
26
+ end
27
+
28
+ def write_extensions_json
29
+ extensions_text = Http.get_file(url: Http.url(path: "templates/extensions.json"))
30
+ target_directory.join("extensions.json").write(extensions_text)
31
+ end
32
+
33
+ def get_and_write_settings_json
34
+ settings_text = Http.get_file(url: Http.url(path: "templates/settings.json"))
35
+ settings = JSON.parse(settings_text, allow_comments: true)
36
+ settings["window.title"] = Pathname.pwd.basename
37
+
38
+ target_file = target_directory.join("settings.json")
39
+ File.write(target_file, JSON.pretty_generate(settings))
40
+ end
41
+
42
+ private
43
+
44
+ def target_directory
45
+ @target_directory unless @target_directory.nil?
46
+
47
+ @target_directory = Pathname("./.vscode")
48
+ FileUtils.mkdir(@target_directory) unless @target_directory.exist?
49
+ @target_directory
50
+ end
51
+ end
52
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "debug"
4
3
  require "net/http"
5
4
  require "pathname"
6
5
  require "thor"
@@ -8,7 +7,10 @@ require "yaml"
8
7
 
9
8
  require_relative "jade_systems_toolbox/version"
10
9
  require_relative "jade_systems_toolbox/cli"
10
+ require_relative "jade_systems_toolbox/docker_initializer"
11
11
  require_relative "jade_systems_toolbox/error_reporter"
12
+ require_relative "jade_systems_toolbox/http"
13
+ require_relative "jade_systems_toolbox/vscode"
12
14
 
13
15
  module JadeSystemsToolbox
14
16
  class Error < StandardError
@@ -0,0 +1,36 @@
1
+ // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2
+ // README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
3
+ {
4
+ // Update the 'dockerComposeFile' list if you have more compose files or use different names.
5
+ "dockerComposeFile": ["compose.yml", "compose.override.yml"],
6
+
7
+ // The 'service' property is the name of the service for the container that VS Code should
8
+ // use. Update this value and .devcontainer/docker-compose.yml to the real service name.
9
+ "service": "web",
10
+
11
+ // The optional 'workspaceFolder' property is the path VS Code should open by default when
12
+ // connected. This is typically a file mount in .devcontainer/docker-compose.yml
13
+ "workspaceFolder": "/home/dev/app",
14
+ // "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}"
15
+
16
+ // Features to add to the dev container. More info: https://containers.dev/features.
17
+ // "features": {},
18
+
19
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
20
+ // "forwardPorts": [],
21
+
22
+ // Uncomment the next line if you want start specific services in your Docker Compose config.
23
+ // "runServices": [],
24
+
25
+ // Uncomment the next line if you want to keep your containers running after VS Code shuts down.
26
+ "shutdownAction": "none"
27
+
28
+ // Uncomment the next line to run commands after the container is created.
29
+ // "postCreateCommand": "cat /etc/os-release",
30
+
31
+ // Configure tool-specific properties.
32
+ // "customizations": {},
33
+
34
+ // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
35
+ // "remoteUser": "devcontainer"
36
+ }
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jade_systems_toolbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Larry Reid
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-11-14 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: thor
@@ -23,6 +24,7 @@ dependencies:
23
24
  - - "~>"
24
25
  - !ruby/object:Gem::Version
25
26
  version: '1.3'
27
+ description:
26
28
  email:
27
29
  - lcreid@jadesystems.ca
28
30
  executables:
@@ -41,9 +43,13 @@ files:
41
43
  - exe/tool
42
44
  - lib/jade_systems_toolbox.rb
43
45
  - lib/jade_systems_toolbox/cli.rb
46
+ - lib/jade_systems_toolbox/docker_initializer.rb
44
47
  - lib/jade_systems_toolbox/error_reporter.rb
48
+ - lib/jade_systems_toolbox/http.rb
45
49
  - lib/jade_systems_toolbox/version.rb
50
+ - lib/jade_systems_toolbox/vscode.rb
46
51
  - sig/jade_systems_toolbox.rbs
52
+ - templates/.devcontainer.json
47
53
  - templates/extensions.json
48
54
  - templates/settings.json
49
55
  homepage: https://github.com/lcreid/jade_systems_toolbox
@@ -53,6 +59,7 @@ metadata:
53
59
  homepage_uri: https://github.com/lcreid/jade_systems_toolbox
54
60
  source_code_uri: https://github.com/lcreid/jade_systems_toolbox
55
61
  changelog_uri: https://github.com/lcreid/jade_systems_toolbox/CHANGELOG.nd
62
+ post_install_message:
56
63
  rdoc_options: []
57
64
  require_paths:
58
65
  - lib
@@ -60,14 +67,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
67
  requirements:
61
68
  - - ">="
62
69
  - !ruby/object:Gem::Version
63
- version: '3.2'
70
+ version: '3.3'
64
71
  required_rubygems_version: !ruby/object:Gem::Requirement
65
72
  requirements:
66
73
  - - ">="
67
74
  - !ruby/object:Gem::Version
68
75
  version: '0'
69
76
  requirements: []
70
- rubygems_version: 4.0.6
77
+ rubygems_version: 3.5.22
78
+ signing_key:
71
79
  specification_version: 4
72
80
  summary: A collection of command line commands to support development.
73
81
  test_files: []