local-development-gateway 0.2.2 → 0.2.4
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/README.md +41 -1
- data/docker-compose.yml +3 -1
- data/docs/architecture/local-tcp-routing.md +2 -0
- data/lib/local_development_gateway/version.rb +1 -1
- data/lib/local_development_gateway.rb +63 -7
- 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: 769fae659862a5e04bbb1f4c1d572552afe3f777f4bbbd4ec007fa02860639c6
|
|
4
|
+
data.tar.gz: e704decf7f614cb01a2a74ae1601fa6e925da9c243b3b4015ea9760e8ec45ae0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 247d626136bee33123253a419d036812041b6e5df6094b14b262f5bc6be7ac629083aea1b717b64f8b29afce69b245b739fff47c0bcebc53c80b496351dbbb50
|
|
7
|
+
data.tar.gz: 6954a6378a4aecf2d6c3d6eef08d8be001abfd2fc5c1758314b37b777d795ea6e7e7f16ec647eb128b0930353128f8c6f7be25539b1444d629f2dddd35e2ec7a
|
data/README.md
CHANGED
|
@@ -106,6 +106,21 @@ command that must not start a missing gateway, pass
|
|
|
106
106
|
`ensure_running: false`; the block still runs and unused-gateway cleanup is
|
|
107
107
|
attempted.
|
|
108
108
|
|
|
109
|
+
### Installed versus active versions
|
|
110
|
+
|
|
111
|
+
`bundle exec local-development-gateway status` reports the invoking gem's installed version separately from each running shared service's active version, provenance, and mounted asset path. Healthy CLI `up`/`ensure` reuse also prints these diagnostics without recreating containers. Ruby callers can inspect `LocalDevelopmentGateway.status`; `ensure_running` still returns `:reused` for healthy services, and `ready?` remains a health check, not an artifact-version check.
|
|
112
|
+
|
|
113
|
+
For the database router, the active version comes from a record written by the Ruby process after loading the library at startup. For the HTTP gateway, it is the gateway configuration artifact version recorded in a container-creation label, not the Traefik image version. Missing records or labels are `unknown`. A legacy mount path containing a gem version is useful diagnostic evidence but does not prove which Ruby code or configuration the running process loaded. Neither version record is a content hash or proof that bind-mounted files have remained unchanged.
|
|
114
|
+
|
|
115
|
+
Installing a newer gem does not update already-running services. A known difference is reported as `mismatch`; legacy `unknown` provenance cannot establish that an upgrade is active. Coordinate restart timing with all consumers before explicitly updating only the database router:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
bundle exec local-development-gateway up --no-deps --force-recreate database-router
|
|
119
|
+
bundle exec local-development-gateway status
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Recreating the shared database router interrupts existing database client sessions across every consuming project/worktree. This command leaves the HTTP gateway and consumer database containers/volumes alone. If the HTTP gateway itself needs an update, separately coordinate and explicitly select `gateway` instead. Do not use `down` for a router-only update. After updating, confirm the selected service's active version is current and exercise an encrypted connection to the intended database; readiness alone is insufficient release verification.
|
|
123
|
+
|
|
109
124
|
## Worktree database services
|
|
110
125
|
|
|
111
126
|
The gateway includes protocol drivers for encrypted SQL Server and PostgreSQL
|
|
@@ -133,6 +148,21 @@ substitutes for private aliases. Database cloning is independent of this
|
|
|
133
148
|
hostname correction: it selects source/destination containers explicitly and
|
|
134
149
|
connects locally.
|
|
135
150
|
|
|
151
|
+
### Private database alias length
|
|
152
|
+
|
|
153
|
+
The complete `${COMPOSE_PROJECT_NAME}-<database-service>` alias is one DNS label. [DNS limits each label to 63 octets](https://www.rfc-editor.org/rfc/rfc1035.html#section-2.3.4); for these ASCII names, that is 63 characters. Budget the project name against the longest database service suffix in the stack: `63 - 1 - <service-name length>`.
|
|
154
|
+
|
|
155
|
+
| Database service | Appended suffix | Maximum project-name length |
|
|
156
|
+
| --- | --- | ---: |
|
|
157
|
+
| `db` | `-db` | 60 |
|
|
158
|
+
| `db_trav_com` | `-db_trav_com` | 51 |
|
|
159
|
+
|
|
160
|
+
A 60-character project name plus `-db` fits exactly; 61 characters produces a 64-character alias. With `-db_trav_com`, the corresponding boundary is 51/52 characters. These are DNS budgets, not a claim that Compose rejects oversized aliases: `docker compose config` can render an alias that exceeds the limit. Configuration rendering alone does not prove DNS resolution or database connectivity.
|
|
161
|
+
|
|
162
|
+
Use the effective project name selected by [Compose's native precedence](https://docs.docker.com/compose/how-tos/project-name/): `-p`, then `COMPOSE_PROJECT_NAME`, top-level `name:`, and the directory-derived default. Inspect the rendered `name`, database aliases, and application/job/test host settings through the consumer's documented wrapper and selected Compose files. Do not implement a separate project-name resolver, truncate/hash names automatically, or change existing short identities.
|
|
163
|
+
|
|
164
|
+
For a new disposable stack, explicitly choose a unique project name within the budget before creating resources. For an existing stack, changing its project name can select different project-scoped volumes and appear to lose its database. Preserve the current project identity and data; any necessary rename requires an explicit consumer-owned data migration, not an automatic gateway correction. Keep qualified aliases private and desktop gateway hostnames unchanged. The gem neither validates nor shortens consumer aliases; upgrading it does not repair an oversized name.
|
|
165
|
+
|
|
136
166
|
### Canonical development and test Compose pattern
|
|
137
167
|
|
|
138
168
|
Merge these entries into the project's existing development base and gateway
|
|
@@ -294,7 +324,8 @@ retaining cancellation-aware PostgreSQL routing despite Traefik's STARTTLS suppo
|
|
|
294
324
|
|
|
295
325
|
1. Give each concurrent checkout a distinct Compose project name and desktop
|
|
296
326
|
route hostname. Qualify every API/job/test database host with
|
|
297
|
-
`${COMPOSE_PROJECT_NAME}-<database-service>` using its existing driver setting
|
|
327
|
+
`${COMPOSE_PROJECT_NAME}-<database-service>` using its existing driver setting,
|
|
328
|
+
within the [suffix-specific DNS budget](#private-database-alias-length).
|
|
298
329
|
2. Advertise each qualified alias only on its private `default` network;
|
|
299
330
|
preserve it through mapping-form network merges in base-only and gateway modes.
|
|
300
331
|
3. Remove fixed database publications from development/test stacks; retain
|
|
@@ -419,6 +450,15 @@ It mounts the Docker socket so it can discover labelled containers. A read-only
|
|
|
419
450
|
| A project uses normal ports unexpectedly | Confirm the gateway container is running and has `local-gateway=true`. |
|
|
420
451
|
| Authentication rejects the callback | Confirm the generated hostname matches the allowed wildcard callback pattern. |
|
|
421
452
|
|
|
453
|
+
## Release 0.2.4
|
|
454
|
+
|
|
455
|
+
- Document suffix-specific DNS length budgets for private database aliases and data-safe project-name handling. No alias validation, shortening, routing, or lifecycle behavior changes.
|
|
456
|
+
|
|
457
|
+
## Release 0.2.3
|
|
458
|
+
|
|
459
|
+
- Status and healthy CLI reuse distinguish the installed gem version from each active shared service, reporting mismatches and unknown legacy provenance without restarting services.
|
|
460
|
+
- Scoped router update instructions explain shared database session interruption and keep restart timing under caller control. Health readiness and Ruby lifecycle return values remain unchanged.
|
|
461
|
+
|
|
422
462
|
## Release 0.2.2
|
|
423
463
|
|
|
424
464
|
- Canonical SQL Server/PostgreSQL Compose integration supports concurrent
|
data/docker-compose.yml
CHANGED
|
@@ -13,6 +13,7 @@ services:
|
|
|
13
13
|
start_period: 1s
|
|
14
14
|
labels:
|
|
15
15
|
local-gateway: "true"
|
|
16
|
+
local-gateway.version: "${LOCAL_DEVELOPMENT_GATEWAY_VERSION:-unknown}"
|
|
16
17
|
ports:
|
|
17
18
|
- "127.0.0.1:80:80"
|
|
18
19
|
restart: unless-stopped
|
|
@@ -29,7 +30,7 @@ services:
|
|
|
29
30
|
- -I/app/lib
|
|
30
31
|
- -rlocal_development_gateway
|
|
31
32
|
- -e
|
|
32
|
-
- LocalDevelopmentGateway::DatabaseRouter.run
|
|
33
|
+
- File.write("/tmp/local-development-gateway-version", LocalDevelopmentGateway::VERSION); LocalDevelopmentGateway::DatabaseRouter.run
|
|
33
34
|
healthcheck:
|
|
34
35
|
test:
|
|
35
36
|
- CMD
|
|
@@ -43,6 +44,7 @@ services:
|
|
|
43
44
|
start_period: 1s
|
|
44
45
|
labels:
|
|
45
46
|
local-gateway: "true"
|
|
47
|
+
local-gateway.version: "${LOCAL_DEVELOPMENT_GATEWAY_VERSION:-unknown}"
|
|
46
48
|
ports:
|
|
47
49
|
- "127.0.0.1:1433:1433"
|
|
48
50
|
- "127.0.0.1:5432:5432"
|
|
@@ -44,6 +44,8 @@ preserves `default.aliases`. Configure all application/job/test contexts with
|
|
|
44
44
|
their appropriate aliases, existing credentials, and intended database names;
|
|
45
45
|
test contexts still select their own test database.
|
|
46
46
|
|
|
47
|
+
The README's [private-alias length contract](../../README.md#private-database-alias-length) defines the DNS budget for each service suffix. Native Compose project selection and existing data identities remain authoritative; the gateway does not shorten or rename consumer projects.
|
|
48
|
+
|
|
47
49
|
Gateway-mode development and test stacks publish no database host ports.
|
|
48
50
|
Container consumers work with the base alone. Desktop access without the
|
|
49
51
|
gateway is an explicit project-local port-publishing overlay, not an inherited
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require "json"
|
|
4
5
|
require "local_development_gateway/version"
|
|
5
6
|
|
|
6
7
|
module LocalDevelopmentGateway
|
|
@@ -29,16 +30,16 @@ module LocalDevelopmentGateway
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
class Docker
|
|
32
|
-
def call(*args, capture: true)
|
|
33
|
+
def call(*args, capture: true, env: {})
|
|
33
34
|
command = ["docker", *args]
|
|
34
35
|
if capture
|
|
35
|
-
stdout, stderr, status = Open3.capture3(*command)
|
|
36
|
+
stdout, stderr, status = Open3.capture3(env, *command)
|
|
36
37
|
unless status.success?
|
|
37
38
|
raise DockerError.new(command, stderr.empty? ? stdout : stderr)
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
stdout
|
|
41
|
-
elsif system(*command)
|
|
42
|
+
elsif system(env, *command)
|
|
42
43
|
true
|
|
43
44
|
else
|
|
44
45
|
raise DockerError.new(command, "")
|
|
@@ -85,7 +86,17 @@ module LocalDevelopmentGateway
|
|
|
85
86
|
end
|
|
86
87
|
|
|
87
88
|
def status
|
|
88
|
-
compose("ps", "--all")
|
|
89
|
+
output = compose("ps", "--all")
|
|
90
|
+
output << "\nInstalled gateway version: #{VERSION}\n"
|
|
91
|
+
SERVICE_NAMES.each do |service|
|
|
92
|
+
ids = gateway_container_ids(service: service, status: "running")
|
|
93
|
+
if ids.empty?
|
|
94
|
+
output << "#{service}: not running; active=unknown\n"
|
|
95
|
+
else
|
|
96
|
+
ids.each { |id| output << service_version_status(service, id) }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
output
|
|
89
100
|
end
|
|
90
101
|
|
|
91
102
|
def logs(*args, follow: false)
|
|
@@ -122,12 +133,56 @@ module LocalDevelopmentGateway
|
|
|
122
133
|
"--file",
|
|
123
134
|
COMPOSE_FILE,
|
|
124
135
|
*args,
|
|
125
|
-
capture: capture
|
|
136
|
+
capture: capture,
|
|
137
|
+
env: {
|
|
138
|
+
"LOCAL_DEVELOPMENT_GATEWAY_VERSION" => VERSION
|
|
139
|
+
}
|
|
126
140
|
)
|
|
127
141
|
end
|
|
128
142
|
|
|
129
143
|
private
|
|
130
144
|
|
|
145
|
+
def service_version_status(service, id)
|
|
146
|
+
container = JSON.parse(@runner.call("inspect", id)).fetch(0)
|
|
147
|
+
if service == "database-router"
|
|
148
|
+
version = router_version(id)
|
|
149
|
+
provenance = "process-start record"
|
|
150
|
+
asset_target = "/app/lib"
|
|
151
|
+
else
|
|
152
|
+
version = container.dig("Config", "Labels", "local-gateway.version")
|
|
153
|
+
provenance = "container-creation label"
|
|
154
|
+
asset_target = "/etc/traefik/traefik.yml"
|
|
155
|
+
end
|
|
156
|
+
version = "unknown" if version.nil? || version.empty?
|
|
157
|
+
comparison = version_comparison(version)
|
|
158
|
+
mount =
|
|
159
|
+
container
|
|
160
|
+
.fetch("Mounts", [])
|
|
161
|
+
.find { |entry| entry["Destination"] == asset_target }
|
|
162
|
+
source = "unknown"
|
|
163
|
+
source = mount.fetch("Source") if mount
|
|
164
|
+
"#{service} (#{id[0, 12]}): active=#{version}; #{comparison}; " \
|
|
165
|
+
"provenance=#{provenance}; mounted assets=#{source}\n"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def router_version(id)
|
|
169
|
+
@runner.call(
|
|
170
|
+
"exec",
|
|
171
|
+
id,
|
|
172
|
+
"cat",
|
|
173
|
+
"/tmp/local-development-gateway-version"
|
|
174
|
+
).strip
|
|
175
|
+
rescue DockerError
|
|
176
|
+
nil
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def version_comparison(version)
|
|
180
|
+
return "unknown" if version == "unknown"
|
|
181
|
+
return "current" if version == VERSION
|
|
182
|
+
|
|
183
|
+
"mismatch (installed=#{VERSION}); explicit service recreation required"
|
|
184
|
+
end
|
|
185
|
+
|
|
131
186
|
def wait_until_ready
|
|
132
187
|
deadline = @clock.call + @timeout
|
|
133
188
|
until ready?
|
|
@@ -233,7 +288,7 @@ module LocalDevelopmentGateway
|
|
|
233
288
|
|
|
234
289
|
Commands:
|
|
235
290
|
up, ensure Start or reuse the shared gateway
|
|
236
|
-
status Show
|
|
291
|
+
status Show Compose status and installed/active gateway versions
|
|
237
292
|
ready Check gateway readiness
|
|
238
293
|
logs Follow gateway logs
|
|
239
294
|
down, stop Stop the gateway only when unused
|
|
@@ -260,7 +315,8 @@ module LocalDevelopmentGateway
|
|
|
260
315
|
|
|
261
316
|
case command
|
|
262
317
|
when "up", "ensure"
|
|
263
|
-
@client.ensure_running(*@argv)
|
|
318
|
+
result = @client.ensure_running(*@argv)
|
|
319
|
+
@output.write(@client.status) if result == :reused
|
|
264
320
|
when "status"
|
|
265
321
|
@output.write(@client.status)
|
|
266
322
|
when "ready"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: local-development-gateway
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marlen Brunner
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Ruby API and CLI for starting, monitoring, and conditionally stopping
|
|
14
14
|
the shared Docker gateway.
|