Module: Tempest::Commands::Base

Defined in:
lib/tempest/commands/base.rb

Constant Summary collapse

VALID_FORMATS =
%i[line json raw].freeze

Class Method Summary collapse

Class Method Details

.authenticate(env:, stderr:, user: nil, logger: nil) ⇒ Object

Loads the per-account cached session and refreshes it. Returns the session on success. On failure writes a human-readable line to stderr and returns nil; callers translate the nil into the appropriate exit code via ‘exit_code_for` (or `authenticate_with_code` for a more specific exit code).

‘user:` is the value of the global `–user <handle|did>` flag, or nil for the default account.



23
24
25
26
# File 'lib/tempest/commands/base.rb', line 23

def authenticate(env:, stderr:, user: nil, logger: nil)
  session, _code = authenticate_with_code(env: env, stderr: stderr, user: user, logger: logger)
  session
end

.authenticate_with_code(env:, stderr:, user: nil, logger: nil) ⇒ Object

Like ‘authenticate` but additionally returns the exit code that the CLI should use when session is nil. Exit codes follow the spec: 2 for “unknown user” / “no accounts configured”, 3 for session missing or refresh failure.



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
59
# File 'lib/tempest/commands/base.rb', line 32

def authenticate_with_code(env:, stderr:, user: nil, logger: nil)
  Tempest::AccountsMigration.run(env: env, stderr: stderr, logger: logger)
  accounts = Tempest::AccountsStore.new(env: env, logger: logger)

  target = resolve_target(accounts, user, stderr)
  return [nil, 2] if target.nil?

  session_store = Tempest::SessionStore.for(env, did: target.did)
  session = session_store.load(identifier: nil, pds_host: nil)
  if session.nil?
    stderr.puts "error: session for @#{target.handle} missing — run `tempest login` to re-authenticate"
    return [nil, 3]
  end

  session.identifier ||= target.identifier
  session.on_change = ->(s) {
    session_store.save(s, identifier: s.identifier || target.identifier)
    accounts.update_handle(did: s.did, handle: s.handle) if s.did && s.handle
  }

  begin
    session.refresh!
  rescue Tempest::Error => e
    stderr.puts "error: session for @#{target.handle} expired — run `tempest login` to re-authenticate (#{e.message})"
    return [nil, 3]
  end
  [session, 0]
end

.default_format(stdout:, env:) ⇒ Object

Returns one of :line, :json, :raw. Callers may override with –format.



85
86
87
# File 'lib/tempest/commands/base.rb', line 85

def default_format(stdout:, env:)
  stdout.respond_to?(:tty?) && stdout.tty? ? :line : :json
end

.exit_code_for(error) ⇒ Object



108
109
110
111
112
113
114
115
116
# File 'lib/tempest/commands/base.rb', line 108

def exit_code_for(error)
  case error
  when Tempest::Config::MissingValue then 2
  when Tempest::AuthenticationError  then 3
  when Tempest::APIError             then 4
  when ArgumentError                 then 64
  else                                    1
  end
end

.resolve_target(accounts, user, stderr) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/tempest/commands/base.rb', line 61

def resolve_target(accounts, user, stderr)
  if user
    target = accounts.resolve(user)
    if target.nil?
      stderr.puts "error: unknown user: #{user} (run `tempest accounts list` to see known accounts)"
      return nil
    end
    return target
  end

  if accounts.default
    return accounts.resolve(accounts.default)
  end

  if accounts.accounts.empty?
    stderr.puts "error: no accounts configured — run `tempest login` to add one"
    return nil
  end

  stderr.puts "error: no default account set — run `tempest accounts set-default <handle>`"
  nil
end

.take_format(argv, default:) ⇒ Object

Parses –format=NAME from argv (destructive: returns [format, argv_without_flag]). Raises ArgumentError on unknown format names.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/tempest/commands/base.rb', line 91

def take_format(argv, default:)
  out = []
  chosen = default
  argv.each do |arg|
    if (m = arg.match(/\A--format=(\S+)\z/))
      sym = m[1].to_sym
      raise ArgumentError, "invalid --format: #{m[1].inspect}" unless VALID_FORMATS.include?(sym)
      chosen = sym
    elsif arg == "--no-color"
      Tempest::REPL::Formatter.color = false if defined?(Tempest::REPL::Formatter)
    else
      out << arg
    end
  end
  [chosen, out]
end