tina4ruby 3.13.144 → 3.13.146
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 +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/tina4/api.rb +26 -4
- data/lib/tina4/cli.rb +16 -15
- data/lib/tina4/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aaafd9078b79dd56e7829b0a92ee162c3eaf471c7102746ed1b119f523f42b8e
|
|
4
|
+
data.tar.gz: eaa7f47dd692a137d5fce2395696a3f84addfb1feaf8a74b96f936f4177ffcf4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b50b1109f4b5caf4001fde5141a248c5eaf7d6b9fb691eebe267235f2b7ad2f513b771eefa7233b4b6ef849908aad2e5671b04adee72cdbb674702ba9b2f1a1
|
|
7
|
+
data.tar.gz: dbc2ffdc2dd55b7ca03b8de790fc24793edf4ff58de61eeec66196444c8bd8788596cc9c518cecf83c1e8021e8710592f3a947e1039a15da2c5f8305c3e955ea
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ number means the same thing everywhere.
|
|
|
6
6
|
**The authoritative release notes for every shipped version live in the documentation:**
|
|
7
7
|
https://tina4.com/ruby/36-releases
|
|
8
8
|
|
|
9
|
+
## 3.13.146 — 2026-10-04
|
|
10
|
+
|
|
11
|
+
### AI skills
|
|
12
|
+
- Every skill now opens with a content list (table of contents) and a degrees-of-freedom legend (what is inviolable, what is a default, what is judgement).
|
|
13
|
+
- New `tina4-cli` skill documents every `tina4` command and flag and the scaffolding discipline.
|
|
14
|
+
- `tina4-maintainer` gains task checklists (PR review, release, installer signing, parity sweep); `tina4-design` split into focused reference files.
|
|
15
|
+
- Skills installer now supports one-level-nested references; `install-skills.ps1` re-signed.
|
|
16
|
+
|
|
17
|
+
### Framework
|
|
18
|
+
- No framework code changes - the version bump carries the skills release across all four frameworks at parity.
|
|
19
|
+
|
|
20
|
+
## 3.13.145 — 2026-10-03
|
|
21
|
+
|
|
22
|
+
Two fixes land together. The Api client no longer carries an application-configured header onto a different origin: credentials travel under many names (`X-Api-Key`, a custom bearer header), so on a cross-origin redirect only content-negotiation and transport headers cross, and every configured or per-call header is bound to the origin it was meant for; a same-origin redirect keeps them. And `tina4ruby migrate` now exits 1 when there is no usable database (unreachable or misconfigured), so CI no longer reads a failed migration as success. Both are proven against real dependencies, no mocks. No runtime gems.
|
|
23
|
+
|
|
9
24
|
## 3.13.144 — 2026-10-02
|
|
10
25
|
|
|
11
26
|
HEAD responses are locked to exactly one `Content-Length` on the wire, equal to the length the GET would have sent. A HEAD emitting two differing `Content-Length` headers is malformed (RFC 7230 s3.3.2); a strict proxy (nginx) 502s it while lenient clients show 200. Ruby already writes a single, correct `Content-Length` with an empty body; this release LOCKS it with a conformance test that boots the real built-in server and reads the raw socket, because the in-process test reads a response object and cannot see the wire - the layer where tina4-php shipped `Content-Length: 0` on every routed HEAD (fixed in the same release). Also internal: the serve-debug readiness harness is unified on the `Server:`-banner identity guard across all four frameworks, and a carbon-benchmark drift (a `to_paginate` call with arguments, which ADR-0043 forbids) is fixed so `carbon_benchmarks.rb --carbon` runs clean. No runtime behaviour change in ruby. The framework still has no required runtime dependencies.
|
data/lib/tina4/api.rb
CHANGED
|
@@ -41,6 +41,14 @@ module Tina4
|
|
|
41
41
|
# master's _STRIP_ON_CROSS_ORIGIN.
|
|
42
42
|
API_STRIP_ON_CROSS_ORIGIN = %w[authorization cookie].freeze
|
|
43
43
|
|
|
44
|
+
# The only headers carried onto a different origin. A caller-set header can
|
|
45
|
+
# hold a credential under any name (X-Api-Key, X-Auth-Token, ...), so a
|
|
46
|
+
# denylist cannot contain it: the headers configured on the client (and any
|
|
47
|
+
# per-call header, on a redirect) are bound to the origin they were meant for,
|
|
48
|
+
# and only these content-negotiation headers cross. Compared case-insensitively.
|
|
49
|
+
API_KEEP_ON_CROSS_ORIGIN = %w[user-agent accept accept-encoding accept-language
|
|
50
|
+
content-type content-length].freeze
|
|
51
|
+
|
|
44
52
|
# A curated extension -> MIME map for guessing a multipart part's Content-Type
|
|
45
53
|
# from its filename (Ruby core ships no mimetypes db, and pulling one in would
|
|
46
54
|
# break the zero-dependency promise). tina4: a small built-in map covering the
|
|
@@ -265,7 +273,7 @@ module Tina4
|
|
|
265
273
|
return error_response("download requires dest_path", path: nil) unless dest_path
|
|
266
274
|
|
|
267
275
|
uri = build_uri(path, params)
|
|
268
|
-
headers =
|
|
276
|
+
headers = configured_headers(uri).dup
|
|
269
277
|
cookie = cookie_header
|
|
270
278
|
headers["Cookie"] = cookie if @cookies_enabled && cookie
|
|
271
279
|
headers = origin_headers(uri, headers)
|
|
@@ -542,11 +550,21 @@ module Tina4
|
|
|
542
550
|
end
|
|
543
551
|
|
|
544
552
|
def apply_headers(request, extra_headers)
|
|
545
|
-
|
|
553
|
+
configured_headers(request.uri).merge(extra_headers).each do |key, value|
|
|
546
554
|
request[key] = value
|
|
547
555
|
end
|
|
548
556
|
end
|
|
549
557
|
|
|
558
|
+
# Headers configured on the client belong to its base origin, like the
|
|
559
|
+
# token: a target on another origin gets only the cross-origin-safe ones. A
|
|
560
|
+
# client with no base has no origin to bind them to, and sends them to the
|
|
561
|
+
# URL each call names.
|
|
562
|
+
def configured_headers(uri)
|
|
563
|
+
return @headers if @base_url.empty? || uri.nil? || same_origin?(uri, URI.parse(@base_url))
|
|
564
|
+
|
|
565
|
+
keep_cross_origin(@headers)
|
|
566
|
+
end
|
|
567
|
+
|
|
550
568
|
# Execute the request with opt-in retry/backoff. Returns an APIResponse.
|
|
551
569
|
#
|
|
552
570
|
# With @max_retries > 0, a transport error (APIResponse#status == 0, the
|
|
@@ -591,7 +609,7 @@ module Tina4
|
|
|
591
609
|
end
|
|
592
610
|
|
|
593
611
|
# Perform a real HTTP round-trip via Net::HTTP, following redirects in a
|
|
594
|
-
# bounded loop and
|
|
612
|
+
# bounded loop and carrying only API_KEEP_ON_CROSS_ORIGIN headers onto a cross-origin hop.
|
|
595
613
|
# When stream_to is given, a 2xx body is streamed to that path in chunks
|
|
596
614
|
# (never buffered whole) and no file is written on a non-2xx status.
|
|
597
615
|
#
|
|
@@ -668,7 +686,7 @@ module Tina4
|
|
|
668
686
|
|
|
669
687
|
if location
|
|
670
688
|
new_uri = URI.join(current_uri.to_s, location)
|
|
671
|
-
current_headers =
|
|
689
|
+
current_headers = keep_cross_origin(current_headers) unless same_origin?(current_uri, new_uri)
|
|
672
690
|
# 301/302/303 downgrade a non-GET/HEAD to GET and drop the body
|
|
673
691
|
# (matches urllib); 307/308 preserve method + body.
|
|
674
692
|
if [301, 302, 303].include?(redirect_code) && !%w[GET HEAD].include?(current_method)
|
|
@@ -787,6 +805,10 @@ module Tina4
|
|
|
787
805
|
headers.reject { |key, _| API_STRIP_ON_CROSS_ORIGIN.include?(key.to_s.downcase) }
|
|
788
806
|
end
|
|
789
807
|
|
|
808
|
+
def keep_cross_origin(headers)
|
|
809
|
+
headers.select { |key, _| API_KEEP_ON_CROSS_ORIGIN.include?(key.to_s.downcase) }
|
|
810
|
+
end
|
|
811
|
+
|
|
790
812
|
# Fetch a value from a Hash trying each key as both a symbol and a string.
|
|
791
813
|
def result_value(result, *keys)
|
|
792
814
|
keys.each do |key|
|
data/lib/tina4/cli.rb
CHANGED
|
@@ -757,6 +757,19 @@ module Tina4
|
|
|
757
757
|
|
|
758
758
|
# ── migrate ───────────────────────────────────────────────────────────
|
|
759
759
|
|
|
760
|
+
# The migrate commands cannot do their job without a database, so they
|
|
761
|
+
# fail. Tina4.initialize! logs a connection error and leaves
|
|
762
|
+
# Tina4.database nil, and returning here used to exit 0 -- a deploy whose
|
|
763
|
+
# database URL was wrong, or missing, went on as though it had migrated.
|
|
764
|
+
def exit_without_database
|
|
765
|
+
if ENV["TINA4_DATABASE_URL"].to_s.strip.empty?
|
|
766
|
+
warn "No database configured. Set TINA4_DATABASE_URL in your .env file."
|
|
767
|
+
else
|
|
768
|
+
warn "Database unavailable: TINA4_DATABASE_URL is set but could not be opened (see the error above)."
|
|
769
|
+
end
|
|
770
|
+
exit 1
|
|
771
|
+
end
|
|
772
|
+
|
|
760
773
|
def cmd_migrate(argv)
|
|
761
774
|
options = {}
|
|
762
775
|
parser = OptionParser.new do |opts|
|
|
@@ -769,11 +782,7 @@ module Tina4
|
|
|
769
782
|
require_relative "../tina4"
|
|
770
783
|
Tina4.initialize!(Dir.pwd)
|
|
771
784
|
|
|
772
|
-
db = Tina4.database
|
|
773
|
-
unless db
|
|
774
|
-
puts "No database configured. Set TINA4_DATABASE_URL in your .env file."
|
|
775
|
-
return
|
|
776
|
-
end
|
|
785
|
+
db = Tina4.database || exit_without_database
|
|
777
786
|
|
|
778
787
|
migration = Tina4::Migration.new(db)
|
|
779
788
|
|
|
@@ -856,11 +865,7 @@ module Tina4
|
|
|
856
865
|
require_relative "../tina4"
|
|
857
866
|
Tina4.initialize!(Dir.pwd)
|
|
858
867
|
|
|
859
|
-
db = Tina4.database
|
|
860
|
-
unless db
|
|
861
|
-
puts "No database configured. Set TINA4_DATABASE_URL in your .env file."
|
|
862
|
-
return
|
|
863
|
-
end
|
|
868
|
+
db = Tina4.database || exit_without_database
|
|
864
869
|
|
|
865
870
|
migration = Tina4::Migration.new(db)
|
|
866
871
|
info = migration.status
|
|
@@ -899,11 +904,7 @@ module Tina4
|
|
|
899
904
|
require_relative "../tina4"
|
|
900
905
|
Tina4.initialize!(Dir.pwd)
|
|
901
906
|
|
|
902
|
-
db = Tina4.database
|
|
903
|
-
unless db
|
|
904
|
-
puts "No database configured. Set TINA4_DATABASE_URL in your .env file."
|
|
905
|
-
return
|
|
906
|
-
end
|
|
907
|
+
db = Tina4.database || exit_without_database
|
|
907
908
|
|
|
908
909
|
migration = Tina4::Migration.new(db)
|
|
909
910
|
results = migration.rollback(options[:steps])
|
data/lib/tina4/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tina4ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.13.
|
|
4
|
+
version: 3.13.146
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tina4 Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sqlite3
|