rb-portless 0.3.0 → 0.3.1.dev.20260630.bb085fc

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: e49a039bffaf418a826c61337253e8cf3f46c54b500f30936fba6e8153f96804
4
- data.tar.gz: b4c6fad1e96ac1c2fd11a9366ad36273af95976e2abcdb1387d4b1b60dd1bd27
3
+ metadata.gz: b934378f7d59599eebee06f392753bc4d33e5dea46132ca43ff4a13c9c587367
4
+ data.tar.gz: 4756b624abf40832afbd71ed570548e12228a26df9740e821fc901eba5fe74ff
5
5
  SHA512:
6
- metadata.gz: 7754996324bcc3c53dac67a3c2999cc2c57d25d0bc6126ac4041d6dfb2936c49ca675b63ddb1fb24d86ad5b3a49b76b76e7397ae20f32f0d637f3b3f63e4f640
7
- data.tar.gz: ec5a8e7ff23b18f7766015e11627938776d88eb672fe174a3902e273af14d044f69db69a146de48284799e08de5425544837a94160cf3c2ddf6eff7faa447673
6
+ metadata.gz: 223dc8fc2262c78440ddb19a1ebfbf31ed05092dab9180bd7b54808d0976ab885d4d3e25f3c8890a90f8eb8fda6f733c5111283575223bb0581e146439d724b0
7
+ data.tar.gz: b8e2c24702cb74ea1de7b60d8db33ac1c8a504928c04bc53d516370c980733525610b0bf44d924cebfe3c6700df95eb128ef04dbeba9b67b51bdb522a189035c
data/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/), and the project adheres to
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.3.1]
8
+
9
+ ### Changed (internal — no behaviour change)
10
+
11
+ - **Deduped the two run paths.** `Runner` and `Multi` shared three methods
12
+ (`child_env`, `display_url`, `ensure_trusted`); they now live in a `RunSupport`
13
+ mixin. Multi-app mode picks up the actionable first-run CA-trust hints the
14
+ single-app path already had.
15
+ - **Dropped dead code** — `CLI#todo`, the unread `Config#script`/`DEFAULT_SCRIPT`,
16
+ `State.ca_serial` (no on-disk serial; the native CA sets it on the cert), and
17
+ `Hosts.managed_hostnames` (no caller).
18
+
7
19
  ## [0.3.0]
8
20
 
9
21
  ### Fixed / hardened
data/lib/portless/cli.rb CHANGED
@@ -205,11 +205,6 @@ module Portless
205
205
  i ? Integer(args[i + 1], exception: false) : nil
206
206
  end
207
207
 
208
- def todo(name, desc, _args = nil)
209
- warn "rb-portless #{name}: #{desc} — not yet implemented (#{Portless::VERSION})"
210
- exit 1
211
- end
212
-
213
208
  def rest(command)
214
209
  @argv.first == command ? @argv[1..] : @argv
215
210
  end
@@ -7,9 +7,7 @@ module Portless
7
7
  # defaults. Mirrors portless's config.ts/auto.ts: name is inferred from the
8
8
  # config, the git root, or the directory; tld defaults to "localhost".
9
9
  class Config
10
- DEFAULT_SCRIPT = "dev"
11
-
12
- attr_reader :name, :tld, :script, :app_port, :tls, :apps
10
+ attr_reader :name, :tld, :app_port, :tls, :apps
13
11
 
14
12
  def self.load(dir = Dir.pwd)
15
13
  new(read_file(dir), dir)
@@ -19,7 +17,6 @@ module Portless
19
17
  @dir = dir
20
18
  @name = sanitize_label(data["name"] || infer_name(dir))
21
19
  @tld = (data["tld"] || Constants::DEFAULT_TLD).to_s
22
- @script = data["script"] || DEFAULT_SCRIPT
23
20
  @app_port = data["appPort"] || data["app_port"]
24
21
  @tls = data.fetch("tls", true)
25
22
  # Monorepo: { "apps": { "web": "bin/rails server", "api": "node api.js" } }.
@@ -24,11 +24,6 @@ module Portless
24
24
  write(strip_block(read))
25
25
  end
26
26
 
27
- def managed_hostnames
28
- read[/#{Regexp.escape(Constants::HOSTS_BEGIN)}\n(.*?)#{Regexp.escape(Constants::HOSTS_END)}/m, 1]
29
- .to_s.scan(/^\s*127\.0\.0\.1\s+(\S+)/).flatten
30
- end
31
-
32
27
  def build_block(hostnames)
33
28
  return "" if hostnames.empty?
34
29
  lines = hostnames.uniq.map { |h| "127.0.0.1\t#{h}" }
@@ -7,6 +7,8 @@ module Portless
7
7
  # supervised + torn down together. Ruby sets env per-spawn, so there's no
8
8
  # NODE_OPTIONS loader hack (cf. portless's turbo.ts).
9
9
  class Multi
10
+ include RunSupport
11
+
10
12
  App = Struct.new(:name, :hostname, :port, :url, :pid, keyword_init: true)
11
13
 
12
14
  def initialize(config: Config.load, route_store: RouteStore.new)
@@ -18,7 +20,7 @@ module Portless
18
20
  def run
19
21
  raise Error, "no apps defined — add an \"apps\" map to portless.json" if @config.apps.empty?
20
22
 
21
- ensure_trusted if @config.tls
23
+ ensure_trusted
22
24
  proxy_port = Daemon.ensure_running(tls: @config.tls)
23
25
  @apps = @config.apps.map { |name, command| start_app(name, command, proxy_port) }
24
26
 
@@ -57,27 +59,5 @@ module Portless
57
59
  @apps.each { |app| @route_store.remove(app.hostname, owner_pid: Process.pid) }
58
60
  kill_all("TERM")
59
61
  end
60
-
61
- def child_env(port, url)
62
- {
63
- "PORT" => port.to_s,
64
- "HOST" => "127.0.0.1",
65
- "PORTLESS_URL" => url,
66
- "SSL_CERT_FILE" => (File.exist?(State.ca_cert) ? State.ca_cert : nil)
67
- }.compact
68
- end
69
-
70
- def display_url(hostname, proxy_port)
71
- scheme = @config.tls ? "https" : "http"
72
- default = @config.tls ? Constants::HTTPS_PORT : Constants::HTTP_PORT
73
- suffix = proxy_port && proxy_port != default ? ":#{proxy_port}" : ""
74
- "#{scheme}://#{hostname}#{suffix}"
75
- end
76
-
77
- def ensure_trusted
78
- Trust.install! if Privilege.interactive? && !Trust.trusted?
79
- rescue Portless::Error
80
- nil
81
- end
82
62
  end
83
63
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Portless
4
+ # Shared bits of the two run paths (single-app Runner + multi-app Multi):
5
+ # the child env, the display URL, and first-run CA trust. Both expect a
6
+ # `@config`.
7
+ module RunSupport
8
+ private
9
+
10
+ def child_env(port, url)
11
+ {
12
+ "PORT" => port.to_s,
13
+ "HOST" => "127.0.0.1",
14
+ "PORTLESS_URL" => url,
15
+ # Let the app's own server-side TLS verification trust our CA.
16
+ "SSL_CERT_FILE" => (File.exist?(State.ca_cert) ? State.ca_cert : nil)
17
+ }.compact
18
+ end
19
+
20
+ def display_url(hostname, proxy_port)
21
+ scheme = @config.tls ? "https" : "http"
22
+ default = @config.tls ? Constants::HTTPS_PORT : Constants::HTTP_PORT
23
+ suffix = proxy_port && proxy_port != default ? ":#{proxy_port}" : ""
24
+ "#{scheme}://#{hostname}#{suffix}"
25
+ end
26
+
27
+ # Trust the local CA on first run (HTTPS only, interactive; never blocks the
28
+ # run), so HTTPS works without browser warnings — like portless.
29
+ def ensure_trusted
30
+ return unless @config.tls
31
+ return if Trust.trusted?
32
+
33
+ unless Privilege.interactive?
34
+ warn "rb-portless: CA not trusted — run `rb-portless trust` (HTTPS shows warnings until then)"
35
+ return
36
+ end
37
+
38
+ warn "rb-portless: trusting the local CA (first run)…"
39
+ Trust.install!
40
+ rescue Portless::Error => e
41
+ warn "rb-portless: couldn't auto-trust the CA (#{e.message}) — run `rb-portless trust`"
42
+ end
43
+ end
44
+ end
@@ -6,6 +6,8 @@ module Portless
6
6
  # group (forwarding signals), and deregister on exit. Rails/puma respect PORT
7
7
  # natively. Mirrors portless's runApp/spawnCommand.
8
8
  class Runner
9
+ include RunSupport
10
+
9
11
  def initialize(command:, config: Config.load, route_store: RouteStore.new, options: {})
10
12
  @command = Array(command)
11
13
  @config = config
@@ -22,7 +24,7 @@ module Portless
22
24
  hostname = @config.hostname
23
25
 
24
26
  warn "rb-portless: #{@config.tld_warning}" if @config.tld_warning
25
- ensure_trusted if @config.tls
27
+ ensure_trusted
26
28
  proxy_port = Daemon.ensure_running(tls: @config.tls)
27
29
  @route_store.add(hostname: hostname, port: port, pid: Process.pid, force: true)
28
30
 
@@ -41,13 +43,6 @@ module Portless
41
43
 
42
44
  private
43
45
 
44
- def display_url(hostname, proxy_port)
45
- scheme = @config.tls ? "https" : "http"
46
- default = @config.tls ? Constants::HTTPS_PORT : Constants::HTTP_PORT
47
- suffix = proxy_port && proxy_port != default ? ":#{proxy_port}" : ""
48
- "#{scheme}://#{hostname}#{suffix}"
49
- end
50
-
51
46
  # ── LAN mode (--lan) ──────────────────────────────────────────────────
52
47
  # Register a `<name>.local` route, publish it over mDNS, and surface the URL
53
48
  # so phones/tablets on the Wi-Fi can reach the app.
@@ -84,24 +79,6 @@ module Portless
84
79
  Share::Tailscale.stop(@tailscale) if @tailscale
85
80
  end
86
81
 
87
- # Trust the local CA on first run (like portless), so HTTPS works without
88
- # browser warnings out of the box. Interactive only — macOS prompts for
89
- # keychain auth; in CI/no-TTY we skip with a hint rather than hang. Never
90
- # blocks the run if trusting fails.
91
- def ensure_trusted
92
- return if Trust.trusted?
93
-
94
- unless Privilege.interactive?
95
- warn "rb-portless: CA not trusted — run `rb-portless trust` (HTTPS shows warnings until then)"
96
- return
97
- end
98
-
99
- warn "rb-portless: trusting the local CA (first run)…"
100
- Trust.install!
101
- rescue Portless::Error => e
102
- warn "rb-portless: couldn't auto-trust the CA (#{e.message}) — run `rb-portless trust`"
103
- end
104
-
105
82
  # Run the child in its own process group so we can signal the whole tree,
106
83
  # forwarding INT/TERM and propagating its exit status.
107
84
  def supervise(command, port, url)
@@ -123,16 +100,6 @@ module Portless
123
100
  nil
124
101
  end
125
102
 
126
- def child_env(port, url)
127
- {
128
- "PORT" => port.to_s,
129
- "HOST" => "127.0.0.1",
130
- "PORTLESS_URL" => url,
131
- # Let the app's own server-side TLS verification trust our CA.
132
- "SSL_CERT_FILE" => (File.exist?(State.ca_cert) ? State.ca_cert : nil)
133
- }.compact
134
- end
135
-
136
103
  # Explicit command wins; bare `rb-portless` falls back to the project's dev
137
104
  # runner (bin/dev, then bin/rails server).
138
105
  def resolved_command
@@ -29,7 +29,6 @@ module Portless
29
29
  def proxy_log = path("proxy.log")
30
30
  def ca_cert = path("ca.pem")
31
31
  def ca_key = path("ca-key.pem")
32
- def ca_serial = path("ca.srl")
33
32
  def ca_trusted_marker = path("ca.trusted")
34
33
  def host_certs_dir = path("host-certs")
35
34
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Portless
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1.dev.20260630.bb085fc"
5
5
  end
data/lib/rb-portless.rb CHANGED
@@ -22,6 +22,7 @@ require_relative "portless/lan_ip"
22
22
  require_relative "portless/mdns"
23
23
  require_relative "portless/share/ngrok"
24
24
  require_relative "portless/share/tailscale"
25
+ require_relative "portless/run_support"
25
26
  require_relative "portless/runner"
26
27
  require_relative "portless/multi"
27
28
  require_relative "portless/cli"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rb-portless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1.dev.20260630.bb085fc
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Afonso
@@ -74,6 +74,7 @@ files:
74
74
  - lib/portless/rails.rb
75
75
  - lib/portless/rails_hosts.rb
76
76
  - lib/portless/route_store.rb
77
+ - lib/portless/run_support.rb
77
78
  - lib/portless/runner.rb
78
79
  - lib/portless/service.rb
79
80
  - lib/portless/share/ngrok.rb