captive-stack-detector 0.6.1 → 0.8.1
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/docs/stacks.md +18 -0
- data/lib/captive_stack_detector/gem_package_map.rb +11 -4
- data/lib/captive_stack_detector/js_stack_detector.rb +1 -0
- data/lib/captive_stack_detector/package_json_analyzer.rb +7 -0
- data/lib/captive_stack_detector/rails_stack_detector.rb +4 -2
- data/lib/captive_stack_detector/system_package_detector.rb +10 -5
- data/lib/captive_stack_detector/types.rb +6 -1
- data/lib/captive_stack_detector/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72edac67211d3cbb3b67441fa5b0bb67e160bef105a770754638576bd7be25f1
|
|
4
|
+
data.tar.gz: c5664e645e6897ec75bdc1cfae9bf97ba9ccaa019a26ff8ada90f85ff91dcb22
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ca13e0e77a74ca4f6e3624b755d73f0f8d3c1b15e29ef7706f6251fc9ffcbcab4e14b4372c8768b8baddb7285b91f096b48393ce86845d93bbc3db63e66847d
|
|
7
|
+
data.tar.gz: 57b1ded773d8cd32707b8e7c3388a9bff4316f9af64ebf8fa45bd619c2dff9261b4659fb4ed34d6b06a8bca7514abef550c1971b02e86e75144d224101f1ebef
|
data/docs/stacks.md
CHANGED
|
@@ -15,6 +15,7 @@ gem "rails", "8.1.0"
|
|
|
15
15
|
- Avec une gem d'assets (`propshaft`, `sprockets-rails`, `importmap-rails`, `jsbundling-rails`, `cssbundling-rails`, `tailwindcss-rails`…), l'application est construite avec ses assets frontend.
|
|
16
16
|
- Sans gem d'assets, elle est construite comme une API.
|
|
17
17
|
- `gem "pg"` provisionne une base Postgres, `gem "redis"` ou `gem "sidekiq"` un Redis.
|
|
18
|
+
- Les bibliothèques système exigées par certaines gems sont installées dans l'image : `ruby-vips` (libvips), `mini_magick` et `rmagick` (imagemagick), `sqlite3`, et `ethon` (libcurl), y compris quand la gem n'arrive qu'en dépendance d'une autre via le `Gemfile.lock`. Pour une bibliothèque non détectée, listez le paquet Debian dans un `Aptfile` à la racine.
|
|
18
19
|
|
|
19
20
|
Si un `Gemfile` est présent, c'est toujours lui qui décide : un dépôt avec un `Gemfile` sans Rails est refusé, même s'il a aussi un `package.json`.
|
|
20
21
|
|
|
@@ -65,6 +66,23 @@ Si votre `package.json` dépend de `next`, `nuxt`, `@sveltejs/kit` ou d'un paque
|
|
|
65
66
|
- `@supabase/supabase-js` ou `@supabase/ssr` provisionne un backend Supabase.
|
|
66
67
|
- Une ligne `worker: <commande>` dans un `Procfile` lance un worker.
|
|
67
68
|
|
|
69
|
+
|
|
70
|
+
### Variables exposées au navigateur
|
|
71
|
+
|
|
72
|
+
Une variable n'arrive dans le code du navigateur que si elle porte le préfixe de votre bundler. Captive le déduit de vos dépendances :
|
|
73
|
+
|
|
74
|
+
| Dépendance | Préfixe |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `expo` | `EXPO_PUBLIC_` |
|
|
77
|
+
| `next` | `NEXT_PUBLIC_` |
|
|
78
|
+
| `vite` | `VITE_` |
|
|
79
|
+
|
|
80
|
+
Si plusieurs sont présentes, c'est le premier de la liste qui l'emporte (`vite` est souvent là pour les tests seulement).
|
|
81
|
+
|
|
82
|
+
Avec un backend Supabase, Captive pose l'URL et la clé publique sous ce préfixe, avec les deux noms de clé en usage : `VITE_SUPABASE_URL`, `VITE_SUPABASE_ANON_KEY` et `VITE_SUPABASE_PUBLISHABLE_KEY` pour une application Vite, par exemple. Côté serveur, les mêmes valeurs sont disponibles sans préfixe : `SUPABASE_URL` et `SUPABASE_ANON_KEY`.
|
|
83
|
+
|
|
84
|
+
Ces variables sont lues **au build** : après en avoir changé une, redéployez.
|
|
85
|
+
|
|
68
86
|
## Expo
|
|
69
87
|
|
|
70
88
|
Condition : un `package.json` à la racine avec la dépendance `expo` (dans `dependencies` ou `devDependencies`). Aucun script n'est exigé.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module CaptiveStackDetector
|
|
4
4
|
# Connaissance « quel gem exige quels paquets système ». Rend les paires gem → paquets
|
|
5
|
-
# pour les gems
|
|
5
|
+
# pour les gems déclarés dans le Gemfile ou résolus dans le Gemfile.lock (dépendances transitives).
|
|
6
6
|
module GemPackageMap
|
|
7
7
|
MAP = {
|
|
8
8
|
"ruby-vips" => %w[libvips42 libvips-dev],
|
|
@@ -10,11 +10,18 @@ module CaptiveStackDetector
|
|
|
10
10
|
"rmagick" => %w[imagemagick],
|
|
11
11
|
"wkhtmltopdf-binary" => %w[], # retiré de Debian Trixie, projet upstream abandonné
|
|
12
12
|
"sqlite3" => %w[libsqlite3-dev],
|
|
13
|
+
"ethon" => %w[libcurl4t64], # charge libcurl via FFI (dépendance de typhoeus)
|
|
13
14
|
}.freeze
|
|
14
15
|
|
|
15
|
-
def self.matches(
|
|
16
|
-
content =
|
|
17
|
-
MAP.select { |gem, _| content.match?(
|
|
16
|
+
def self.matches(*sources)
|
|
17
|
+
content = sources.join("\n")
|
|
18
|
+
MAP.select { |gem, _| content.match?(pattern(gem)) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Déclaration `gem "x"` du Gemfile, ou spec résolue ` x (1.2.3)` du Gemfile.lock.
|
|
22
|
+
def self.pattern(gem)
|
|
23
|
+
name = Regexp.escape(gem)
|
|
24
|
+
/gem ['"]#{name}['"]|^ {4}#{name} \(/
|
|
18
25
|
end
|
|
19
26
|
end
|
|
20
27
|
end
|
|
@@ -5,6 +5,7 @@ require_relative "ssr_framework_detector"
|
|
|
5
5
|
|
|
6
6
|
module CaptiveStackDetector
|
|
7
7
|
class PackageJsonAnalyzer
|
|
8
|
+
PUBLIC_ENV_PREFIXES = { "expo" => "EXPO_PUBLIC_", "next" => "NEXT_PUBLIC_", "vite" => "VITE_" }.freeze
|
|
8
9
|
# Des clients, pas des ORM : Prisma ou Drizzle parlent aussi MySQL et SQLite, leur
|
|
9
10
|
# présence ne dit pas que l'app attend une base Postgres.
|
|
10
11
|
POSTGRES_CLIENTS = %w[pg postgres].freeze
|
|
@@ -41,6 +42,12 @@ module CaptiveStackDetector
|
|
|
41
42
|
any_dep?(SUPABASE_CLIENTS) ? "supabase" : nil
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
# Le premier bundler trouvé, dans l'ordre du type : vite en dernier, parce qu'il arrive
|
|
46
|
+
# souvent avec Vitest sans construire le front.
|
|
47
|
+
def public_env_prefix
|
|
48
|
+
PUBLIC_ENV_PREFIXES.find { |dependance, _| deps.key?(dependance) }&.last
|
|
49
|
+
end
|
|
50
|
+
|
|
44
51
|
private
|
|
45
52
|
|
|
46
53
|
def any_dep?(names)
|
|
@@ -5,6 +5,7 @@ require_relative "system_package_detector"
|
|
|
5
5
|
require_relative "solid_queue_worker"
|
|
6
6
|
|
|
7
7
|
module CaptiveStackDetector
|
|
8
|
+
# Construit le Result d'une app Rails à partir des fichiers du repo.
|
|
8
9
|
class RailsStackDetector
|
|
9
10
|
def initialize(reader, analyzer)
|
|
10
11
|
@reader = reader
|
|
@@ -34,8 +35,9 @@ module CaptiveStackDetector
|
|
|
34
35
|
|
|
35
36
|
def system_packages
|
|
36
37
|
SystemPackageDetector.new(
|
|
37
|
-
gemfile:
|
|
38
|
-
aptfile:
|
|
38
|
+
gemfile: @reader.read("Gemfile"),
|
|
39
|
+
aptfile: @reader.read("Aptfile"),
|
|
40
|
+
lockfile: @reader.read("Gemfile.lock"),
|
|
39
41
|
).packages
|
|
40
42
|
end
|
|
41
43
|
end
|
|
@@ -4,27 +4,32 @@ require_relative "aptfile_parser"
|
|
|
4
4
|
require_relative "gem_package_map"
|
|
5
5
|
|
|
6
6
|
module CaptiveStackDetector
|
|
7
|
+
# Paquets APT à installer dans l'image : ceux qu'exigent les gems du Gemfile/Gemfile.lock, plus l'Aptfile.
|
|
7
8
|
class SystemPackageDetector
|
|
8
|
-
def initialize(gemfile:, aptfile:)
|
|
9
|
-
@
|
|
9
|
+
def initialize(gemfile:, aptfile:, lockfile: nil)
|
|
10
|
+
@gem_sources = [ gemfile, lockfile ]
|
|
10
11
|
@aptfile = aptfile.to_s
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def packages
|
|
14
15
|
result = (from_gemfile + from_aptfile).uniq
|
|
15
|
-
$stderr.puts "[captive-stack-detector] paquets système détectés : #{result
|
|
16
|
+
$stderr.puts "[captive-stack-detector] paquets système détectés : #{describe(result, "aucun")}"
|
|
16
17
|
result
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
private
|
|
20
21
|
|
|
21
22
|
def from_gemfile
|
|
22
|
-
GemPackageMap.matches(
|
|
23
|
-
$stderr.puts "[captive-stack-detector] gem '#{gem}' détecté → #{pkgs
|
|
23
|
+
GemPackageMap.matches(*@gem_sources).each_with_object([]) do |(gem, pkgs), result|
|
|
24
|
+
$stderr.puts "[captive-stack-detector] gem '#{gem}' détecté → #{describe(pkgs, "aucun paquet")}"
|
|
24
25
|
result.concat(pkgs)
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
def describe(pkgs, none)
|
|
30
|
+
pkgs.any? ? pkgs.join(", ") : none
|
|
31
|
+
end
|
|
32
|
+
|
|
28
33
|
def from_aptfile
|
|
29
34
|
AptfileParser.parse(@aptfile).each do |pkg|
|
|
30
35
|
$stderr.puts "[captive-stack-detector] Aptfile → paquet '#{pkg}'"
|
|
@@ -4,7 +4,12 @@ module CaptiveStackDetector
|
|
|
4
4
|
Services = Data.define(:database, :queue, :backend)
|
|
5
5
|
Worker = Data.define(:command)
|
|
6
6
|
Runtime = Data.define(:ruby, :node)
|
|
7
|
-
|
|
7
|
+
# public_env_prefix : préfixe des variables que le bundler expose au navigateur
|
|
8
|
+
# (VITE_, NEXT_PUBLIC_, EXPO_PUBLIC_), nil sans bundler front (PackageJsonAnalyzer).
|
|
9
|
+
Result = Data.define(:type, :subtype, :services, :worker, :runtime, :env_vars, :system_packages,
|
|
10
|
+
:public_env_prefix) do
|
|
11
|
+
def initialize(public_env_prefix: nil, **) = super
|
|
12
|
+
end
|
|
8
13
|
|
|
9
14
|
# reason : :no_manifest, :no_rails ou :no_node_entry, pour que l'appelant explique le refus.
|
|
10
15
|
class UnsupportedStack < StandardError
|