gemstack-dev 0.2.2 → 0.2.3

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: f069d4590853658028193e501306dbdb10f6359f0ce371920b809462a12d50a9
4
- data.tar.gz: 4b5c5c5b0ee36b1525cbba1cde1b8e96faba911fa48b18f32d87e89336ec25ab
3
+ metadata.gz: 4367307a1f2096e2672373427947112d9415a4e1b6563e5bf378ec8b730f7399
4
+ data.tar.gz: 83db91725ba722e334d68056f3063633e4c56ee43af1e89f90e3ebb8fbe77c6e
5
5
  SHA512:
6
- metadata.gz: b35e56cd5688fd0b9100e9b7b94cae53b63d77d3aeb486f670c3135dfefaec0399bfa40434457073cd1cd8258e8687842ae94be3a0a5d766456327c07786c2a8
7
- data.tar.gz: ff38af01fe6a635232490790e7a5bf39daaba3e3e6540851095ac4b26c1b7adb8523203cecc7b4da3b99bb6947bdd1ffab13bc38729b903c530614e2699e536a
6
+ metadata.gz: 3ea6107e79bc7c419c5302a37f0d67e9f7a83d29153fb81c75343ca7e37f512b33f39f4c3b4631bbe7e75e651606f0bfc02a38bdd08e5a90cc3c39c51c6a5225
7
+ data.tar.gz: 435533d5e3f323d4a6cb85e3dba4c7de7cf9e534f8ff383157279ce65ca8b7e6525f7d9524363f982f347d3866215efec0264f517dfeb26b0140a86bf0e3e3e1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.3
4
+
5
+ See the [GemStack changelog](https://github.com/gemstack-rb/gemstack/blob/main/CHANGELOG.md).
6
+
3
7
  ## 0.2.2
4
8
 
5
9
  See the [GemStack changelog](https://github.com/gemstack-rb/gemstack/blob/main/CHANGELOG.md).
@@ -22,7 +22,10 @@ module GemStack
22
22
  end
23
23
 
24
24
  def run
25
- prepare_frontend if frontend?
25
+ if frontend?
26
+ check_node!
27
+ prepare_frontend
28
+ end
26
29
  start
27
30
  install_signal_handlers
28
31
  loop_until_stopped
@@ -108,6 +111,18 @@ module GemStack
108
111
  [frontend_dir.join("node_modules/.bin/next").to_s, "dev", "--hostname", LOOPBACK, "--port", port.to_s]
109
112
  end
110
113
 
114
+ # Next.js exits at once on an old Node.js with a line that's easy to miss
115
+ # among the other output; say it up front, with the command that fixes it.
116
+ def check_node!(node = Toolchain.node)
117
+ return if node && Toolchain.node_ok?(node[:version])
118
+
119
+ wanted = [@root.join(".node-version"), @root.join(".nvmrc")].find(&:file?)&.read&.strip
120
+ wanted = Toolchain::LTS_NODE if wanted.nil? || wanted.empty?
121
+ found = node ? "Node.js #{node[:version]} (#{node[:path]})" : "no `node` on the PATH"
122
+ raise Error, "Next.js needs Node.js #{Toolchain::MIN_NODE.join(".")} or newer; found #{found}.\n " \
123
+ "→ #{Toolchain.node_hint(wanted, Toolchain.node_manager(node&.fetch(:path)))}"
124
+ end
125
+
111
126
  # Installs frontend dependencies on first run so `gemstack new && gemstack dev`
112
127
  # works even with --skip-install.
113
128
  def prepare_frontend
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "rbconfig"
5
+
6
+ module GemStack
7
+ module Dev
8
+ # The Ruby and Node.js a developer runs, and how to change them — for
9
+ # whichever version manager installed them (rbenv, rvm, asdf, mise,
10
+ # chruby, nvm, fnm, nodenv, Volta, Homebrew…). GemStack pins versions in
11
+ # files every manager reads (.ruby-version, .node-version, .nvmrc,
12
+ # .tool-versions) and never assumes one manager (DECISIONS D-066).
13
+ module Toolchain
14
+ MIN_RUBY = "3.3"
15
+ MIN_NODE = [20, 9].freeze # Next.js 16
16
+ LTS_NODE = "22"
17
+
18
+ # [pattern in the executable's path, manager name]; the first match wins.
19
+ RUBY_MANAGERS = [
20
+ [%r{/\.rbenv/}, :rbenv], [%r{/\.rvm/|/rvm/rubies/}, :rvm], [%r{/\.asdf/|/asdf/installs/}, :asdf],
21
+ [%r{/mise/installs/|/\.local/share/mise/}, :mise], [%r{/\.rubies/|/opt/rubies/}, :chruby],
22
+ [%r{/(?:opt/)?homebrew/|/usr/local/Cellar/}, :homebrew]
23
+ ].freeze
24
+ NODE_MANAGERS = [
25
+ [%r{/\.nvm/}, :nvm], [%r{/fnm/|/\.fnm/|fnm_multishells}, :fnm], [%r{/\.nodenv/}, :nodenv],
26
+ [%r{/\.asdf/|/asdf/installs/}, :asdf], [%r{/mise/installs/|/\.local/share/mise/}, :mise],
27
+ [%r{/\.volta/}, :volta], [%r{/(?:opt/)?homebrew/|/usr/local/Cellar/}, :homebrew]
28
+ ].freeze
29
+
30
+ module_function
31
+
32
+ def ruby_manager(path = RbConfig.ruby) = detect(RUBY_MANAGERS, path)
33
+ def node_manager(path) = path && detect(NODE_MANAGERS, path)
34
+
35
+ def detect(managers, path)
36
+ managers.find { |pattern, _| path.to_s.match?(pattern) }&.last
37
+ end
38
+
39
+ # How to install and select a Ruby, with the manager in use.
40
+ def ruby_hint(version, manager = ruby_manager)
41
+ {
42
+ rbenv: "rbenv install #{version} && rbenv local #{version}",
43
+ rvm: "rvm install #{version} && rvm use #{version}",
44
+ asdf: "asdf install ruby #{version} && asdf set ruby #{version}",
45
+ mise: "mise use ruby@#{version}",
46
+ chruby: "ruby-install ruby #{version}, then: chruby #{version}",
47
+ homebrew: "brew upgrade ruby (or install #{version} with rbenv, rvm, asdf or mise)"
48
+ }.fetch(manager, "install Ruby #{version} (e.g. with rbenv, rvm, asdf or mise) and make it the active Ruby")
49
+ end
50
+
51
+ # How to install and select a Node.js, with the manager in use.
52
+ def node_hint(version, manager)
53
+ {
54
+ nvm: "nvm install #{version} && nvm use #{version}",
55
+ fnm: "fnm install #{version} && fnm use #{version}",
56
+ nodenv: "nodenv install #{version} && nodenv local #{version}",
57
+ asdf: "asdf install nodejs #{version} && asdf set nodejs #{version}",
58
+ mise: "mise use node@#{version}",
59
+ volta: "volta install node@#{version}",
60
+ homebrew: "brew install node@#{version} (or use nvm, fnm, asdf or mise)"
61
+ }.fetch(manager, "install Node.js #{version} (e.g. with nvm, fnm, asdf or mise) and make it the active node")
62
+ end
63
+
64
+ # { version: "22.11.0", path: "/…/bin/node" } or nil when there's no node.
65
+ def node(run = ->(*cmd) { Open3.capture2e(*cmd) })
66
+ out, status = run.call("node", "-p", "process.version + ' ' + process.execPath")
67
+ return nil unless status.success?
68
+
69
+ version, path = out.strip.split(" ", 2)
70
+ { version: version.delete_prefix("v"), path: path }
71
+ rescue SystemCallError
72
+ nil
73
+ end
74
+
75
+ def node_ok?(version) = (version.to_s.split(".").map(&:to_i) <=> MIN_NODE) >= 0
76
+ def ruby_ok?(version = RUBY_VERSION) = Gem::Version.new(version) >= Gem::Version.new(MIN_RUBY)
77
+
78
+ # The Node.js version new apps pin: the one running `gemstack new` when
79
+ # it's new enough, else the current LTS line.
80
+ def pinned_node_version(run = ->(*cmd) { Open3.capture2e(*cmd) })
81
+ found = node(run)
82
+ found && node_ok?(found[:version]) ? found[:version] : LTS_NODE
83
+ end
84
+ end
85
+ end
86
+ end
data/lib/gemstack/dev.rb CHANGED
@@ -13,6 +13,7 @@ module GemStack
13
13
  autoload :Ports, "gemstack/dev/ports"
14
14
  autoload :Supervisor, "gemstack/dev/supervisor"
15
15
  autoload :Terminal, "gemstack/dev/terminal"
16
+ autoload :Toolchain, "gemstack/dev/toolchain"
16
17
 
17
18
  class Config < Settings
18
19
  # The single public port.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemstack-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adware Technologies
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.2
19
+ version: 0.2.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.2.2
26
+ version: 0.2.3
27
27
  email:
28
28
  - gemstack26@gmail.com
29
29
  executables: []
@@ -40,6 +40,7 @@ files:
40
40
  - lib/gemstack/dev/ports.rb
41
41
  - lib/gemstack/dev/supervisor.rb
42
42
  - lib/gemstack/dev/terminal.rb
43
+ - lib/gemstack/dev/toolchain.rb
43
44
  homepage: https://github.com/gemstack-rb/gemstack
44
45
  licenses:
45
46
  - MIT
@@ -56,7 +57,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
57
  requirements:
57
58
  - - ">="
58
59
  - !ruby/object:Gem::Version
59
- version: '4.0'
60
+ version: '3.3'
60
61
  required_rubygems_version: !ruby/object:Gem::Requirement
61
62
  requirements:
62
63
  - - ">="