Module: Copse
- Defined in:
- lib/copse.rb,
lib/copse/railtie.rb,
lib/copse/session.rb,
lib/copse/version.rb,
lib/copse/database.rb,
lib/copse/procfile.rb,
lib/copse/worktree.rb,
lib/copse/url_options.rb,
lib/generators/copse/install_generator.rb
Overview
Copse gives every Rails app and every git worktree its own hostname and its own port, derived rather than assigned, so nothing collides and nothing has to be chosen.
Defined Under Namespace
Modules: Database, Generators, UrlOptions Classes: Procfile, Railtie, Session, Worktree
Constant Summary collapse
- PORT_RANGE =
The port space derivation draws from.
(3000..9999).freeze
- RESERVED_PORTS =
Well-known service ports inside PORT_RANGE. These are excluded from the available set rather than probed around, so derivation stays a pure function of the hostname -- a reserved port is unreachable by construction.
Changing this list shifts every index in AVAILABLE_PORTS above the change and therefore moves most derived ports. That is a breaking change to the promise that teammates derive the same port (R4); it requires a major version bump.
[ 3036, # vite_ruby dev server default 3306, # MySQL 3389, # RDP 4200, # Angular dev server 4369, # Erlang EPMD 5000, # macOS AirPlay Receiver, Flask 5060, # SIP 5173, # Vite default 5432, # PostgreSQL 5601, # Kibana 5672, # RabbitMQ 5900, # VNC 6379, # Redis 6432, # PgBouncer 7000, # macOS AirPlay Receiver, Cassandra 8000, # common alternate HTTP 8025, # Mailhog / Mailpit web UI 8080, # common alternate HTTP 8443, # common alternate HTTPS 8500, # Consul 8888, # Jupyter 9000, # PHP-FPM, SonarQube 9092, # Kafka 9200, # Elasticsearch 9418 # git protocol ].freeze
- AVAILABLE_PORTS =
The ordered set derivation indexes into. Frozen at load: there is no configuration accessor, so there is no memoized state that can go stale.
(PORT_RANGE.to_a - RESERVED_PORTS).freeze
- PROCESS_MANAGERS =
The supervisors
bin/devcan be generated against. Only the supervisor differs: the derivation is a pure function of the worktree and knows nothing about either one. %w[foreman overmind].freeze
- VERSION =
"0.1.0"
Class Method Summary collapse
-
.companion_port_for(hostname) ⇒ Object
A second port derived from the same hostname, for a JS bundler's own dev server.
-
.port_for(hostname) ⇒ Object
The derived port for a hostname.
-
.start(root: Dir.pwd, process_manager: :foreman, args: []) ⇒ Object
Boots the app.
Class Method Details
.companion_port_for(hostname) ⇒ Object
A second port derived from the same hostname, for a JS bundler's own dev server. Salting keeps it stable (R4) and inside the available set (so it can never land on a reserved service port) while guaranteeing it differs from the primary port for that same hostname.
92 93 94 95 96 97 98 99 |
# File 'lib/copse.rb', line 92 def self.companion_port_for(hostname) primary = port_for(hostname) index = Zlib.crc32("vite:#{hostname}") % AVAILABLE_PORTS.size candidate = AVAILABLE_PORTS.fetch(index) return candidate unless candidate == primary AVAILABLE_PORTS.fetch((index + 1) % AVAILABLE_PORTS.size) end |
.port_for(hostname) ⇒ Object
The derived port for a hostname. A pure function: same hostname, same port, on every machine, in every terminal, across reboots and Ruby versions.
84 85 86 |
# File 'lib/copse.rb', line 84 def self.port_for(hostname) AVAILABLE_PORTS.fetch(Zlib.crc32(hostname) % AVAILABLE_PORTS.size) end |
.start(root: Dir.pwd, process_manager: :foreman, args: []) ⇒ Object
Boots the app. This is what bin/dev calls.
Returns the exit status of the foreground process so bin/dev can propagate
it, which matters for shell && chains and scripted use.
process_manager: :overmind hands the whole Procfile to Overmind instead,
replacing this process, and args is forwarded to it (bin/dev -l web).
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/copse.rb', line 70 def self.start(root: Dir.pwd, process_manager: :foreman, args: []) session = Session.new(Worktree.new(root)) # `exec_overmind` never returns, so falling through to the foreman session # means overmind is not installed *on this machine*. That is a fallback rather # than an error on purpose: `bin/dev` is committed, and a teammate without # overmind should still boot. session.exec_overmind(args) if process_manager.to_s == "overmind" && session.overmind_available? session.start end |