Module: FzfRuby::Installer

Defined in:
ext/fzf/extconf.rb

Defined Under Namespace

Classes: InstallError

Constant Summary collapse

FZF_VERSION =

Release of fzf to install. Overridable so a project can pin a different version at install time: FZF_VERSION=0.70.0 bundle install

ENV.fetch("FZF_VERSION", "0.74.1").freeze
RELEASE_BASE =

Where release assets live.

"https://github.com/junegunn/fzf/releases/download".freeze
FZF_INSTALL_URL =

Upstream installation instructions, cited when we cannot help further.

"https://github.com/junegunn/fzf#installation".freeze
ASSET_TEMPLATE =

Asset name templates. Both are published for every release.

"fzf-%<version>s-%<slug>s.tar.gz".freeze
CHECKSUMS_TEMPLATE =
"fzf_%<version>s_checksums.txt".freeze
BIN_DIR =

Final resting place of the binary, inside the installed gem.

File.expand_path("../../lib/fzf/bin", __dir__).freeze
DOWNLOAD_TIMEOUT =

Seconds to wait on each network call before giving up.

60

Class Method Summary collapse

Class Method Details

.expected_checksum(asset) ⇒ Object

Expected SHA256 for an asset, read from the release's checksums file. The file lists one " " pair per line.

Raises:



61
62
63
64
65
66
67
# File 'ext/fzf/extconf.rb', line 61

def self.expected_checksum(asset)
  checksums = fetch("#{RELEASE_BASE}/v#{FZF_VERSION}/#{format(CHECKSUMS_TEMPLATE, version: FZF_VERSION)}")
  line = checksums.lines.find { |l| l.split(/\s+/).last == asset }
  raise InstallError, "No checksum listed for #{asset}" unless line

  line.split(/\s+/).first
end

.fetch(url) ⇒ Object

Download a URL into memory, surfacing network failures as InstallError.



47
48
49
50
51
52
53
54
55
56
57
# File 'ext/fzf/extconf.rb', line 47

def self.fetch(url)
  URI.parse(url).open(open_timeout: DOWNLOAD_TIMEOUT, read_timeout: DOWNLOAD_TIMEOUT, &:read)
rescue StandardError => e
  raise InstallError, <<~MSG
    Could not download #{url}
      #{e.class}: #{e.message}

    If this machine has no network access, install fzf yourself and point
    the gem at it with FZF_PATH=/path/to/fzf, or see #{FZF_INSTALL_URL}
  MSG
end

.installObject

Download, verify and unpack the release, leaving the binary at BIN_DIR/fzf.



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
101
102
103
104
105
# File 'ext/fzf/extconf.rb', line 70

def self.install
  slug = FzfRuby::Platform.slug
  raise unsupported_platform_error unless slug

  asset = format(ASSET_TEMPLATE, version: FZF_VERSION, slug: slug)
  url   = "#{RELEASE_BASE}/v#{FZF_VERSION}/#{asset}"

  puts "fzf-ruby: downloading #{asset}"
  tarball = fetch(url)

  # Verify before unpacking — never execute an unverified download.
  actual   = Digest::SHA256.hexdigest(tarball)
  expected = expected_checksum(asset)
  unless actual == expected
    raise InstallError, "Checksum mismatch for #{asset}\n  expected #{expected}\n  actual   #{actual}"
  end

  Dir.mktmpdir("fzf-install") do |tmpdir|
    archive = File.join(tmpdir, asset)
    File.binwrite(archive, tarball)

    # The release tarballs contain a single `fzf` binary at the root.
    unless system("tar", "-xzf", archive, "-C", tmpdir)
      raise InstallError, "Failed to unpack #{asset}"
    end

    extracted = File.join(tmpdir, "fzf")
    raise InstallError, "#{asset} did not contain an fzf binary" unless File.exist?(extracted)

    FileUtils.mkdir_p(BIN_DIR)
    FileUtils.cp(extracted, File.join(BIN_DIR, "fzf"))
    FileUtils.chmod(0o755, File.join(BIN_DIR, "fzf"))
  end

  puts "fzf-ruby: installed fzf #{FZF_VERSION} to #{BIN_DIR}/fzf"
end

.unsupported_platform_errorObject

No prebuilt release matches this machine, so there is nothing to install.



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'ext/fzf/extconf.rb', line 108

def self.unsupported_platform_error
  InstallError.new(<<~MSG)
    fzf-ruby: no prebuilt fzf release exists for this platform
      host_os:   #{RbConfig::CONFIG["host_os"]}
      host_cpu:  #{RbConfig::CONFIG["host_cpu"]}

    Install fzf by hand and point the gem at it:
      export FZF_PATH=/path/to/fzf

    Installation options, including building from source:
      #{FZF_INSTALL_URL}
  MSG
end