fzf-ruby 0.1.0 → 0.1.1

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: 1d8aeb61620f712a78d80f9efe0928314cd9eb96fcb8e47c15bf4911f7d31393
4
- data.tar.gz: 68d72dcb58df89ac5d8f415af888d36e008dfad7c68fe50e2034f912b0fc5859
3
+ metadata.gz: 3219f16fc90118b1bccef8ba71a98faab74a9b8bc007997e4d4ef2904ee652bf
4
+ data.tar.gz: abafdedf74c201202bf79c916f5c536ed12979d651c82198304ae0c45b3b6c68
5
5
  SHA512:
6
- metadata.gz: c18947f2ec169459782fdee57c3d0c5d6174407da594eaabef6babf6e8b53c75414c67d2583c6a4a7319f3c97f1af7f482b0cd6f179d3bae4110e551b2388e7d
7
- data.tar.gz: 04ea977125b483d2e55007bfceb15af4cc5660f2a5f9f90cc522df00c81030b86897763d764c6f4e4e8ae8f0073154c3866d1175492496a534e467cee56be679
6
+ metadata.gz: 1ad1d363b716fc4fd20cf43d253c06a56ee81df717748c71bd458f5a3b66b1fe3515339ff5170c3d3e780f51f6fe8c7f7fc73f218bc4942a4f7d4988dc0697bc
7
+ data.tar.gz: f9b0fbe46c078db77f8373586fac96bbd4bffa8c52c8a138bbd94636693b2774789f17562b1c3f065097749fb3a8139e3d2a3c2b27cbceaa82ab7dcc6c618b1d
@@ -0,0 +1,137 @@
1
+ # Install-time hook: fetch the fzf binary so the gem works straight after
2
+ # `gem install` / `bundle install`, with no follow-up steps for the user.
3
+ #
4
+ # RubyGems runs this file as a "native extension". Nothing is compiled — the
5
+ # official prebuilt release for the host platform is downloaded, checksum
6
+ # verified, and unpacked into the gem's own lib/fzf/bin/ directory, which is
7
+ # where FzfRuby.fzf_binary looks. A no-op Makefile is written at the end
8
+ # because RubyGems always invokes `make` afterwards.
9
+ #
10
+ # Building from source is deliberately not attempted here: it would require Go
11
+ # on every machine that installs the gem. `rake fzf:install` (lib/fzf/tasks.rb)
12
+ # still offers that route for anyone who wants it.
13
+
14
+ require "digest"
15
+ require "fileutils"
16
+ require "open-uri"
17
+ require "tmpdir"
18
+
19
+ require_relative "../../lib/fzf/platform"
20
+
21
+ module FzfRuby
22
+ module Installer
23
+ # Release of fzf to install. Overridable so a project can pin a different
24
+ # version at install time: FZF_VERSION=0.70.0 bundle install
25
+ FZF_VERSION = ENV.fetch("FZF_VERSION", "0.74.1").freeze
26
+
27
+ # Where release assets live.
28
+ RELEASE_BASE = "https://github.com/junegunn/fzf/releases/download".freeze
29
+
30
+ # Upstream installation instructions, cited when we cannot help further.
31
+ FZF_INSTALL_URL = "https://github.com/junegunn/fzf#installation".freeze
32
+
33
+ # Asset name templates. Both are published for every release.
34
+ ASSET_TEMPLATE = "fzf-%<version>s-%<slug>s.tar.gz".freeze
35
+ CHECKSUMS_TEMPLATE = "fzf_%<version>s_checksums.txt".freeze
36
+
37
+ # Final resting place of the binary, inside the installed gem.
38
+ BIN_DIR = File.expand_path("../../lib/fzf/bin", __dir__).freeze
39
+
40
+ # Seconds to wait on each network call before giving up.
41
+ DOWNLOAD_TIMEOUT = 60
42
+
43
+ # Raised when no usable binary can be installed.
44
+ class InstallError < StandardError; end
45
+
46
+ # Download a URL into memory, surfacing network failures as InstallError.
47
+ def self.fetch(url)
48
+ URI.parse(url).open(open_timeout: DOWNLOAD_TIMEOUT, read_timeout: DOWNLOAD_TIMEOUT, &:read)
49
+ rescue StandardError => e
50
+ raise InstallError, <<~MSG
51
+ Could not download #{url}
52
+ #{e.class}: #{e.message}
53
+
54
+ If this machine has no network access, install fzf yourself and point
55
+ the gem at it with FZF_PATH=/path/to/fzf, or see #{FZF_INSTALL_URL}
56
+ MSG
57
+ end
58
+
59
+ # Expected SHA256 for an asset, read from the release's checksums file.
60
+ # The file lists one "<sha256> <filename>" pair per line.
61
+ def self.expected_checksum(asset)
62
+ checksums = fetch("#{RELEASE_BASE}/v#{FZF_VERSION}/#{format(CHECKSUMS_TEMPLATE, version: FZF_VERSION)}")
63
+ line = checksums.lines.find { |l| l.split(/\s+/).last == asset }
64
+ raise InstallError, "No checksum listed for #{asset}" unless line
65
+
66
+ line.split(/\s+/).first
67
+ end
68
+
69
+ # Download, verify and unpack the release, leaving the binary at BIN_DIR/fzf.
70
+ def self.install
71
+ slug = FzfRuby::Platform.slug
72
+ raise unsupported_platform_error unless slug
73
+
74
+ asset = format(ASSET_TEMPLATE, version: FZF_VERSION, slug: slug)
75
+ url = "#{RELEASE_BASE}/v#{FZF_VERSION}/#{asset}"
76
+
77
+ puts "fzf-ruby: downloading #{asset}"
78
+ tarball = fetch(url)
79
+
80
+ # Verify before unpacking — never execute an unverified download.
81
+ actual = Digest::SHA256.hexdigest(tarball)
82
+ expected = expected_checksum(asset)
83
+ unless actual == expected
84
+ raise InstallError, "Checksum mismatch for #{asset}\n expected #{expected}\n actual #{actual}"
85
+ end
86
+
87
+ Dir.mktmpdir("fzf-install") do |tmpdir|
88
+ archive = File.join(tmpdir, asset)
89
+ File.binwrite(archive, tarball)
90
+
91
+ # The release tarballs contain a single `fzf` binary at the root.
92
+ unless system("tar", "-xzf", archive, "-C", tmpdir)
93
+ raise InstallError, "Failed to unpack #{asset}"
94
+ end
95
+
96
+ extracted = File.join(tmpdir, "fzf")
97
+ raise InstallError, "#{asset} did not contain an fzf binary" unless File.exist?(extracted)
98
+
99
+ FileUtils.mkdir_p(BIN_DIR)
100
+ FileUtils.cp(extracted, File.join(BIN_DIR, "fzf"))
101
+ FileUtils.chmod(0o755, File.join(BIN_DIR, "fzf"))
102
+ end
103
+
104
+ puts "fzf-ruby: installed fzf #{FZF_VERSION} to #{BIN_DIR}/fzf"
105
+ end
106
+
107
+ # No prebuilt release matches this machine, so there is nothing to install.
108
+ def self.unsupported_platform_error
109
+ InstallError.new(<<~MSG)
110
+ fzf-ruby: no prebuilt fzf release exists for this platform
111
+ host_os: #{RbConfig::CONFIG["host_os"]}
112
+ host_cpu: #{RbConfig::CONFIG["host_cpu"]}
113
+
114
+ Install fzf by hand and point the gem at it:
115
+ export FZF_PATH=/path/to/fzf
116
+
117
+ Installation options, including building from source:
118
+ #{FZF_INSTALL_URL}
119
+ MSG
120
+ end
121
+ end
122
+ end
123
+
124
+ FzfRuby::Installer.install
125
+
126
+ # RubyGems runs `make` and `make install` after this script regardless of what
127
+ # it did, so hand it a Makefile whose targets are all satisfied.
128
+ File.write("Makefile", <<~MAKEFILE)
129
+ all:
130
+ \t@true
131
+
132
+ install:
133
+ \t@true
134
+
135
+ clean:
136
+ \t@true
137
+ MAKEFILE
@@ -0,0 +1,65 @@
1
+ # Host platform detection, in the naming scheme fzf uses.
2
+ #
3
+ # fzf's release assets and Go's own toolchain share one naming convention
4
+ # ("linux_amd64", "darwin_arm64"), so a single set of maps serves both the
5
+ # install-time downloader (ext/fzf/extconf.rb) and the source build
6
+ # (lib/fzf/tasks.rb). This file is the one home for those names.
7
+ #
8
+ # Detection returns nil rather than aborting — callers decide whether an
9
+ # unsupported platform is fatal (source build) or merely a reason to fall
10
+ # back to a system fzf (install-time download).
11
+
12
+ require "rbconfig"
13
+
14
+ module FzfRuby
15
+ module Platform
16
+ # RbConfig host_cpu → fzf/Go architecture name.
17
+ # Keys are matched as prefixes, since host_cpu carries suffixes such as
18
+ # "armv7l" that the release names ("armv7") do not.
19
+ ARCH_MAP = {
20
+ "x86_64" => "amd64",
21
+ "amd64" => "amd64",
22
+ "aarch64" => "arm64",
23
+ "arm64" => "arm64",
24
+ "armv7" => "armv7",
25
+ "armv6" => "armv6",
26
+ "armv5" => "armv5",
27
+ "loong64" => "loong64",
28
+ "ppc64le" => "ppc64le",
29
+ "riscv64" => "riscv64",
30
+ "s390x" => "s390x",
31
+ }.freeze
32
+
33
+ # RbConfig host_os → fzf/Go operating system name.
34
+ # Keys are matched as prefixes ("linux-gnu", "darwin23" and so on).
35
+ OS_MAP = {
36
+ "linux" => "linux",
37
+ "darwin" => "darwin",
38
+ "freebsd" => "freebsd",
39
+ "openbsd" => "openbsd",
40
+ }.freeze
41
+
42
+ # Operating system in fzf's naming, or nil when unrecognised.
43
+ def self.os
44
+ host = RbConfig::CONFIG["host_os"]
45
+ OS_MAP.each { |prefix, name| return name if host.start_with?(prefix) }
46
+ nil
47
+ end
48
+
49
+ # Architecture in fzf's naming, or nil when unrecognised.
50
+ def self.arch
51
+ cpu = RbConfig::CONFIG["host_cpu"]
52
+ ARCH_MAP.each { |prefix, name| return name if cpu.start_with?(prefix) }
53
+ nil
54
+ end
55
+
56
+ # Platform slug as it appears in a release asset name, e.g. "linux_amd64".
57
+ # nil when either half is unrecognised, meaning no prebuilt asset exists.
58
+ def self.slug
59
+ detected_os, detected_arch = os, arch
60
+ return nil unless detected_os && detected_arch
61
+
62
+ "#{detected_os}_#{detected_arch}"
63
+ end
64
+ end
65
+ end
data/lib/fzf/tasks.rb CHANGED
@@ -14,6 +14,8 @@ require "fileutils"
14
14
  require "open-uri"
15
15
  require "rbconfig"
16
16
 
17
+ require_relative "platform"
18
+
17
19
  module FzfRuby
18
20
  module Tasks
19
21
  extend Rake::DSL
@@ -27,29 +29,19 @@ module FzfRuby
27
29
  # Go download base URL
28
30
  GO_DL_BASE = "https://go.dev/dl"
29
31
 
30
- # Map Ruby's RbConfig values to Go's platform naming
31
- ARCH_MAP = {
32
- "x86_64" => "amd64",
33
- "aarch64" => "arm64",
34
- "arm64" => "arm64",
35
- }.freeze
36
-
37
- OS_MAP = {
38
- "linux" => "linux",
39
- "darwin" => "darwin",
40
- }.freeze
32
+ # Go and fzf share one platform naming scheme, so the maps live in
33
+ # FzfRuby::Platform and are used by the install-time downloader too.
34
+ # Platform returns nil for anything unrecognised; Go's own downloads are
35
+ # all-or-nothing, so here that is fatal.
41
36
 
42
37
  # Detect the host OS in Go's naming convention
43
38
  def self.detect_os
44
- host = RbConfig::CONFIG["host_os"]
45
- OS_MAP.each { |prefix, name| return name if host.start_with?(prefix) }
46
- abort "Unsupported OS for Go auto-install: #{host}"
39
+ FzfRuby::Platform.os || abort("Unsupported OS for Go auto-install: #{RbConfig::CONFIG["host_os"]}")
47
40
  end
48
41
 
49
42
  # Detect the host architecture in Go's naming convention
50
43
  def self.detect_arch
51
- cpu = RbConfig::CONFIG["host_cpu"]
52
- ARCH_MAP.fetch(cpu) { abort "Unsupported arch for Go auto-install: #{cpu}" }
44
+ FzfRuby::Platform.arch || abort("Unsupported arch for Go auto-install: #{RbConfig::CONFIG["host_cpu"]}")
53
45
  end
54
46
 
55
47
  # Fetch the latest stable Go version string (e.g. "go1.26.5")
data/lib/fzf/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # Gem version constant
2
2
  module FzfRuby
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
data/lib/fzf.rb CHANGED
@@ -34,8 +34,11 @@ module FzfRuby
34
34
  # Raised when fzf binary is not found in PATH
35
35
  class FzfNotFoundError < StandardError; end
36
36
 
37
+ # Binary installed with the gem itself by ext/fzf/extconf.rb.
38
+ VENDORED_BINARY = File.join(__dir__, "fzf", "bin", "fzf").freeze
39
+
37
40
  # Resolve the fzf binary path.
38
- # Priority: FZF_PATH env var → project bin/fzf → system PATH
41
+ # Priority: FZF_PATH env var → project bin/fzf → gem's own bin → system PATH
39
42
  def self.fzf_binary
40
43
  # Explicit override via environment variable
41
44
  from_env = ENV["FZF_PATH"]
@@ -45,6 +48,9 @@ module FzfRuby
45
48
  local = File.join(project_root, "bin", "fzf")
46
49
  return local if File.executable?(local)
47
50
 
51
+ # Binary downloaded when the gem was installed
52
+ return VENDORED_BINARY if File.executable?(VENDORED_BINARY)
53
+
48
54
  # Fall back to system PATH
49
55
  "fzf"
50
56
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fzf-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - asmrtfm
@@ -15,25 +15,19 @@ description: Pipes items into fzf and returns the user's selection. Supports mul
15
15
  email:
16
16
  - asmrtfm@gmail.com
17
17
  executables: []
18
- extensions: []
18
+ extensions:
19
+ - ext/fzf/extconf.rb
19
20
  extra_rdoc_files: []
20
21
  files:
22
+ - ext/fzf/extconf.rb
21
23
  - lib/fzf.rb
24
+ - lib/fzf/platform.rb
22
25
  - lib/fzf/tasks.rb
23
26
  - lib/fzf/version.rb
24
27
  homepage: https://github.com/asmrtfm/fzf-ruby
25
28
  licenses:
26
29
  - MIT
27
30
  metadata: {}
28
- post_install_message: |
29
- fzf-ruby requires the `fzf` binary.
30
-
31
- Build from source (requires Go >= 1.23):
32
- 1. Add to your Rakefile: require "fzf/tasks"
33
- 2. Run: rake fzf:install
34
-
35
- This clones and compiles fzf into your project's bin/ directory.
36
- Alternatively, install fzf system-wide: https://github.com/junegunn/fzf#installation
37
31
  rdoc_options: []
38
32
  require_paths:
39
33
  - lib