captive-stack-detector 0.10.0 → 0.12.5

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: 5275b5b8c6fc70aec72f1ab4de5c4301c93e8e269908d93fc8db6d7735f99540
4
- data.tar.gz: e8ba22932d1a222f38f5b6d569c32d772af28930ccfc8c9967c916db9ac14152
3
+ metadata.gz: e5c9cf6e0559903b9b472715ed1d425903be96cdf7ca09c4689a66f63df7b19f
4
+ data.tar.gz: 7146a542cf6d2887063172c47a0d8f85251d3e7e02062969c8ee04882c321398
5
5
  SHA512:
6
- metadata.gz: 18fd7d008bb7eba3e0f9b6a1efc3baead566d0cb3b676b49126776ec6112692a43334bdc199bb59c883d28aac67abf5e54515077bc79f4a9c265b7d36b57989f
7
- data.tar.gz: be421c440ddee1fecdeeacec913e83cbdf2cdd23094ce1fbae24f9450bf02507a4d701746c389b912ed31b95ba7fc4483170e9eb4c586a0ce1db8b6167385500
6
+ metadata.gz: d2162dfa8b2aa58308fb3523677fba79ffba3cd7abb7fa980bd2466408bc8347519845ec93bb83b8d751b308e504488c4f2edc56a2147132634ef288de5e78b1
7
+ data.tar.gz: 65b1daea44aa2172b74e215d3d6fd55b87e94ae6dc5115657605dee8ee880dbc5e4f0b2f04db139348cab9c34883a33d07a131e32fe5377db6256368531de625
data/docs/stacks.md CHANGED
@@ -112,7 +112,7 @@ Ces variables sont lues **au build** : après en avoir changé une, redéployez.
112
112
 
113
113
  ### Site statique sans build
114
114
 
115
- Condition : ni `Gemfile` ni `package.json` à la racine, mais un `public/index.html` ou un `index.html` à la racine. Rien n'est installé ni buildé : vos fichiers sont servis tels quels.
115
+ Condition : pas de `Gemfile` à la racine, mais un `public/index.html` ou un `index.html` à la racine. Un `package.json` est accepté s'il ne sert qu'à l'outillage (Supabase CLI, prettier…) : sans dépendance `expo` ni script `start` ou `build`. Rien n'est installé ni buildé : vos fichiers sont servis tels quels.
116
116
 
117
117
  - Si `public/` existe, seul ce dossier est servi.
118
118
  - Sinon la racine est servie, limitée aux fichiers web (HTML, JavaScript, CSS, JSON, images, polices, manifeste, texte). Les dossiers cachés (`.git/`, `.github/`), `supabase/`, vos scripts SQL ou votre documentation ne sont jamais publiés.
@@ -150,6 +150,6 @@ Condition : un `package.json` à la racine avec la dépendance `expo` (dans `dep
150
150
  | --- | --- | --- |
151
151
  | Aucun Gemfile ni package.json à la racine du dépôt. | Pas de fichier de dépendances ni d'`index.html` à la racine. | Vérifiez que l'application est bien à la racine du dépôt, pas dans un sous-dossier (`app/`, `frontend/`…). Les monorepos ne sont pas pris en charge. |
152
152
  | Gemfile trouvé, mais sans la gem rails. | Application Ruby non Rails (Sinatra, Hanami…), ou `Gemfile` d'outillage dans un projet JavaScript. | Seul Rails est pris en charge côté Ruby. Si le `Gemfile` ne sert qu'à de l'outillage, retirez-le du dépôt. |
153
- | package.json trouvé, mais sans dépendance expo ni script start ou build. | Captive ne sait pas comment construire l'application. | Ajoutez un script `build` (application web) ou `start` (serveur), comme dans les exemples ci-dessus. |
153
+ | package.json trouvé, mais sans dépendance expo ni script start ou build. | Captive ne sait pas comment construire l'application, et aucun `index.html` ne permet de la servir telle quelle. | Ajoutez un script `build` (application web) ou `start` (serveur), comme dans les exemples ci-dessus, ou un `index.html` pour un site statique. |
154
154
 
155
155
  Si votre stack n'est pas listée ici, contactez l'équipe Captive.
@@ -5,9 +5,14 @@ require "uri"
5
5
  require "base64"
6
6
  require "json"
7
7
  require_relative "github_tree"
8
+ require_relative "types"
9
+ require_relative "github_response"
8
10
 
9
11
  module CaptiveStackDetector
10
12
  class GithubApiClient
13
+ # Un corps illisible (JSON invalide ou sans le champ attendu) vaut une absence.
14
+ UNREADABLE = [ JSON::ParserError, NoMethodError, TypeError ].freeze
15
+
11
16
  def initialize(token, repo)
12
17
  @token = token
13
18
  @repo = repo
@@ -18,7 +23,7 @@ module CaptiveStackDetector
18
23
  return nil unless body
19
24
 
20
25
  Base64.decode64(JSON.parse(body)["content"]).force_encoding("utf-8")
21
- rescue StandardError
26
+ rescue *UNREADABLE
22
27
  nil
23
28
  end
24
29
 
@@ -27,7 +32,7 @@ module CaptiveStackDetector
27
32
  return [] unless body
28
33
 
29
34
  GithubTree.new(body).files_in(dir)
30
- rescue StandardError
35
+ rescue *UNREADABLE
31
36
  []
32
37
  end
33
38
 
@@ -35,9 +40,10 @@ module CaptiveStackDetector
35
40
 
36
41
  def get(path)
37
42
  uri = URI("https://api.github.com/repos/#{@repo}/#{path}")
38
- req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer #{@token}", "Accept" => "application/vnd.github+json")
39
- res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
40
- res.body if res.is_a?(Net::HTTPSuccess)
43
+ GithubResponse.body(Net::HTTP.get_response(uri, "Authorization" => "Bearer #{@token}",
44
+ "Accept" => "application/vnd.github+json"))
45
+ rescue SystemCallError, IOError, SocketError, Timeout::Error, OpenSSL::SSL::SSLError => e
46
+ raise SourceUnavailable.new(:network, detail: e.message)
41
47
  end
42
48
  end
43
49
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require_relative "types"
5
+
6
+ module CaptiveStackDetector
7
+ # Ce que veut dire une réponse de l'API GitHub : un corps, une absence, ou un refus nommé.
8
+ # Change avec la façon dont GitHub signale ses erreurs, pas avec ce que l'on y lit.
9
+ module GithubResponse
10
+ # 404 : fichier absent ; 409 : dépôt vide. Tout autre échec vient de GitHub, pas du dépôt.
11
+ ABSENT = [ "404", "409" ].freeze
12
+ REFUSALS = { "401" => :unauthorized, "429" => :rate_limited }.freeze
13
+
14
+ def self.body(res)
15
+ return res.body if res.is_a?(Net::HTTPSuccess)
16
+ return nil if ABSENT.include?(res.code)
17
+
18
+ raise SourceUnavailable.new(refusal_kind(res), status: res.code.to_i)
19
+ end
20
+
21
+ # GitHub signale un quota épuisé par un 403 ou un 429, avec x-ratelimit-remaining à 0.
22
+ def self.refusal_kind(res)
23
+ return :rate_limited if res["x-ratelimit-remaining"] == "0"
24
+
25
+ REFUSALS.fetch(res.code) { res.is_a?(Net::HTTPServerError) ? :server_error : :forbidden }
26
+ end
27
+ end
28
+ end
@@ -11,6 +11,8 @@ module CaptiveStackDetector
11
11
  @analyzer = PackageJsonAnalyzer.new(package_json)
12
12
  end
13
13
 
14
+ def entry? = !@analyzer.type.nil?
15
+
14
16
  def detect
15
17
  type = @analyzer.type
16
18
  raise UnsupportedStack, :no_node_entry unless type
@@ -20,7 +20,7 @@ module CaptiveStackDetector
20
20
  subtype: @analyzer.subtype,
21
21
  services: Services.new(database: @analyzer.database, queue: @analyzer.queue, backend: nil),
22
22
  worker: build_worker,
23
- runtime: Runtime.new(ruby: @reader.ruby_version, node: nil),
23
+ runtime: Runtime.new(ruby: @reader.ruby_version, node: @reader.node_version),
24
24
  env_vars: @reader.env_vars,
25
25
  system_packages: system_packages,
26
26
  )
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "types"
4
+ require_relative "gemfile_analyzer"
5
+ require_relative "rails_stack_detector"
6
+ require_relative "js_stack_detector"
7
+ require_relative "static_site_detector"
8
+
9
+ module CaptiveStackDetector
10
+ # Aiguille un dépôt vers le détecteur de sa stack, d'après ses manifestes à la racine.
11
+ class StackRouter
12
+ def initialize(reader)
13
+ @reader = reader
14
+ end
15
+
16
+ def detect
17
+ gemfile = @reader.read("Gemfile")
18
+ package_json = @reader.read("package.json")
19
+
20
+ return RailsStackDetector.new(@reader, GemfileAnalyzer.new(gemfile)).detect if gemfile
21
+
22
+ static_site = StaticSiteDetector.new(@reader)
23
+ # Un package.json sans point d'entrée (Supabase CLI, prettier…) n'est que de l'outillage :
24
+ # un index.html présent reste servi tel quel au lieu d'un refus no_node_entry.
25
+ js = package_json && JsStackDetector.new(@reader, package_json)
26
+ return js.detect if js && (js.entry? || !static_site.site?)
27
+ raise UnsupportedStack, :no_manifest unless static_site.site?
28
+
29
+ static_site.detect
30
+ end
31
+ end
32
+ end
@@ -26,4 +26,17 @@ module CaptiveStackDetector
26
26
  super(reason&.to_s || "unsupported stack")
27
27
  end
28
28
  end
29
+
30
+ # GitHub n'a pas laissé lire le dépôt, ce qui ne dit rien de sa stack. `kind` dit pourquoi,
31
+ # pour que l'appelant l'explique : :unauthorized (jeton refusé), :rate_limited (quota épuisé),
32
+ # :forbidden (accès refusé), :server_error (panne GitHub), :network (pas de réponse).
33
+ class SourceUnavailable < StandardError
34
+ attr_reader :kind, :status
35
+
36
+ def initialize(kind = :network, status: nil, detail: nil)
37
+ @kind = kind
38
+ @status = status
39
+ super(detail || "GitHub illisible : #{kind}#{" (#{status})" if status}")
40
+ end
41
+ end
29
42
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CaptiveStackDetector
4
- VERSION = "0.10.0"
4
+ VERSION = "0.12.5"
5
5
  end
@@ -3,10 +3,7 @@
3
3
  require "json"
4
4
  require_relative "captive_stack_detector/types"
5
5
  require_relative "captive_stack_detector/file_reader"
6
- require_relative "captive_stack_detector/gemfile_analyzer"
7
- require_relative "captive_stack_detector/rails_stack_detector"
8
- require_relative "captive_stack_detector/js_stack_detector"
9
- require_relative "captive_stack_detector/static_site_detector"
6
+ require_relative "captive_stack_detector/stack_router"
10
7
  require_relative "captive_stack_detector/addon_recommender"
11
8
 
12
9
  # Déduit d'un dépôt sa stack, ses services et les options d'add-on à recommander.
@@ -20,21 +17,7 @@ module CaptiveStackDetector
20
17
  end
21
18
 
22
19
  def self.detect_from(reader)
23
- AddonRecommender.new(reader).apply(detect_stack(reader))
20
+ AddonRecommender.new(reader).apply(StackRouter.new(reader).detect)
24
21
  end
25
22
  private_class_method :detect_from
26
-
27
- def self.detect_stack(reader)
28
- gemfile = reader.read("Gemfile")
29
- package_json = reader.read("package.json")
30
-
31
- return RailsStackDetector.new(reader, GemfileAnalyzer.new(gemfile)).detect if gemfile
32
- return JsStackDetector.new(reader, package_json).detect if package_json
33
-
34
- static_site = StaticSiteDetector.new(reader)
35
- raise UnsupportedStack, :no_manifest unless static_site.site?
36
-
37
- static_site.detect
38
- end
39
- private_class_method :detect_stack
40
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: captive-stack-detector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.12.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Captive Studio
@@ -29,6 +29,7 @@ files:
29
29
  - lib/captive_stack_detector/gem_package_map.rb
30
30
  - lib/captive_stack_detector/gemfile_analyzer.rb
31
31
  - lib/captive_stack_detector/github_api_client.rb
32
+ - lib/captive_stack_detector/github_response.rb
32
33
  - lib/captive_stack_detector/github_tree.rb
33
34
  - lib/captive_stack_detector/js_env_detector.rb
34
35
  - lib/captive_stack_detector/js_env_files.rb
@@ -46,6 +47,7 @@ files:
46
47
  - lib/captive_stack_detector/solid_queue_usage.rb
47
48
  - lib/captive_stack_detector/solid_queue_worker.rb
48
49
  - lib/captive_stack_detector/ssr_framework_detector.rb
50
+ - lib/captive_stack_detector/stack_router.rb
49
51
  - lib/captive_stack_detector/static_site_detector.rb
50
52
  - lib/captive_stack_detector/system_package_detector.rb
51
53
  - lib/captive_stack_detector/types.rb