Class: Terret::Home

Inherits:
Object
  • Object
show all
Defined in:
lib/terret/home.rb

Overview

Terret home: where profiles live (docs/composition.md §3).

~/.terret/
├── patch.yml                  # applies to every profile
└── profiles/<name>/{profile.yml,patch.yml}

TERRET_HOME overrides ~/.terret wholesale, which is what makes the layer stack testable — every composition test points it at a tmpdir — and what lets a deployment ship a home directory as an artifact rather than as instructions for populating a user's dotfiles.

A home that does not hold the named profile falls back to the templates this gem ships (gems/terret/profiles), so trt boot --profile headless works on a machine whose home is empty. The home always wins where it has an opinion; the shipped templates are only the floor.

Constant Summary collapse

DEFAULT =
"~/.terret"
SHIPPED =
File.expand_path("../../profiles", __dir__)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Home

Returns a new instance of Home.



35
36
37
# File 'lib/terret/home.rb', line 35

def initialize(path)
  @path = File.expand_path(path.to_s)
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



33
34
35
# File 'lib/terret/home.rb', line 33

def path
  @path
end

Class Method Details

.resolve(path = nil) ⇒ Object

ENV is read here rather than at load time so a test can set TERRET_HOME after this file is required.



25
26
27
28
29
30
31
# File 'lib/terret/home.rb', line 25

def self.resolve(path = nil)
  return path if path.is_a?(Home)

  env = ENV["TERRET_HOME"]
  env = nil if env.nil? || env.empty?
  new(path || env || DEFAULT)
end

Instance Method Details

#label(file) ⇒ Object

dump-config prints these, so they are home-relative rather than absolute: a command whose whole purpose is "show me my config" should be safe to paste into an issue, and an absolute path names the operator.



75
76
77
78
79
80
81
# File 'lib/terret/home.rb', line 75

def label(file)
  f = file.to_s
  return f.delete_prefix("#{path}/") if f.start_with?("#{path}/")
  return "terret:#{f.delete_prefix("#{SHIPPED}/")}" if f.start_with?("#{SHIPPED}/")

  f
end

#patchObject



39
# File 'lib/terret/home.rb', line 39

def patch = File.join(path, "patch.yml")

#profile_config(name) ⇒ Object



41
# File 'lib/terret/home.rb', line 41

def profile_config(name) = File.join(profile_dir(name), "profile.yml")

#profile_dir(name) ⇒ Object



40
# File 'lib/terret/home.rb', line 40

def profile_dir(name) = File.join(path, "profiles", name.to_s)

#profile_files(name) ⇒ Object

The pair of files a profile resolves to, home first and the shipped template as the floor. Either may be nil; a profile with neither does not exist.

The two files are found independently on purpose. A home holding only a patch.yml is an operator who edited the one file the template told them to edit, and tying the patch's fate to a sibling profile.yml would drop it in silence — including when what it drops is a tightening of policy.



56
57
58
59
60
61
62
# File 'lib/terret/home.rb', line 56

def profile_files(name)
  config = [profile_config(name), shipped_profile_config(name)].find { |f| File.file?(f) }
  shipped = config && config == shipped_profile_config(name)
  patch = [profile_patch(name), (shipped_profile_patch(name) if shipped)]
          .compact.find { |f| File.file?(f) }
  [config, patch]
end

#profile_namesObject

Profile names offered by this home and by the shipped templates.



65
66
67
68
69
70
# File 'lib/terret/home.rb', line 65

def profile_names
  [File.join(path, "profiles"), SHIPPED]
    .flat_map { |d| Dir.glob(File.join(d, "*", "profile.yml")) }
    .map { |f| File.basename(File.dirname(f)) }
    .uniq.sort
end

#profile_patch(name) ⇒ Object



42
# File 'lib/terret/home.rb', line 42

def profile_patch(name) = File.join(profile_dir(name), "patch.yml")

#shipped_profile_config(name) ⇒ Object



45
# File 'lib/terret/home.rb', line 45

def shipped_profile_config(name) = File.join(shipped_profile_dir(name), "profile.yml")

#shipped_profile_dir(name) ⇒ Object



44
# File 'lib/terret/home.rb', line 44

def shipped_profile_dir(name) = File.join(SHIPPED, name.to_s)

#shipped_profile_patch(name) ⇒ Object



46
# File 'lib/terret/home.rb', line 46

def shipped_profile_patch(name) = File.join(shipped_profile_dir(name), "patch.yml")

#to_sObject



83
# File 'lib/terret/home.rb', line 83

def to_s = path