Top Level Namespace

Defined Under Namespace

Modules: GoDbscanFilterBinary

Instance Method Summary collapse

Instance Method Details

#detect_platformObject

Determine platform



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'ext/go_dbscan_filter_binary/extconf.rb', line 13

def detect_platform
  os = case RbConfig::CONFIG['host_os']
  when /linux/i
    'linux'
  when /darwin|mac os/i
    'darwin'
  when /mswin|msys|mingw|cygwin|bccwin|wince|emc/i
    'windows'
  else
    raise "Unsupported OS: #{RbConfig::CONFIG['host_os']}"
  end

  arch = case RbConfig::CONFIG['host_cpu']
  when /x86_64|amd64/i
    'amd64'
  when /arm64|aarch64/i
    'arm64'
  else
    raise "Unsupported architecture: #{RbConfig::CONFIG['host_cpu']}"
  end

  [os, arch]
end

#download_binary(os, arch, version) ⇒ Object

Download and extract the binary



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'ext/go_dbscan_filter_binary/extconf.rb', line 191

def download_binary(os, arch, version)
  puts "Downloading go_dbscan_filter binary for #{os}/#{arch}..."

  # Construct download URL
  archive_name = "go-dbscan-filter_#{os}_#{arch}"
  archive_name += '.zip' if os == 'windows'
  archive_name += '.tar.gz' unless os == 'windows'

  url = "https://github.com/serg-kovalev/go-dbscan-filter/releases/download/v#{version}/#{archive_name}"

  puts "Downloading from: #{url}"

  # Download the archive (open-uri handles redirects automatically)
  begin
    response_body = URI.open(url, 'User-Agent' => 'go_dbscan_filter_binary-gem', &:read)
  rescue OpenURI::HTTPError => e
    raise "Failed to download binary: HTTP #{e.io.status[0]} #{e.io.status[1]}"
  rescue => e
    raise "Failed to download binary: #{e.message}"
  end

  # Determine binary name
  binary_name = 'go_dbscan_filter'
  binary_name += '.exe' if os == 'windows'

  # Download binary to lib/go_dbscan_filter_binary/bin/ (wrapper script stays in bin/)
  # From ext/go_dbscan_filter_binary/extconf.rb, go up to gem root
  gem_root = File.expand_path('../..', File.dirname(__FILE__))
  bin_dir = File.join(gem_root, 'lib', 'go_dbscan_filter_binary', 'bin')
  FileUtils.mkdir_p(bin_dir)
  output_path = File.join(bin_dir, binary_name)

  # Extract binary
  success = if os == 'windows'
    extract_zip(response_body, binary_name, output_path)
  else
    extract_tar_gz(response_body, binary_name, output_path)
  end

  unless success && File.exist?(output_path)
    raise "Binary #{binary_name} was not extracted correctly"
  end

  puts "Binary downloaded successfully to #{output_path}"
  output_path
end

#extract_tar_gz(data, binary_name, output_path) ⇒ Object

Extract tar.gz using system tar command (more reliable)



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
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
135
136
137
138
139
140
141
# File 'ext/go_dbscan_filter_binary/extconf.rb', line 72

def extract_tar_gz(data, binary_name, output_path)
  require 'tempfile'
  temp_tar = Tempfile.new(['binary', '.tar.gz'])
  temp_tar.binmode
  temp_tar.write(data)
  temp_tar.close

  # Create temp directory for extraction
  temp_dir = Dir.mktmpdir('go_dbscan_filter_extract')
  begin
    # Extract entire archive to temp directory
    if system("tar -xzf '#{temp_tar.path}' -C '#{temp_dir}' 2>/dev/null")
      # Find the binary in the extracted files (could be at root or in subdirectory)
      found_binary = nil
      Dir.glob(File.join(temp_dir, '**', binary_name)).each do |file|
        if File.file?(file) && File.executable?(file) || true # Accept any file matching the name
          found_binary = file
          break
        end
      end

      # Also check root level
      root_binary = File.join(temp_dir, binary_name)
      found_binary = root_binary if File.exist?(root_binary) && !found_binary

      if found_binary && File.exist?(found_binary)
        FileUtils.cp(found_binary, output_path)
        File.chmod(0o755, output_path)
        temp_tar.unlink
        return true
      end
    end
  ensure
    FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
  end

  # Fallback: manual extraction
  io = StringIO.new(data)
  gz = Zlib::GzipReader.new(io)
  tar_data = gz.read
  gz.close

  # Simple tar extraction
  pos = 0
  while pos < tar_data.length
    header = tar_data[pos, 512]
    break if header.nil? || header.empty? || header[0] == "\0"

    name = header[0, 100].strip
    size_str = header[124, 12].strip
    size = size_str.oct rescue 0

    pos += 512

    if name.end_with?(binary_name) && size > 0
      binary_data = tar_data[pos, size]
      File.open(output_path, 'wb') do |f|
        f.write(binary_data)
      end
      File.chmod(0o755, output_path)
      temp_tar.unlink
      return true
    end

    pos += (size + 511) / 512 * 512
  end

  temp_tar.unlink
  false
end

#extract_zip(data, binary_name, output_path) ⇒ Object

Extract zip using system unzip command (more reliable)



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'ext/go_dbscan_filter_binary/extconf.rb', line 144

def extract_zip(data, binary_name, output_path)
  require 'tempfile'
  temp_zip = Tempfile.new(['binary', '.zip'])
  temp_zip.binmode
  temp_zip.write(data)
  temp_zip.close

  # Create temp directory for extraction
  temp_dir = Dir.mktmpdir('go_dbscan_filter_extract')
  begin
    # Extract entire archive to temp directory
    if system("unzip -q '#{temp_zip.path}' -d '#{temp_dir}' 2>/dev/null")
      # Find the binary in the extracted files
      found_binary = nil
      Dir.glob(File.join(temp_dir, '**', binary_name)).each do |file|
        if File.file?(file)
          found_binary = file
          break
        end
      end

      # Also check root level
      root_binary = File.join(temp_dir, binary_name)
      found_binary = root_binary if File.exist?(root_binary) && !found_binary

      if found_binary && File.exist?(found_binary)
        FileUtils.cp(found_binary, output_path)
        temp_zip.unlink
        return true
      end
    end

    # Fallback: try direct extraction to output directory
    if system("unzip -q -j '#{temp_zip.path}' '*#{binary_name}' -d '#{File.dirname(output_path)}' 2>/dev/null")
      result = File.exist?(output_path)
      temp_zip.unlink
      return result
    end
  ensure
    FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
  end

  temp_zip.unlink
  false
end

#get_gem_versionObject

Get the gem version (should match the release version)



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
65
66
67
68
69
# File 'ext/go_dbscan_filter_binary/extconf.rb', line 38

def get_gem_version
  # When installed, extconf.rb runs in: gem_dir/ext/go_dbscan_filter_binary/extconf.rb
  # We need to find: gem_dir/lib/go_dbscan_filter_binary/version.rb

  # Calculate gem root: go up two directory levels from extconf.rb
  # Use expand_path to ensure we get absolute path
  extconf_dir = File.expand_path(File.dirname(__FILE__))
  # extconf_dir = gem_dir/ext/go_dbscan_filter_binary
  ext_dir = File.expand_path('..', extconf_dir)
  # ext_dir = gem_dir/ext
  gem_root = File.expand_path('..', ext_dir)
  # gem_root = gem_dir

  version_file = File.join(gem_root, 'lib', 'go_dbscan_filter_binary', 'version.rb')

  # Try to load the version file directly
  if File.exist?(version_file)
    version_content = File.read(version_file)
    if version_content =~ /VERSION\s*=\s*['"]([^'"]+)['"]/
      return $1
    end
  end

  # Fallback: try to require it
  begin
    $LOAD_PATH.unshift(File.join(gem_root, 'lib'))
    require 'go_dbscan_filter_binary/version'
    return GoDbscanFilterBinary::VERSION
  rescue LoadError => e
    raise "Could not determine gem version. Tried: #{version_file} (gem_root: #{gem_root}, extconf_dir: #{extconf_dir})"
  end
end