Class: GDKBox::HostsFile

Inherits:
Object
  • Object
show all
Defined in:
lib/gdkbox/hosts_file.rb

Overview

Manages the gdk.local entry in the host's /etc/hosts.

GDK generates URLs and redirects that use the gdk.local hostname, so the host's browser needs it mapped to loopback to reach a box's web UI. The line gdkbox adds is tagged with a marker comment so it can be recognized and removed later without touching entries the user wrote themselves.

/etc/hosts is root-owned: writes are attempted directly first (covers tests and unusual setups), then via sudo tee. Callers get a symbol describing what happened — :present/:added/:removed/:absent/:manual — and decide how to talk to the user; :manual means nothing was changed and the user has to edit the file themselves.

Constant Summary collapse

HOSTNAME =
Config::GDK_HOSTNAME
MARKER =
"# added by gdkbox"
ENTRY =
"127.0.0.1 #{HOSTNAME} #{MARKER}"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path: "/etc/hosts", shell: Shell.new) ⇒ HostsFile

Returns a new instance of HostsFile.



23
24
25
26
# File 'lib/gdkbox/hosts_file.rb', line 23

def initialize(path: "/etc/hosts", shell: Shell.new)
  @path = path
  @shell = shell
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



21
22
23
# File 'lib/gdkbox/hosts_file.rb', line 21

def path
  @path
end

Instance Method Details

#add(sudo: true) ⇒ Object

Ensure the entry exists. Returns :present when it already does, :added on success, or :manual when the file could not be written (with sudo declined/failed or disabled).



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/gdkbox/hosts_file.rb', line 39

def add(sudo: true)
  return :present if entry?

  File.open(path, "a") { |f| f.puts(ENTRY) }
  :added
rescue Errno::EACCES
  return :manual unless sudo

  result = @shell.run("sudo", "tee", "-a", path, input: "#{ENTRY}\n")
  result.success? ? :added : :manual
end

#entry?Boolean

Whether any active (non-comment) line already maps HOSTNAME, regardless of who added it — a user's own entry counts, so we never duplicate it.

Returns:

  • (Boolean)


30
31
32
33
34
# File 'lib/gdkbox/hosts_file.rb', line 30

def entry?
  return false unless File.readable?(path)

  File.foreach(path).any? { |line| maps_hostname?(line) }
end

#remove(sudo: true) ⇒ Object

Remove the gdkbox-managed entry (matched by MARKER; user-written lines are never touched). Returns :absent when there is nothing to remove, :removed on success, or :manual when the file could not be written.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/gdkbox/hosts_file.rb', line 54

def remove(sudo: true)
  lines = File.readable?(path) ? File.readlines(path) : []
  kept = lines.reject { |line| managed?(line) }
  return :absent if kept.size == lines.size

  content = kept.join
  begin
    File.write(path, content)
    :removed
  rescue Errno::EACCES
    return :manual unless sudo

    result = @shell.run("sudo", "tee", path, input: content)
    result.success? ? :removed : :manual
  end
end