Class: Space::Architect::SessionSync::Plist

Inherits:
Object
  • Object
show all
Defined in:
lib/space_architect/session_sync/plist.rb

Overview

Hand-rolled launchd plist emitter for the session-sync agent, imitating Space::Src::Launchd::Plist's shape. Unlike that emitter, this one does NOT wrap the invocation in mise/ruby toolchain resolution: the architect executable is a rubygems binstub (or the dev exe/architect script, which sets up Bundler itself) and needs no toolchain pinning to run non-interactively, so ProgramArguments invokes it directly.

Constant Summary collapse

HEADER =
<<~XML
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
  <dict>
XML
"</dict>\n</plist>\n"

Class Method Summary collapse

Class Method Details

.call(label:, refresh_interval:, log_dir:, bin_path:, host:, token:) ⇒ String

Returns the full plist XML.

Parameters:

  • label (String)

    The job label (reverse-DNS; on-disk plist basename).

  • refresh_interval (Integer)

    StartInterval in seconds (must be > 0).

  • log_dir (String)

    Absolute directory for stdout/stderr logs.

  • bin_path (String)

    Absolute path to the architect bin script.

  • host (String)

    --host value passed to sessions sync.

  • token (String)

    --token value passed to sessions sync (an op:// ref is passed through verbatim — never resolved here).

Returns:

  • (String)

    the full plist XML.

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/space_architect/session_sync/plist.rb', line 29

def call(label:, refresh_interval:, log_dir:, bin_path:, host:, token:)
  raise ArgumentError, "label is required" if label.to_s.empty?
  raise ArgumentError, "refresh_interval must be > 0" unless refresh_interval.is_a?(Integer) && refresh_interval > 0
  %w[log_dir bin_path].each do |k|
    v = binding.local_variable_get(k)
    raise ArgumentError, "#{k} must be absolute (got #{v.inspect})" unless v.is_a?(String) && File.absolute_path?(v)
  end
  raise ArgumentError, "host is required" if host.to_s.empty?
  raise ArgumentError, "token is required" if token.to_s.empty?

  out_log = File.join(log_dir, "#{label}.out.log")
  err_log = File.join(log_dir, "#{label}.err.log")

  body = +""
  body << key("Label") << string(label) << "\n"
  body << key("ProgramArguments") << "\n" << array([
    bin_path,
    "sessions",
    "sync",
    "--host", host,
    "--token", token
  ])
  body << key("StartInterval") << integer(refresh_interval) << "\n"
  body << key("RunAtLoad") << boolean(true) << "\n"
  body << key("ProcessType") << string("Background") << "\n"
  body << key("StandardOutPath") << string(out_log) << "\n"
  body << key("StandardErrorPath") << string(err_log) << "\n"

  HEADER + body + FOOTER
end