Module: Ruflet::Rails::WebInstaller
- Defined in:
- lib/ruflet/rails/web_installer.rb
Overview
Installs the prebuilt Ruflet web client into a Rails app’s frontend/ directory. The web client is the ‘ruflet_client-web.tar.gz` release asset (the same prebuilt Flutter web build shipped on GitHub releases) — so the Rails app serves a web frontend without ever running a Flutter build.
rake ruflet:web
The extracted build is what Ruflet::Rails.web_app serves (its default build dir is <Rails.root>/frontend).
Constant Summary collapse
- REPO =
"AdamMusa/Ruflet"- ASSET_NAME =
"ruflet_client-web.tar.gz"- TARGET_DIR =
"frontend"
Class Method Summary collapse
- .download(url, path) ⇒ Object
- .env_asset_name ⇒ Object
- .env_tag ⇒ Object
- .extract(archive, target) ⇒ Object
- .github_json(url) ⇒ Object
-
.install!(root:, dir: TARGET_DIR, asset_name: env_asset_name, tag: env_tag, force: true) ⇒ Object
Downloads and extracts the web client into <root>/<dir>.
- .release_asset_url(asset_name, tag: nil) ⇒ Object
- .replace_target(staging, target) ⇒ Object
- .safe_archive?(archive) ⇒ Boolean
Class Method Details
.download(url, path) ⇒ Object
93 94 95 96 97 |
# File 'lib/ruflet/rails/web_installer.rb', line 93 def download(url, path) ok = system("curl", "-sSL", "--fail", "-o", path, url) warn "Download failed: #{url}" unless ok ok end |
.env_asset_name ⇒ Object
126 127 128 129 |
# File 'lib/ruflet/rails/web_installer.rb', line 126 def env_asset_name value = ENV["RUFLET_RAILS_WEB_ARTIFACT"].to_s.strip value.empty? ? ASSET_NAME : value end |
.env_tag ⇒ Object
131 132 133 134 |
# File 'lib/ruflet/rails/web_installer.rb', line 131 def env_tag value = ENV["RUFLET_RAILS_WEB_TAG"].to_s.strip value.empty? ? nil : value end |
.extract(archive, target) ⇒ Object
99 100 101 |
# File 'lib/ruflet/rails/web_installer.rb', line 99 def extract(archive, target) system("tar", "-xzf", archive, "-C", target) end |
.github_json(url) ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/ruflet/rails/web_installer.rb', line 79 def github_json(url) out, status = Open3.capture2( "curl", "-sSL", "--fail", "-H", "Accept: application/vnd.github+json", "-H", "User-Agent: ruflet_rails", url ) return nil unless status.success? JSON.parse(out) rescue JSON::ParserError nil end |
.install!(root:, dir: TARGET_DIR, asset_name: env_asset_name, tag: env_tag, force: true) ⇒ Object
Downloads and extracts the web client into <root>/<dir>. Returns true on success. The asset name and release tag are overridable via ENV so a project can pin a specific build.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/ruflet/rails/web_installer.rb', line 29 def install!(root:, dir: TARGET_DIR, asset_name: env_asset_name, tag: env_tag, force: true) target = File.join(root.to_s, dir) url = release_asset_url(asset_name, tag: tag) unless url warn "Ruflet web client asset #{asset_name.inspect} not found in the #{REPO} release#{tag ? " #{tag}" : " (latest)"}." return false end Dir.mktmpdir("ruflet-web-") do |tmp| archive = File.join(tmp, asset_name) return false unless download(url, archive) staging = File.join(tmp, "frontend") FileUtils.mkdir_p(staging) unless safe_archive?(archive) && extract(archive, staging) warn "Failed to safely extract #{asset_name}." return false end unless File.file?(File.join(staging, "index.html")) warn "Extracted web client but no index.html was found." return false end return false if Dir.exist?(target) && !force replace_target(staging, target) end puts "Installed Ruflet web client into #{target} (#{asset_name})." true rescue SystemCallError => e warn "Failed to install Ruflet web client into #{target}: #{e.}" false end |
.release_asset_url(asset_name, tag: nil) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/ruflet/rails/web_installer.rb', line 66 def release_asset_url(asset_name, tag: nil) api = tag ? "releases/tags/#{tag}" : "releases/latest" release = github_json("https://api.github.com/repos/#{REPO}/#{api}") return nil unless release asset = Array(release["assets"]).find { |a| a["name"] == asset_name } unless asset names = Array(release["assets"]).map { |a| a["name"] } warn "Available release assets: #{names.join(', ')}" unless names.empty? end asset && asset["browser_download_url"] end |
.replace_target(staging, target) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ruflet/rails/web_installer.rb', line 113 def replace_target(staging, target) FileUtils.mkdir_p(File.dirname(target)) backup = "#{target}.ruflet-backup" FileUtils.rm_rf(backup) FileUtils.mv(target, backup) if File.exist?(target) FileUtils.mv(staging, target) FileUtils.rm_rf(backup) rescue StandardError FileUtils.rm_rf(target) FileUtils.mv(backup, target) if File.exist?(backup) raise end |
.safe_archive?(archive) ⇒ Boolean
103 104 105 106 107 108 109 110 111 |
# File 'lib/ruflet/rails/web_installer.rb', line 103 def safe_archive?(archive) out, status = Open3.capture2("tar", "-tzf", archive) return false unless status.success? out.lines.all? do |line| path = line.strip !path.start_with?("/") && path.split("/").none?("..") end end |