tina4ruby 3.13.83 → 3.13.84

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: 047daf112e32975b3a932f37b8cc266495c67744228dad532831ecfc918d76d1
4
- data.tar.gz: 06a13610c6f4dca323533afda0170fe9ab8b80c8181bd21ee3cc9743314704c3
3
+ metadata.gz: a44456ebacba7cc7b8a9b8ffe8a973411c13f8055e3297a9bfb1cbb370d4397b
4
+ data.tar.gz: 1fce4c668f1439fd7c057a4b94e804670d7ce4672a0641f0a325e343e44bba3b
5
5
  SHA512:
6
- metadata.gz: 9836d9caa0a14bcdebaca2effb98265c76a326c21e4d67a50daa6a22f2eef134fce9cf31ab068a60da688b2daa6fc0717a0244d58b1f17116a501a2f8ab91685
7
- data.tar.gz: d6fae651771e754bf8a3bb5fd9a73aee386765bf0574abb285a1eccea1fefe294e96d1e15913b3b413f380d2c239881b2cb70431956a67bb3282d132068c97f2
6
+ metadata.gz: 9d18d9143115b94beb1f6a4c4e067702f3127c2f05b24dc29a0c9efe04419622d2bb3c8bf94a19c61d1dad38f75ba2cc76d3d15fd530e51ee22198990584173d
7
+ data.tar.gz: 506e937dc17782c347efc91af039b21ea9e73e76fd92457bc8eab345ab27a7f6bc454eafca2d999a930053494926450f5d615eb871463577f2b5983d127f5f6c
data/CHANGELOG.md CHANGED
@@ -12,13 +12,6 @@ UNRELEASED work. When a version ships, its notes go to the release notes above.
12
12
 
13
13
  ## Unreleased
14
14
 
15
- ### Added
16
-
17
- - **MQTT 3.1.1 client** (`Tina4::Mqtt` / `Tina4::MqttMessage`), zero-dependency (stdlib `socket` +
18
- lazily-required `openssl`), verified against a real broker with no mocks. publish/subscribe/consume,
19
- QoS 0/1, retained, Last Will, per-client TLS, QoS 2 refused loudly. Ruby ships all 97 shared
20
- features plus its native ERB engine, for **98 built-in features**.
21
-
22
15
  ### Changed
23
16
 
24
17
  - Internal: the SQL dialect-translation file is renamed
@@ -30,26 +23,31 @@ UNRELEASED work. When a version ships, its notes go to the release notes above.
30
23
 
31
24
  ### Fixed
32
25
 
33
- - **Security: the bundled Swagger UI static assets now honour the swagger gate.** `/swagger`,
34
- `/swagger/`, `/swagger/index.html` and `/swagger/oauth2-redirect.html` were served from the
35
- framework's own public directory BEFORE route matching (with directory-index resolution turning
36
- `/swagger` into `swagger/index.html`), so a production server with `TINA4_SWAGGER_ENABLED=false`
37
- still served the whole UI while `/swagger/openapi.json` correctly 404'd. Static serving now checks
38
- the gate before it resolves an index. Bite-verified lock-in test. (python#97)
39
- - **The startup banner advertises only a surface that answers.** The `Swagger:` and `Dashboard:`
40
- rows printed unconditionally, so a production log claimed a dev surface was exposed and a
41
- developer following the link hit a 404. Each row is now built by one pure helper of
42
- (port, swagger_enabled, dev_admin_enabled), unit tested rather than inferred from stdout.
43
- (python#99)
44
- - **MQTT TLS tests verify the CA before trusting it.** A stale CA file in the shared temp directory
45
- made six TLS tests FAIL instead of skip, in all four frameworks, pointing at correct TLS code.
46
- The suites now confirm the CA actually validates the broker certificate before treating the TLS
47
- environment as present. (python#98)
48
- - **The gemspec declares `logger` and `base64`.** Ruby 4 dropped both from the default gems.
49
- `tina4ruby` requires `logger` and nothing in its transitive closure provided it, so a fresh
50
- install on Ruby 4 could fail at require time. `base64` is satisfied through `jwt` today and is
51
- declared directly so a change there cannot break us.
52
-
26
+ - **`tina4 deploy docker` produced images that could not start.** Of the eight
27
+ Dockerfile generators in the stack (four templates in the `tina4` CLI plus one
28
+ in each framework's own CLI), exactly one was correct. Python named
29
+ `python -m tina4_python.cli`, a package with no `__main__.py`, so the container
30
+ died on startup; PHP ran `php index.php <addr>`, but `App::run(?host, port)`
31
+ never reads argv so the address was dropped and production never engaged;
32
+ Node named a path that exists only inside the tina4-nodejs monorepo and
33
+ depended on tsx, which `npm ci --omit=dev` strips. Every generator now names a
34
+ published entry point and requests production. Verified by scaffolding,
35
+ generating, building and running a container for all four languages.
36
+ - **`serve` no longer kills PID 1.** The port-reclaim step read `lsof -ti`
37
+ without validating it. Where lsof prints a different shape, a non-numeric field
38
+ coerced to 0 or 1 -- and signalling PID 0 hits every process in the caller's
39
+ own process group. In a container the server IS PID 1, so it killed itself
40
+ (Node logged "Killed existing process on port 7148 (PID: 1 ...)" then exited
41
+ 143; PHP logged the same attempt and survived by luck). Reclaiming is now
42
+ skipped inside a container, only all-digit PIDs are accepted, and PID 0, PID 1
43
+ and the current process are never signalled.
44
+ - **The Ruby image builds.** The builder stage was `ruby:3.3-slim`, which has no
45
+ compiler, so `bundle install` failed with `Gem::Ext::BuildError` on the first
46
+ native extension (date, json, nio4r, sqlite3 are all in the default set). The
47
+ builder is now the full `ruby:3.3` image and the slim runtime installs
48
+ `libsqlite3-0`. Bundler's `deployment true` is gone: it demands a lockfile
49
+ matching the image's bundler exactly, so a lockfile from a newer bundler on a
50
+ developer machine hard-failed the build.
53
51
 
54
52
  ## Earlier history (pre-3.x)
55
53
 
data/lib/tina4/cli.rb CHANGED
@@ -340,18 +340,81 @@ module Tina4
340
340
  end
341
341
 
342
342
  # Kill any process listening on the given port. Returns true if killed.
343
+ # True when this process is running inside a container.
344
+ #
345
+ # Reclaiming a port makes sense on a dev machine, where a previous
346
+ # `tina4 serve` may still hold it. Inside a container the server IS the
347
+ # container, so there is no stale sibling to reclaim from -- and trying is
348
+ # actively dangerous (see #kill_process_on_port).
349
+ def in_container?
350
+ return true if File.exist?("/.dockerenv") || File.exist?("/run/.containerenv")
351
+
352
+ blob = File.read("/proc/1/cgroup")
353
+ blob.include?("docker") || blob.include?("containerd") || blob.include?("kubepods")
354
+ rescue SystemCallError
355
+ false
356
+ end
357
+
358
+ # Kill any process listening on `port`. Returns true if anything was killed.
359
+ #
360
+ # Every PID is validated before use. `pid.to_i` on a non-numeric field
361
+ # yields 0, and Process.kill("TERM", 0) signals EVERY process in the
362
+ # caller's own process group -- the server kills itself. That is exactly
363
+ # what happened in a container: "Killed existing process on port 7148
364
+ # (PID: 1 ...)" followed by exit 143.
365
+ #
366
+ # So: skip entirely in a container, accept only all-digit PIDs, and never
367
+ # signal 0 (our process group), 1 (init), or ourselves.
368
+ # The PIDs from `lsof -ti` output that are safe to signal.
369
+ #
370
+ # Pure so the safety rule can be tested directly. An unvalidated parse is a
371
+ # footgun with real teeth: where lsof prints a different shape than -ti
372
+ # implies, a non-numeric field becomes 0, and signalling PID 0 hits EVERY
373
+ # process in the caller's own process group -- the server kills itself.
374
+ #
375
+ # Accept only all-digit tokens; never PID 0 (our group), PID 1 (init),
376
+ # ourselves, or our own process group.
377
+ def selectable_pids(lsof_output, me, my_group = nil)
378
+ pids = []
379
+ lsof_output.split(/\s+/).each do |token|
380
+ next unless token.match?(/\A\d+\z/) # never coerce junk into a PID
381
+
382
+ pid = token.to_i
383
+ next if pid <= 1 || pid == me # 0 = our group, 1 = init, me = suicide
384
+ next if !my_group.nil? && pid == my_group
385
+
386
+ pids << pid unless pids.include?(pid)
387
+ end
388
+ pids
389
+ end
390
+
343
391
  def kill_process_on_port(port)
392
+ return false if in_container?
393
+
344
394
  result = `lsof -ti :#{port} 2>/dev/null`.strip
345
395
  return false if result.empty?
346
396
 
347
- pids = result.split("\n")
348
- pids.each do |pid|
349
- Process.kill("TERM", pid.to_i)
350
- rescue Errno::ESRCH, Errno::EPERM
351
- # Process already gone or no permission
397
+ me = Process.pid
398
+ my_group = begin
399
+ Process.getpgrp
400
+ rescue StandardError
401
+ nil
402
+ end
403
+
404
+ killed = []
405
+ selectable_pids(result, me, my_group).each do |pid|
406
+ begin
407
+ Process.kill("TERM", pid)
408
+ killed << pid.to_s
409
+ rescue Errno::ESRCH, Errno::EPERM
410
+ # Process already gone or no permission
411
+ end
352
412
  end
413
+
414
+ return false if killed.empty?
415
+
353
416
  sleep 0.5
354
- puts " Killed existing process on port #{port} (PID: #{pids.join(', ')})"
417
+ puts " Killed existing process on port #{port} (PID: #{killed.join(', ')})"
355
418
  true
356
419
  rescue Errno::ENOENT
357
420
  false
@@ -3136,7 +3199,7 @@ module Tina4
3136
3199
  ENV SWAGGER_DESCRIPTION="Auto-generated API documentation"
3137
3200
 
3138
3201
  # Start the server on all interfaces
3139
- CMD ["bundle", "exec", "tina4ruby", "start", "-p", "7147", "-h", "0.0.0.0"]
3202
+ CMD ["bundle", "exec", "tina4ruby", "start", "-p", "7147", "-h", "0.0.0.0", "--production"]
3140
3203
  DOCKERFILE
3141
3204
  end
3142
3205
 
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.13.83"
4
+ VERSION = "3.13.84"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.83
4
+ version: 3.13.84
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team