Module: Vcdeps::Bootstrap
- Defined in:
- lib/vcdeps/bootstrap.rb
Overview
Creates a private, registration-free vcpkg instance under vcpkg.exe bootstrap-standalone with
VCPKG_ROOT=
Constant Summary collapse
- OPEN_TIMEOUT =
30- READ_TIMEOUT =
30- MAX_REDIRECTS =
5
Class Method Summary collapse
- .absolutize(location, base) ⇒ Object
- .bootstrap_standalone!(exe, root, out) ⇒ Object
- .cleanup_tmp(tmp) ⇒ Object
-
.download!(url, dest, redirects_left = MAX_REDIRECTS) ⇒ Object
Net::HTTP GET to
dest, following up to MAX_REDIRECTS redirects, with TLS and bounded timeouts. -
.kill_and_close(pid, io) ⇒ Object
Shared abnormal-unwind teardown for the two child-spawn sites (here and Runner.run share the same shape so neither can drift): TerminateProcess the child, then close the pipe so Open3's drain returns at once.
-
.run!(out: $stderr) ⇒ Object
Idempotent: an already-valid private root returns immediately.
Class Method Details
.absolutize(location, base) ⇒ Object
152 153 154 155 |
# File 'lib/vcdeps/bootstrap.rb', line 152 def absolutize(location, base) u = URI.parse(location) u.absolute? ? location : (base + location).to_s end |
.bootstrap_standalone!(exe, root, out) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/vcdeps/bootstrap.rb', line 102 def bootstrap_standalone!(exe, root, out) env = { "VCPKG_ROOT" => root } env["VCPKG_DISABLE_METRICS"] = "1" unless ENV["VCDEPS_METRICS"] == "1" combined = +"" Open3.popen2e(env, exe, "bootstrap-standalone") do |_stdin, oe, wt| pid = wt.pid oe.binmode begin oe.each_line do |raw| line = raw.dup.force_encoding("UTF-8").scrub("�") combined << line out&.print(line) end status = wt.value unless status.success? raise BootstrapError, "vcdeps: `vcpkg bootstrap-standalone` exited " \ "#{status.exitstatus}. Output tail: " \ "#{combined[-500..] || combined}" end rescue BootstrapError raise rescue Exception # rubocop:disable Lint/RescueException # ANY abnormal unwind (Interrupt, Timeout, a raise from the out block): # kill THIS child before Open3's block teardown runs `wait_thr.join`, # which would otherwise BLOCK until the orphaned bootstrap child (busy # downloading/extracting/cloning) exits on its own. Same discipline as # Runner.run — vcpkg.exe never outlives the call (§3.2). kill_and_close(pid, oe) raise end end end |
.cleanup_tmp(tmp) ⇒ Object
157 158 159 160 161 |
# File 'lib/vcdeps/bootstrap.rb', line 157 def cleanup_tmp(tmp) File.unlink(tmp) if tmp && File.exist?(tmp) rescue StandardError nil end |
.download!(url, dest, redirects_left = MAX_REDIRECTS) ⇒ Object
Net::HTTP GET to dest, following up to MAX_REDIRECTS redirects, with
TLS and bounded timeouts. Writes atomically (temp then rename).
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/vcdeps/bootstrap.rb', line 53 def download!(url, dest, redirects_left = MAX_REDIRECTS) uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = (uri.scheme == "https") http.open_timeout = OPEN_TIMEOUT http.read_timeout = READ_TIMEOUT tmp = "#{dest}.tmp.#{Process.pid}" http.start do |conn| request = Net::HTTP::Get.new(uri) conn.request(request) do |response| case response when Net::HTTPSuccess File.open(tmp, "wb") do |io| response.read_body { |chunk| io.write(chunk) } end when Net::HTTPRedirection raise BootstrapError, "vcdeps: too many redirects fetching " \ "#{url}" if redirects_left <= 0 location = response["location"] next_url = absolutize(location, uri) # The bootstrap artifact is executed, so it MUST arrive over TLS # end-to-end. GitHub release downloads normally redirect (to # objects.githubusercontent.com); honoring a `Location: http://...` # would silently drop TLS and let an on-path attacker substitute the # executable. Refuse any non-HTTPS redirect target. unless URI.parse(next_url).scheme == "https" raise BootstrapError, "vcdeps: refusing non-HTTPS redirect to " \ "#{next_url} while fetching vcpkg.exe (TLS downgrade)" end return download!(next_url, dest, redirects_left - 1) else raise BootstrapError, "vcdeps: download of #{url} failed: " \ "#{response.code} #{response.}" end end end File.rename(tmp, dest) rescue BootstrapError cleanup_tmp(tmp) raise rescue StandardError => e cleanup_tmp(tmp) raise BootstrapError, "vcdeps: download of #{url} failed: #{e.class}: " \ "#{e.}" end |
.kill_and_close(pid, io) ⇒ Object
Shared abnormal-unwind teardown for the two child-spawn sites (here and Runner.run share the same shape so neither can drift): TerminateProcess the child, then close the pipe so Open3's drain returns at once.
139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/vcdeps/bootstrap.rb', line 139 def kill_and_close(pid, io) begin Process.kill(:KILL, pid) rescue Errno::ESRCH, RangeError nil end begin io.close rescue IOError nil end end |
.run!(out: $stderr) ⇒ Object
Idempotent: an already-valid private root returns immediately. Progress
lines to out. Raises Vcdeps::BootstrapError on any failure.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/vcdeps/bootstrap.rb', line 24 def run!(out: $stderr) root = File.join(Vcdeps.home, "vcpkg").tr("/", "\\") if (exe = ToolFinder.valid_root(root)) return ToolFinder.build_tool(exe, :private) end FileUtils.mkdir_p(root) exe_path = File.join(root, "vcpkg.exe") out&.puts("[vcdeps] downloading vcpkg.exe (~7 MB) from " \ "#{ToolFinder::BOOTSTRAP_URL} ...") download!(ToolFinder::BOOTSTRAP_URL, exe_path) out&.puts("[vcdeps] running `vcpkg bootstrap-standalone` in #{root} ...") bootstrap_standalone!(exe_path, root, out) resolved = ToolFinder.valid_root(root) unless resolved raise BootstrapError, "vcdeps: bootstrap-standalone completed but " \ "#{root} is not a valid vcpkg root (.vcpkg-root missing). Run " \ "`vcdeps doctor`." end ToolFinder.build_tool(resolved, :private) end |