rails-dev 0.1.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 +7 -0
- data/CHANGELOG.md +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +134 -0
- data/docs/reference.md +220 -0
- data/exe/rails-dev +6 -0
- data/lib/rails_dev/child.rb +111 -0
- data/lib/rails_dev/cli/base.rb +41 -0
- data/lib/rails_dev/cli/main.rb +69 -0
- data/lib/rails_dev/cli/services.rb +28 -0
- data/lib/rails_dev/cli/shell.rb +25 -0
- data/lib/rails_dev/cli.rb +44 -0
- data/lib/rails_dev/commands.rb +115 -0
- data/lib/rails_dev/compose.rb +97 -0
- data/lib/rails_dev/configuration.rb +137 -0
- data/lib/rails_dev/cookies.rb +22 -0
- data/lib/rails_dev/endpoint.rb +105 -0
- data/lib/rails_dev/foreman.rb +45 -0
- data/lib/rails_dev/hooks.rb +43 -0
- data/lib/rails_dev/instance.rb +145 -0
- data/lib/rails_dev/output.rb +31 -0
- data/lib/rails_dev/portless.mjs +28 -0
- data/lib/rails_dev/portless.rb +110 -0
- data/lib/rails_dev/ports.rb +20 -0
- data/lib/rails_dev/process_table.rb +32 -0
- data/lib/rails_dev/rails_vite.rb +14 -0
- data/lib/rails_dev/railtie.rb +21 -0
- data/lib/rails_dev/service_ports.rb +88 -0
- data/lib/rails_dev/services.rb +37 -0
- data/lib/rails_dev/session.rb +215 -0
- data/lib/rails_dev/state.rb +60 -0
- data/lib/rails_dev/tailscale.rb +59 -0
- data/lib/rails_dev/version.rb +5 -0
- data/lib/rails_dev/vite.mjs +31 -0
- data/lib/rails_dev.rb +55 -0
- metadata +141 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "English"
|
|
4
|
+
require "forwardable"
|
|
5
|
+
|
|
6
|
+
module RailsDev
|
|
7
|
+
class Session
|
|
8
|
+
extend Forwardable
|
|
9
|
+
|
|
10
|
+
attr_reader :root, :runtime, :error
|
|
11
|
+
|
|
12
|
+
def_delegators :@commands, :env, :run, :capture, :say, :check_for_interrupt
|
|
13
|
+
def_delegator :@config, :enabled?
|
|
14
|
+
|
|
15
|
+
def initialize(root, config, tailscale: false, output: $stdout)
|
|
16
|
+
@root = root
|
|
17
|
+
@config = config
|
|
18
|
+
@tailscale = tailscale
|
|
19
|
+
@commands = Commands.new(root, output: output, shutdown_timeout: config.shutdown_timeout + 1)
|
|
20
|
+
@state = State.new(@commands)
|
|
21
|
+
@hooks = Hooks.new(config, self)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def start
|
|
25
|
+
@commands.with_signals do
|
|
26
|
+
@state.with_run do |runtime|
|
|
27
|
+
begin_run(runtime)
|
|
28
|
+
prepare_environment
|
|
29
|
+
@hooks.around { start_application }
|
|
30
|
+
ensure
|
|
31
|
+
@error = $ERROR_INFO
|
|
32
|
+
stop
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def url(name = @config.web)
|
|
38
|
+
@endpoints&.find { |endpoint| endpoint.name == name.to_s }&.url
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def begin_run(runtime)
|
|
44
|
+
@runtime = runtime
|
|
45
|
+
@instance = Instance.new(runtime)
|
|
46
|
+
@commands.instance = @instance
|
|
47
|
+
record_run("starting", owner: ProcessTable.new.identity(Process.pid), processes: [],
|
|
48
|
+
shutdown_timeout: @config.shutdown_timeout + 1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def record_run(status, **details)
|
|
52
|
+
@instance.record(name: @config.name, root: root, pid: Process.pid, status: status,
|
|
53
|
+
urls: Array(@endpoints).to_h { |endpoint| [endpoint.name, endpoint.url] }, **details)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def prepare_environment
|
|
57
|
+
env.merge!(@config.env)
|
|
58
|
+
@config.validate_connection_environment(env) if @config.compose_file
|
|
59
|
+
env.merge!("RAILS_ENV" => "development", "RACK_ENV" => "development", "RAILS_DEV_RUNTIME" => runtime,
|
|
60
|
+
"DEV_HOST" => "127.0.0.1", "PIDFILE" => File.join(runtime, "rails.pid"))
|
|
61
|
+
@foreman = Foreman.new(root, @config)
|
|
62
|
+
@endpoints = @foreman.endpoints
|
|
63
|
+
prepare_proxy if @tailscale || @config.https
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def prepare_proxy
|
|
67
|
+
@proxy = (@tailscale ? Tailscale : Portless).new(@commands)
|
|
68
|
+
@proxy.check
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def start_application
|
|
72
|
+
@state.synchronize("checkout:#{root}") do
|
|
73
|
+
check_existing_rails
|
|
74
|
+
prepare_services
|
|
75
|
+
start_web
|
|
76
|
+
end
|
|
77
|
+
@hooks.call(:after_ready)
|
|
78
|
+
report_ready
|
|
79
|
+
supervise
|
|
80
|
+
ensure
|
|
81
|
+
@error = $ERROR_INFO
|
|
82
|
+
@commands.stop
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def check_existing_rails
|
|
86
|
+
path = File.join(root, "tmp/pids/server.pid")
|
|
87
|
+
return unless File.file?(path)
|
|
88
|
+
|
|
89
|
+
pid = File.read(path).to_i
|
|
90
|
+
return unless pid.positive?
|
|
91
|
+
|
|
92
|
+
Process.kill(0, pid)
|
|
93
|
+
raise Error, "Rails is already running for this checkout. Stop it before starting rails-dev."
|
|
94
|
+
rescue Errno::ESRCH
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def prepare_services
|
|
99
|
+
if @config.compose_file
|
|
100
|
+
compose = Compose.new(@config, @commands, @state, runtime)
|
|
101
|
+
env.merge!(compose.start)
|
|
102
|
+
@state.synchronize("database:#{compose.lock_key}") { @hooks.call(:before_start) }
|
|
103
|
+
else
|
|
104
|
+
@hooks.call(:before_start)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def start_web
|
|
109
|
+
@state.synchronize("web-ports") do
|
|
110
|
+
5.times do
|
|
111
|
+
ports = []
|
|
112
|
+
@endpoints.each do |endpoint|
|
|
113
|
+
endpoint.allocate(excluding: ports)
|
|
114
|
+
ports << endpoint.port
|
|
115
|
+
end
|
|
116
|
+
return if launch_web
|
|
117
|
+
|
|
118
|
+
@commands.say("A selected port was taken during startup; selecting ports again.")
|
|
119
|
+
@commands.stop
|
|
120
|
+
end
|
|
121
|
+
raise Error, "Ports were repeatedly taken during startup. Try starting again."
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def launch_web
|
|
126
|
+
@deadline = @commands.monotonic_time + @config.startup_timeout
|
|
127
|
+
@processes = []
|
|
128
|
+
web = @endpoints.find { |endpoint| endpoint.name == @config.web.to_s }
|
|
129
|
+
@proxy&.start(@config.name, endpoints: @endpoints, web: web) do |process|
|
|
130
|
+
@processes << process
|
|
131
|
+
return false unless await_processes
|
|
132
|
+
end
|
|
133
|
+
start_foreman(web)
|
|
134
|
+
await_processes(check_endpoints: true)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def start_foreman(web)
|
|
138
|
+
@endpoints.each { |endpoint| env.merge!(endpoint.environment) }
|
|
139
|
+
env.merge!("RAILS_PORT" => web.port.to_s, "RAILS_DEV_URL" => web.url)
|
|
140
|
+
@application = @commands.start("foreman", @foreman.command, keep_stdin_open: true)
|
|
141
|
+
@processes << @application
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def await_processes(check_endpoints: false)
|
|
145
|
+
loop do
|
|
146
|
+
@commands.check_for_interrupt
|
|
147
|
+
return false if startup_failed?
|
|
148
|
+
return true if ready?(check_endpoints)
|
|
149
|
+
|
|
150
|
+
raise Error, startup_timeout(check_endpoints) if @commands.monotonic_time >= @deadline
|
|
151
|
+
|
|
152
|
+
sleep 0.05
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def startup_failed?
|
|
157
|
+
failed = @processes.find { |process| !process.running? }
|
|
158
|
+
return false unless failed
|
|
159
|
+
return true if failed.startup_conflict? && (failed != @application || ports_taken?)
|
|
160
|
+
|
|
161
|
+
process_failed(failed, "during startup")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def ports_taken?
|
|
165
|
+
@endpoints.any? { |endpoint| Ports.available(endpoint.port) != endpoint.port }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def process_failed(process, phase)
|
|
169
|
+
status = process.status.exitstatus || (128 + process.status.termsig)
|
|
170
|
+
raise Error.new("#{process.name} exited #{phase} (#{process.exit_reason}). See its output above.",
|
|
171
|
+
status: status.zero? ? 1 : status)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def ready?(check_endpoints)
|
|
175
|
+
@processes.all?(&:ready?) && (!check_endpoints || @endpoints.map { |endpoint| endpoint.ready?(@deadline) }.all?)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def startup_timeout(check_endpoints)
|
|
179
|
+
pending = @processes.filter_map do |process|
|
|
180
|
+
"#{process.name}: readiness message not received" unless process.ready?
|
|
181
|
+
end
|
|
182
|
+
pending.concat(@endpoints.filter_map(&:readiness_failure)) if check_endpoints
|
|
183
|
+
["Startup timed out after #{@config.startup_timeout}s.", *pending,
|
|
184
|
+
"Check the process output and readiness settings."].join("\n")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def report_ready
|
|
188
|
+
@commands.record_processes
|
|
189
|
+
record_run("ready")
|
|
190
|
+
@commands.say("Ready: #{url}")
|
|
191
|
+
@endpoints.reject { |endpoint| endpoint.name == @config.web.to_s }.each do |endpoint|
|
|
192
|
+
@commands.say("#{endpoint.name}: #{endpoint.url}")
|
|
193
|
+
end
|
|
194
|
+
@commands.say("Press Ctrl+C to stop the app processes.")
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def supervise
|
|
198
|
+
loop do
|
|
199
|
+
@commands.check_for_interrupt
|
|
200
|
+
failed = @processes.find { |process| !process.running? }
|
|
201
|
+
return if failed == @application && failed.status.success?
|
|
202
|
+
|
|
203
|
+
process_failed(failed, "while running") if failed
|
|
204
|
+
sleep 0.05
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def stop
|
|
209
|
+
@commands.stop
|
|
210
|
+
ensure
|
|
211
|
+
@error ||= $ERROR_INFO
|
|
212
|
+
@hooks.after_stop(error)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "json"
|
|
6
|
+
require "tmpdir"
|
|
7
|
+
|
|
8
|
+
module RailsDev
|
|
9
|
+
class State
|
|
10
|
+
attr_reader :directory
|
|
11
|
+
|
|
12
|
+
def initialize(commands = nil, base: ENV.fetch("XDG_STATE_HOME", File.join(Dir.home, ".local/state")))
|
|
13
|
+
@commands = commands
|
|
14
|
+
@directory = File.join(base, "rails-dev")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def with_run
|
|
18
|
+
FileUtils.mkdir_p(directory, mode: 0o700)
|
|
19
|
+
Dir.mktmpdir("run-", directory) do |runtime|
|
|
20
|
+
File.open(File.join(runtime, "owner.lock"), File::RDWR | File::CREAT, 0o600) do |owner|
|
|
21
|
+
owner.flock(File::LOCK_EX)
|
|
22
|
+
FileUtils.cp(File.join(__dir__, "vite.mjs"), File.join(runtime, "vite.mjs"))
|
|
23
|
+
yield runtime
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def runs
|
|
29
|
+
instances.filter_map(&:snapshot)
|
|
30
|
+
.sort_by { |run| run.values_at("name", "root", "pid") }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def stop(id, root: nil)
|
|
34
|
+
instance = instances.find { |run| run.id == id }
|
|
35
|
+
if !instance || (root && instance.snapshot&.fetch("root") != root)
|
|
36
|
+
raise Error, "Unknown instance: #{id}. See rails-dev list."
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
synchronize("stop:#{id}") { instance.stop }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def synchronize(key)
|
|
43
|
+
FileUtils.mkdir_p(directory, mode: 0o700)
|
|
44
|
+
path = File.join(directory, "#{Digest::SHA256.hexdigest(key)}.lock")
|
|
45
|
+
File.open(path, File::RDWR | File::CREAT, 0o600) do |file|
|
|
46
|
+
until file.flock(File::LOCK_EX | File::LOCK_NB)
|
|
47
|
+
@commands&.check_for_interrupt
|
|
48
|
+
sleep 0.05
|
|
49
|
+
end
|
|
50
|
+
yield
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def instances
|
|
57
|
+
Dir.glob(File.join(directory, "run-*/instance.json")).map { |path| Instance.new(File.dirname(path)) }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module RailsDev
|
|
6
|
+
class Tailscale
|
|
7
|
+
def initialize(commands)
|
|
8
|
+
@commands = commands
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def check
|
|
12
|
+
status = JSON.parse(@commands.capture("tailscale", "status", "--json"))
|
|
13
|
+
node = status["Self"] || {}
|
|
14
|
+
@hostname = node.fetch("DNSName", "").delete_suffix(".")
|
|
15
|
+
unless status["BackendState"] == "Running" && !@hostname.empty?
|
|
16
|
+
raise Error, "Connect Tailscale before using --tailscale. Run tailscale status."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
return if https_enabled?(node)
|
|
20
|
+
|
|
21
|
+
raise Error, "Enable HTTPS certificates in your tailnet's DNS settings before using --tailscale."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def start(_name, endpoints:, **)
|
|
25
|
+
endpoints.zip(available_ports(endpoints.size)).each do |endpoint, https|
|
|
26
|
+
endpoint.url = origin(https)
|
|
27
|
+
yield @commands.start("tailscale-#{endpoint.name}",
|
|
28
|
+
["tailscale", "serve", "--bg=false", "--https=#{https}", "http://127.0.0.1:#{endpoint.port}"],
|
|
29
|
+
ready_message: "Press Ctrl+C to exit.")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def https_enabled?(node)
|
|
36
|
+
capabilities = Array(node["Capabilities"]) + node.fetch("CapMap", {}).keys
|
|
37
|
+
capabilities.any? { |capability| capability == "https" || capability.end_with?("/https") }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def available_ports(count)
|
|
41
|
+
config = JSON.parse(@commands.capture("tailscale", "serve", "status", "--json"))
|
|
42
|
+
used = [config, *config.fetch("Foreground", {}).values].flat_map { |entry| used_ports(entry) }
|
|
43
|
+
ports = [443].chain(8443..65_535).lazy.reject { |port| used.include?(port) }.take(count).to_a
|
|
44
|
+
raise Error, "Tailscale needs #{count} available HTTPS ports." unless ports.size == count
|
|
45
|
+
|
|
46
|
+
ports
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def used_ports(config)
|
|
50
|
+
config.fetch("TCP", {}).keys.map(&:to_i) + config.fetch("AllowFunnel", {}).filter_map do |host, enabled|
|
|
51
|
+
host.split(":").last.to_i if enabled
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def origin(port)
|
|
56
|
+
URI::HTTPS.build(host: @hostname, port: port).to_s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
|
|
3
|
+
export default function railsDev() {
|
|
4
|
+
const runtime = process.env.RAILS_DEV_RUNTIME
|
|
5
|
+
const origin = new URL(process.env.RAILS_DEV_VITE_URL)
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
rails: { devMetaFile: join(runtime, 'rails-vite.json') },
|
|
9
|
+
plugin: {
|
|
10
|
+
name: 'rails-dev',
|
|
11
|
+
apply: 'serve',
|
|
12
|
+
config: () => ({
|
|
13
|
+
cacheDir: join(runtime, 'vite-cache'),
|
|
14
|
+
define: {
|
|
15
|
+
'import.meta.env.VITE_RAILS_DEV_COOKIE_PREFIX': JSON.stringify(`rails-dev-${process.env.RAILS_PORT}-`),
|
|
16
|
+
},
|
|
17
|
+
server: {
|
|
18
|
+
host: '127.0.0.1',
|
|
19
|
+
origin: origin.origin,
|
|
20
|
+
allowedHosts: [origin.hostname],
|
|
21
|
+
cors: { origin: process.env.RAILS_DEV_URL },
|
|
22
|
+
ws: {
|
|
23
|
+
protocol: origin.protocol === 'https:' ? 'wss' : 'ws',
|
|
24
|
+
host: origin.hostname,
|
|
25
|
+
clientPort: Number(origin.port || (origin.protocol === 'https:' ? 443 : 80)),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
}
|
data/lib/rails_dev.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rails_dev/version"
|
|
4
|
+
|
|
5
|
+
module RailsDev
|
|
6
|
+
class Error < StandardError
|
|
7
|
+
attr_reader :status
|
|
8
|
+
|
|
9
|
+
def initialize(message, status: 1)
|
|
10
|
+
@status = status
|
|
11
|
+
super(message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Interrupted < Error
|
|
16
|
+
attr_reader :signal
|
|
17
|
+
|
|
18
|
+
def initialize(signal)
|
|
19
|
+
@signal = signal
|
|
20
|
+
super("Stopped.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.configure
|
|
25
|
+
raise Error, "Configuration must be loaded by rails-dev." unless @configuration
|
|
26
|
+
|
|
27
|
+
yield @configuration
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.load_configuration(configuration, paths)
|
|
31
|
+
@configuration = configuration
|
|
32
|
+
paths.each { |path| load path if File.file?(path) }
|
|
33
|
+
ensure
|
|
34
|
+
@configuration = nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
require_relative "rails_dev/configuration"
|
|
39
|
+
require_relative "rails_dev/ports"
|
|
40
|
+
require_relative "rails_dev/endpoint"
|
|
41
|
+
require_relative "rails_dev/foreman"
|
|
42
|
+
require_relative "rails_dev/output"
|
|
43
|
+
require_relative "rails_dev/process_table"
|
|
44
|
+
require_relative "rails_dev/instance"
|
|
45
|
+
require_relative "rails_dev/child"
|
|
46
|
+
require_relative "rails_dev/commands"
|
|
47
|
+
require_relative "rails_dev/state"
|
|
48
|
+
require_relative "rails_dev/service_ports"
|
|
49
|
+
require_relative "rails_dev/compose"
|
|
50
|
+
require_relative "rails_dev/services"
|
|
51
|
+
require_relative "rails_dev/portless"
|
|
52
|
+
require_relative "rails_dev/tailscale"
|
|
53
|
+
require_relative "rails_dev/hooks"
|
|
54
|
+
require_relative "rails_dev/session"
|
|
55
|
+
require_relative "rails_dev/cli"
|
metadata
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails-dev
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ali Hamdi Ali Fadel
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bundler
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.5'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '5'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '2.5'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '5'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: foreman
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0.90'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0.90'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: json
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '2.7'
|
|
53
|
+
type: :runtime
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '2.7'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: thor
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '1.5'
|
|
67
|
+
type: :runtime
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '1.5'
|
|
74
|
+
email:
|
|
75
|
+
- aliosm1997@gmail.com
|
|
76
|
+
executables:
|
|
77
|
+
- rails-dev
|
|
78
|
+
extensions: []
|
|
79
|
+
extra_rdoc_files: []
|
|
80
|
+
files:
|
|
81
|
+
- CHANGELOG.md
|
|
82
|
+
- LICENSE.txt
|
|
83
|
+
- README.md
|
|
84
|
+
- docs/reference.md
|
|
85
|
+
- exe/rails-dev
|
|
86
|
+
- lib/rails_dev.rb
|
|
87
|
+
- lib/rails_dev/child.rb
|
|
88
|
+
- lib/rails_dev/cli.rb
|
|
89
|
+
- lib/rails_dev/cli/base.rb
|
|
90
|
+
- lib/rails_dev/cli/main.rb
|
|
91
|
+
- lib/rails_dev/cli/services.rb
|
|
92
|
+
- lib/rails_dev/cli/shell.rb
|
|
93
|
+
- lib/rails_dev/commands.rb
|
|
94
|
+
- lib/rails_dev/compose.rb
|
|
95
|
+
- lib/rails_dev/configuration.rb
|
|
96
|
+
- lib/rails_dev/cookies.rb
|
|
97
|
+
- lib/rails_dev/endpoint.rb
|
|
98
|
+
- lib/rails_dev/foreman.rb
|
|
99
|
+
- lib/rails_dev/hooks.rb
|
|
100
|
+
- lib/rails_dev/instance.rb
|
|
101
|
+
- lib/rails_dev/output.rb
|
|
102
|
+
- lib/rails_dev/portless.mjs
|
|
103
|
+
- lib/rails_dev/portless.rb
|
|
104
|
+
- lib/rails_dev/ports.rb
|
|
105
|
+
- lib/rails_dev/process_table.rb
|
|
106
|
+
- lib/rails_dev/rails_vite.rb
|
|
107
|
+
- lib/rails_dev/railtie.rb
|
|
108
|
+
- lib/rails_dev/service_ports.rb
|
|
109
|
+
- lib/rails_dev/services.rb
|
|
110
|
+
- lib/rails_dev/session.rb
|
|
111
|
+
- lib/rails_dev/state.rb
|
|
112
|
+
- lib/rails_dev/tailscale.rb
|
|
113
|
+
- lib/rails_dev/version.rb
|
|
114
|
+
- lib/rails_dev/vite.mjs
|
|
115
|
+
homepage: https://github.com/milkstrawai/rails-dev
|
|
116
|
+
licenses:
|
|
117
|
+
- MIT
|
|
118
|
+
metadata:
|
|
119
|
+
allowed_push_host: https://rubygems.org
|
|
120
|
+
homepage_uri: https://github.com/milkstrawai/rails-dev
|
|
121
|
+
source_code_uri: https://github.com/milkstrawai/rails-dev
|
|
122
|
+
changelog_uri: https://github.com/milkstrawai/rails-dev/blob/main/CHANGELOG.md
|
|
123
|
+
rubygems_mfa_required: 'true'
|
|
124
|
+
rdoc_options: []
|
|
125
|
+
require_paths:
|
|
126
|
+
- lib
|
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '3.4'
|
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - ">="
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '0'
|
|
137
|
+
requirements: []
|
|
138
|
+
rubygems_version: 3.6.9
|
|
139
|
+
specification_version: 4
|
|
140
|
+
summary: Available ports, HTTPS, and Tailscale for Rails development with Foreman.
|
|
141
|
+
test_files: []
|