locd 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb0dc7c5d3b58d3a71a6f37ca805ad663a503d47
4
- data.tar.gz: 0c4c9fe5124b32cdc7774b956bc44b95e80891c5
3
+ metadata.gz: 630061e7d172448491a1da49c70cfac510519d26
4
+ data.tar.gz: 417ac04a01d1bc9e6e888f3e8a1e847407d7b017
5
5
  SHA512:
6
- metadata.gz: 51ec7cb0807e43bebe247e25854e8a2037f524d542b7d55bd6f40433704e3c354c4859335b5c48b72a908dca41fec8f56ccad955bd65a844a6d9e13269ff886b
7
- data.tar.gz: e9e9309a808d16576fc38826a73b6f9034ce870be0b34871f57c360d398d736e61a9508ac2fb46a68f8e6c07925d38e8af334303f8c3e942b6ae4521ba992d7e
6
+ metadata.gz: f3bf11b09b944869c254175fd90984f54959596c9506220d9a79ec49fe6a4ee7d7beb355bff2773b6db552d7bec74f69d3e1c3fb7a313550790ff3334384f72e
7
+ data.tar.gz: 5c03efd66d67973b72a2608ee21c695b2e9aa473c7f9659c713da8a13736799e8941406a2c4d5b2f24ddcfff56915badb785c5ef7c949c580c728d3b49e3d5e5
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
data/exe/locd CHANGED
@@ -13,12 +13,4 @@ logger = SemanticLogger[__FILE__]
13
13
 
14
14
  require 'locd/cli'
15
15
 
16
- begin
17
- Locd::CLI::Command::Main.start
18
- rescue Exception => e
19
- if e.instance_of?(SystemExit)
20
- raise
21
- else
22
- logger.fatal e
23
- end
24
- end
16
+ Locd::CLI::Command::Main.exec!
@@ -40,14 +40,6 @@ class Locd::Agent
40
40
  # Constants
41
41
  # ==========================================================================
42
42
 
43
- # Extra plist key we store Loc'd config under (this is where the port is
44
- # stored). We also use it's presence to detect that a plist is Loc'd.
45
- #
46
- # @return [String]
47
- #
48
- CONFIG_KEY = 'locd_config'
49
-
50
-
51
43
  # Attribute / method names that {#to_h} uses.
52
44
  #
53
45
  # @return [Hamster::SortedSet<Symbol>]
@@ -69,7 +61,7 @@ class Locd::Agent
69
61
  # ==========================================================================
70
62
 
71
63
  # Test if the parse of a property list is for a Loc'd agent by seeing if
72
- # it has {CONFIG_KEY} as a key.
64
+ # it has the config key (from `Locd.config[:agent, :config_key]`) as a key.
73
65
  #
74
66
  # @param [Hash<String, Object>] plist
75
67
  # {include:file:doc/include/plist.md}
@@ -78,7 +70,7 @@ class Locd::Agent
78
70
  # `true` if the plist looks like it's from Loc'd.
79
71
  #
80
72
  def self.plist? plist
81
- plist.key? CONFIG_KEY
73
+ plist.key? Locd.config[:agent, :config_key]
82
74
  end # .plist?
83
75
 
84
76
 
@@ -351,7 +343,7 @@ class Locd::Agent
351
343
  # Parameters are passed to {Locd::Label.regexp_for_glob} and the resulting
352
344
  # {Regexp} is matched against each agent's {#label}.
353
345
  #
354
- # @see Locd::Label.regexp_for_glob
346
+ # @see Locd::Pattern
355
347
  #
356
348
  # @param (see .find_all)
357
349
  #
@@ -364,7 +356,7 @@ class Locd::Agent
364
356
 
365
357
  # Find all the agents that match a pattern.
366
358
  #
367
- # @see Locd::Label.regexp_for_glob
359
+ # @see Locd::Pattern
368
360
  #
369
361
  # @param [String | Pattern] pattern
370
362
  # Pattern to match against agent.
@@ -539,7 +531,7 @@ class Locd::Agent
539
531
 
540
532
  # Extras we need... `launchd` complains in the system log about this
541
533
  # but it's the easiest way to handle it at the moment
542
- CONFIG_KEY => {
534
+ Locd.config[:agent, :config_key] => {
543
535
  # Save this too why the hell not, might help debuging at least
544
536
  cmd_template: cmd_template,
545
537
 
@@ -636,27 +628,11 @@ class Locd::Agent
636
628
  logger.debug "Property list written", path: plist_abs_path
637
629
 
638
630
  from_path( plist_abs_path ).tap { |agent|
639
- logger.debug { ["{Agent} added", agent: agent] }
631
+ agent.send :log_info, "added"
640
632
  }
641
633
  end # .add
642
634
 
643
635
 
644
- # @!group Removing Agents
645
- # ----------------------------------------------------------------------------
646
-
647
- # Find an agent using a label pattern and call {#remove} on it.
648
- #
649
- # @param (see .find_only!)
650
- # @return (see #remove)
651
- # @raise See {.find_only!} and {#remove}
652
- #
653
- def self.remove *find_only_args
654
- Locd::Agent.find_only!( *find_only_args ).remove
655
- end # .remove
656
-
657
- # @!endgroup
658
-
659
-
660
636
  # Attributes
661
637
  # ============================================================================
662
638
 
@@ -689,9 +665,9 @@ class Locd::Agent
689
665
 
690
666
  # Sanity check...
691
667
 
692
- unless plist.key? CONFIG_KEY
668
+ unless plist.key? Locd.config[:agent, :config_key]
693
669
  raise ArgumentError.new binding.erb <<~END
694
- Not a Loc'd plist (no <%= Locd::Agent::CONFIG_KEY %> key)
670
+ Not a Loc'd plist (no <%= Locd.config[:agent, :config_key] %> key)
695
671
 
696
672
  path: <%= path %>
697
673
  plist:
@@ -779,7 +755,7 @@ class Locd::Agent
779
755
 
780
756
 
781
757
  def config
782
- plist[CONFIG_KEY]
758
+ plist[Locd.config[:agent, :config_key]]
783
759
  end
784
760
 
785
761
 
@@ -822,6 +798,18 @@ class Locd::Agent
822
798
  plist['StandardErrorPath'].to_pn if plist['StandardErrorPath']
823
799
  end
824
800
 
801
+
802
+ # Get a list of all unique log paths.
803
+ #
804
+ # @return [Array<Pathname>]
805
+ #
806
+ def log_paths
807
+ [
808
+ out_path,
809
+ err_path,
810
+ ].compact.uniq
811
+ end
812
+
825
813
  # @!endgroup Instance Methods: Attribute Readers
826
814
 
827
815
 
@@ -875,15 +863,15 @@ class Locd::Agent
875
863
  # @param [Boolean] force:
876
864
  # Force the loading of the plist. Ignore the `launchd` *Disabled* key.
877
865
  #
878
- # @param [Boolean] write:
866
+ # @param [Boolean] enable:
879
867
  # Overrides the `launchd` *Disabled* key and sets it to `false`.
880
868
  #
881
869
  # @return [self]
882
870
  #
883
- def load force: false, write: false
884
- logger.debug "Loading #{ label } agent...", force: force, write: write
871
+ def load force: false, enable: false
872
+ logger.debug "Loading #{ label } agent...", force: force, enable: enable
885
873
 
886
- result = Locd::Launchctl.load! path, force: force, write: write
874
+ result = Locd::Launchctl.load! path, force: force, write: enable
887
875
 
888
876
  message = if result.err =~ /service\ already\ loaded/
889
877
  "already loaded"
@@ -891,7 +879,7 @@ class Locd::Agent
891
879
  "LOADED"
892
880
  end
893
881
 
894
- status_info message, status: status
882
+ log_info message, status: status
895
883
 
896
884
  self
897
885
  end
@@ -901,17 +889,17 @@ class Locd::Agent
901
889
  #
902
890
  # This is a bit low-level; you probably want to use {#stop}.
903
891
  #
904
- # @param [Boolean] write:
892
+ # @param [Boolean] disable:
905
893
  # Overrides the `launchd` *Disabled* key and sets it to `true`.
906
894
  #
907
895
  # @return [self]
908
896
  #
909
- def unload write: false
910
- logger.debug "Unloading #{ label } agent...", write: write
897
+ def unload disable: false
898
+ logger.debug "Unloading #{ label } agent...", disable: disable
911
899
 
912
- result = Locd::Launchctl.unload! path, write: write
900
+ result = Locd::Launchctl.unload! path, write: disable
913
901
 
914
- status_info "UNLOADED"
902
+ log_info "UNLOADED"
915
903
 
916
904
  self
917
905
  end
@@ -922,9 +910,9 @@ class Locd::Agent
922
910
  # @param (see #load)
923
911
  # @return (see #load)
924
912
  #
925
- def reload force: false, write: false
913
+ def reload force: false, enable: false
926
914
  unload
927
- load force: force, write: write
915
+ load force: force, enable: enable
928
916
  end
929
917
 
930
918
 
@@ -935,23 +923,23 @@ class Locd::Agent
935
923
  # start, even if it's {#disabled?}).
936
924
  #
937
925
  # @param [Boolean] load:
938
- # Call {#load} first, passing it `write` and `force`.
926
+ # Call {#load} first, passing it `enable` and `force`.
939
927
  #
940
- # @param force (see #load)
941
- # @param write (see #load)
928
+ # @param force (see #load)
929
+ # @param enable (see #load)
942
930
  #
943
931
  # @return [self]
944
932
  #
945
- def start load: true, force: true, write: false
933
+ def start load: true, force: false, enable: false
946
934
  logger.trace "Starting `#{ label }` agent...",
947
935
  load: load,
948
936
  force: force,
949
- write: write
937
+ enable: enable
950
938
 
951
- self.load( force: force, write: write ) if load
939
+ self.load( force: force, enable: enable ) if load
952
940
 
953
941
  Locd::Launchctl.start! label
954
- status_info "STARTED"
942
+ log_info "STARTED"
955
943
 
956
944
  self
957
945
  end
@@ -962,19 +950,19 @@ class Locd::Agent
962
950
  # @param [Boolean] unload:
963
951
  # Call {#unload} first, passing it `write`.
964
952
  #
965
- # @param write (see #unload)
953
+ # @param disable (see #unload)
966
954
  #
967
955
  # @return [self]
968
956
  #
969
- def stop unload: true, write: false
957
+ def stop unload: true, disable: false
970
958
  logger.debug "Stopping `#{ label } agent...`",
971
959
  unload: unload,
972
- write: write
960
+ disable: disable
973
961
 
974
962
  Locd::Launchctl.stop label
975
- status_info "STOPPED"
963
+ log_info "STOPPED"
976
964
 
977
- self.unload( write: write ) if unload
965
+ self.unload( disable: disable ) if unload
978
966
 
979
967
  self
980
968
  end
@@ -982,28 +970,44 @@ class Locd::Agent
982
970
 
983
971
  # Restart the agent ({#stop} then {#start}).
984
972
  #
985
- # @param unload (see #stop)
973
+ # @param [Boolean] reload:
974
+ # Unload then load the agent in `launchd`.
975
+ #
986
976
  # @param force (see #start)
987
- # @param write (see #start)
977
+ # @param enable (see #start)
988
978
  #
989
979
  # @return [self]
990
980
  #
991
- def restart unload: true, force: true, write: false
992
- stop unload: unload
993
- start load: unload, force: force, write: write
981
+ def restart reload: true, force: true, enable: false
982
+ stop unload: reload
983
+ start load: reload, force: force, enable: enable
994
984
  end # #restart
995
985
 
996
986
 
997
987
  # Remove the agent by removing it's {#path} file. Will {#stop} and
998
988
  # {#unloads} the agent first.
999
989
  #
990
+ # @param [Boolean] logs:
991
+ # When `true` remove all logs as well.
992
+ #
1000
993
  # @return [self]
1001
994
  #
1002
- def remove
995
+ def remove logs: false
1003
996
  stop unload: true
1004
997
 
998
+ if logs
999
+ log_paths.each { |log_path|
1000
+ if log_path.exists?
1001
+ FileUtils.rm log_path
1002
+ log_info "Removed log", path: log_path.to_s
1003
+ else
1004
+ log_info "Log path does not exist", path: log_path.to_s
1005
+ end
1006
+ }
1007
+ end
1008
+
1005
1009
  FileUtils.rm path
1006
- status_info "REMOVED"
1010
+ log_info "REMOVED"
1007
1011
 
1008
1012
  self
1009
1013
  end
@@ -1011,18 +1015,25 @@ class Locd::Agent
1011
1015
  # @!endgroup Instance Methods: `launchctl` Interface
1012
1016
 
1013
1017
 
1018
+ # @!group Modifying Agents
1019
+ # --------------------------------------------------------------------------
1020
+
1014
1021
  # Update specific values on the agent, which *may* change it's file path if
1015
1022
  # a different label is provided.
1016
1023
  #
1017
1024
  # **_Does not mutate this instance! Returns a new {Locd::Agent} with the
1018
1025
  # updated values._**
1019
1026
  #
1020
- # @param (see .create_plist_data)
1027
+ # @param [Boolean] force:
1028
+ # Overwrite any existing agent with the same label.
1029
+ #
1030
+ # @param [Hash<Symbol, Object>] **values
1031
+ # See {.create_plist_data}.
1021
1032
  #
1022
1033
  # @return [Locd::Agent]
1023
1034
  # A new instance with the updated values.
1024
1035
  #
1025
- def update **values
1036
+ def update force: false, **values
1026
1037
  logger.trace "Updating `#{ label }` agent", **values
1027
1038
 
1028
1039
  # Remove the `cmd_template` if it's nil of an empty array so that
@@ -1066,16 +1077,22 @@ class Locd::Agent
1066
1077
  # another agent
1067
1078
 
1068
1079
  if File.exists? new_plist_abs_path
1069
- # There's someone already there! Bail out
1070
- raise binding.erb <<-END
1071
- A different agent already exists at:
1072
-
1073
- <%= new_plist_abs_path %>
1074
-
1075
- Remove that agent first if you really want to replace it with an
1076
- updated version of this one.
1077
-
1078
- END
1080
+ # There's someone already there!
1081
+ # Bail out unless we are forcing the operation
1082
+ if force
1083
+ logger.info "Overwriting agent #{ new_label } with update to " \
1084
+ "agent #{ label } (force: `true`)"
1085
+ else
1086
+ raise binding.erb <<-END
1087
+ A different agent already exists at:
1088
+
1089
+ <%= new_plist_abs_path %>
1090
+
1091
+ Remove that agent first if you really want to replace it with an
1092
+ updated version of this one or provide `force: true`.
1093
+
1094
+ END
1095
+ end
1079
1096
  end
1080
1097
 
1081
1098
  # Ok, we're in the clear (save for the obvious race condition, but,
@@ -1092,6 +1109,7 @@ class Locd::Agent
1092
1109
  end
1093
1110
  end # #update
1094
1111
 
1112
+ # @!endgroup Modifying Agents
1095
1113
 
1096
1114
 
1097
1115
  # @!group Instance Methods: Language Integration
@@ -1167,8 +1185,20 @@ class Locd::Agent
1167
1185
  end
1168
1186
 
1169
1187
 
1170
- def status_info message, **details
1171
- logger.info "Agent `#{ label }` #{ message }", **details
1188
+ # Log a message with a details payload, prefixing the agent's label.
1189
+ #
1190
+ # Used to relay info to the user in the CLI.
1191
+ #
1192
+ # @param [#to_s] message
1193
+ # Message to log.
1194
+ #
1195
+ # @param [Hash<Symbol, Object>] **payload
1196
+ # Optional values to dump.
1197
+ #
1198
+ # @return [void]
1199
+ #
1200
+ def log_info message, **payload
1201
+ logger.info "Agent `#{ label }` #{ message }", **payload
1172
1202
  end
1173
1203
 
1174
1204
  # end protected
@@ -113,11 +113,12 @@ class Locd::Agent::Job < Locd::Agent
113
113
 
114
114
  start_interval_data = t.match start_interval,
115
115
  t.pos_int,
116
- {"StartInterval" => start_interval},
116
+ { "StartInterval" => start_interval },
117
117
 
118
118
  Types.start_calendar_interval,
119
119
  ->( interval ) {
120
- interval.map { |key, value| [key.to_s.capitalize, value] }.to_h
120
+ { 'StartCalendarInterval' =>
121
+ interval.map { |key, value| [key.to_s.capitalize, value] }.to_h }
121
122
  },
122
123
 
123
124
  Types.start_calendar_intervals,
@@ -76,7 +76,7 @@ class Locd::Agent::Site < Locd::Agent
76
76
  # `true` if we think this `plist` belongs to a site.
77
77
  #
78
78
  def self.plist? plist
79
- !! plist.dig( Locd::Agent::CONFIG_KEY, 'port' )
79
+ !! plist.dig( Locd.config[:agent, :config_key], 'port' )
80
80
  end # .plist?
81
81
 
82
82
 
@@ -55,7 +55,7 @@ module Locd::Agent::System
55
55
  #
56
56
  def self.plist? plist
57
57
  !!(
58
- plist.dig( Locd::Agent::CONFIG_KEY, 'is_system' ) \
58
+ plist.dig( Locd.config[:agent, :config_key], 'is_system' ) \
59
59
  && label?( plist['Label'] )
60
60
  )
61
61
  end # .plist?
@@ -117,18 +117,36 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
117
117
  # Options when provided a `PATTERN` argument used to find agents by label.
118
118
  #
119
119
 
120
- shared_option :exact,
120
+ shared_option :full,
121
121
  groups: :pattern,
122
- desc: "Force PATTERN to match entire label string",
123
- aliases: '-x',
122
+ desc: "Require label PATTERN to match entire string",
123
+ aliases: '-u',
124
+ type: :boolean
125
+
126
+ shared_option :ignore_case,
127
+ groups: :pattern,
128
+ desc: "Make label PATTERN case-insensitive",
129
+ aliases: '-i',
124
130
  type: :boolean
125
131
 
126
132
  shared_option :recursive,
127
133
  groups: :pattern,
128
- desc: "Workdir patterns match all subdirs too",
134
+ desc: "Make workdir PATTERN match all subdirs too",
129
135
  aliases: '-r',
130
136
  type: :boolean
131
137
 
138
+ # NOTE We don't expose the workdir pattern's `:cwd` option...
139
+ # If you want to match a directory from the CLI, just provide that
140
+ # directory... no reason to specify the `:cwd` in an option then
141
+ # provide a relative PATTERN
142
+ #
143
+
144
+ # `:multi` Group
145
+ # ----------------------------------------------------------------------------
146
+ #
147
+ # For commands that can match multiple agents.
148
+ #
149
+
132
150
  shared_option :all,
133
151
  groups: :multi,
134
152
  desc: "Apply to ALL agents that PATTERN matches",
@@ -161,11 +179,25 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
161
179
  aliases: ['--log'],
162
180
  type: :string
163
181
 
182
+ shared_option :keep_alive,
183
+ groups: :write,
184
+ desc: "Try to keep the agent running",
185
+ type: :boolean,
186
+ default: false
187
+
188
+ shared_option :run_at_load,
189
+ groups: :write,
190
+ desc: "Start the agent when loading it",
191
+ type: :boolean,
192
+ default: false
193
+
194
+
195
+ # `:add` Group
196
+ # ----------------------------------------------------------------------------
164
197
 
165
198
  shared_option :force,
166
199
  groups: :add,
167
200
  desc: "Overwrite any existing agent",
168
- aliases: '-f',
169
201
  type: :boolean,
170
202
  default: false
171
203
 
@@ -177,12 +209,6 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
177
209
  default: true
178
210
 
179
211
 
180
- shared_option :unload,
181
- groups: :start,
182
- desc: "Unload the agent from `launchd` after stopping",
183
- type: :boolean,
184
- default: true
185
-
186
212
  # Commands
187
213
  # ============================================================================
188
214
 
@@ -191,9 +217,12 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
191
217
 
192
218
  desc "ls [PATTERN]",
193
219
  "List agents"
220
+
194
221
  map list: :ls
222
+
195
223
  include_options :long,
196
224
  groups: :pattern
225
+
197
226
  def ls pattern = nil
198
227
  results = if pattern.nil?
199
228
  agent_class.all
@@ -205,20 +234,74 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
205
234
  end
206
235
 
207
236
 
208
- # Write Commands
237
+ desc "plist PATTERN",
238
+ "Print an agent's launchd property list"
239
+
240
+ include_options groups: :pattern
241
+
242
+ def plist pattern
243
+ agent = find_only! pattern
244
+
245
+ if options[:json] || options[:yaml]
246
+ respond agent.plist
247
+ else
248
+ respond agent.path.read
249
+ end
250
+ end
251
+
252
+
253
+ desc 'status PATTERN',
254
+ "Print agent status"
255
+
256
+ include_options groups: :pattern
257
+
258
+ def status pattern
259
+ agent = find_only! pattern
260
+ respond \
261
+ label: agent.label,
262
+ port: agent.port,
263
+ status: agent.status
264
+ end
265
+
266
+
267
+ # Commands for Manipulating Agent Definitions
209
268
  # ----------------------------------------------------------------------------
210
269
 
211
270
  desc "add CMD_TEMPLATE...",
212
- "Add agents that runs a command in the current directory"
271
+ "Add an agent that runs a command in the current directory"
272
+
213
273
  include_options groups: [:write, :add, :respond_with_agents]
214
- def add *cmd_template
215
- agent = agent_class.add \
274
+
275
+ def add *cmd_template, **kwds
276
+ logger.trace __method__.to_s,
216
277
  cmd_template: cmd_template,
217
- **option_kwds( groups: :write )
278
+ kwds: kwds,
279
+ options: options
218
280
 
219
- logger.info "`#{ agent.label }` agent created."
281
+ # Merge all the keywords together into the format needed to call
282
+ # {Locd::Agent.add}
283
+ kwds.merge! **option_kwds( :force, groups: [:write] ),
284
+ cmd_template: cmd_template
220
285
 
221
- agent.load if options[:load]
286
+ # Check args
287
+
288
+ # `:cmd_template` can not be empty at this point
289
+ if kwds[:cmd_template].empty? || kwds[:cmd_template].all?( &:empty? )
290
+ raise Thor::RequiredArgumentMissingError,
291
+ "CMD_TEMPLATE argument is required to add an agent"
292
+ end
293
+
294
+ # Need a `:label` too
295
+ unless t.non_empty_str === kwds[:label]
296
+ raise Thor::RequiredArgumentMissingError,
297
+ "--label=LABEL option is required to add an agent"
298
+ end
299
+
300
+ # Do the add
301
+ agent = agent_class.add **kwds
302
+
303
+ # Reload (unless we were told not to... usually you want to reload)
304
+ agent.reload if options[:load]
222
305
 
223
306
  respond agent
224
307
  end
@@ -226,7 +309,9 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
226
309
 
227
310
  desc "update PATTERN [OPTIONS] [-- CMD_TEMPLATE...]",
228
311
  "Update an existing agent"
312
+
229
313
  include_options groups: [:pattern, :write, :respond_with_agents]
314
+
230
315
  def update pattern, *cmd_template
231
316
  agent = find_only! pattern
232
317
 
@@ -240,71 +325,193 @@ class Locd::CLI::Command::Agent < Locd::CLI::Command::Base
240
325
  end
241
326
 
242
327
 
243
- desc "open PATTERN",
244
- "Open an agent's URL in the browser"
245
- include_options groups: [:pattern, :multi]
246
- def open pattern
247
- find_multi!( pattern ).each do |agent|
248
- Cmds! "open %s", agent.url
249
- logger.info "Opened agent `#{ agent.label }` at #{ agent.url }"
250
- end
251
- end
252
-
253
-
254
328
  desc "rm PATTERN",
255
329
  "Remove (uninstall, delete) a agent"
330
+
256
331
  map remove: :rm
332
+
257
333
  include_options groups: [:pattern, :multi]
334
+
335
+ option :logs,
336
+ desc: "Remove logs too",
337
+ type: :boolean,
338
+ default: false
339
+
258
340
  def rm pattern
259
- agent_class.remove pattern, **option_kwds( :exact )
341
+ kwds = option_kwds :logs
342
+ find_multi!( pattern ).each { |agent| agent.remove **kwds }
260
343
  end
261
344
 
262
345
 
263
- desc "plist PATTERN",
264
- "Show an agent's launchd property list"
265
- include_options groups: :pattern
266
- def plist pattern
267
- # TODO Why doesn't this use {#find_only!}?
268
- agent = agent_class.find_only! pattern, **option_kwds( :exact )
269
-
270
- if options[:json] || options[:yaml]
271
- respond agent.plist
272
- else
273
- respond agent.path.read
274
- end
275
- end
346
+ # Commands for Manipulating Agent State
347
+ # --------------------------------------------------------------------------
276
348
 
277
-
278
349
  desc "start PATTERN",
279
350
  "Start an agent"
351
+
280
352
  include_options groups: :pattern
281
- option :write,
353
+
354
+ option :load,
355
+ desc: "Load the agent before starting",
356
+ type: :boolean,
357
+ default: true
358
+
359
+ option :force,
360
+ desc: "Force loading of agent even if it's disabled",
361
+ type: :boolean,
362
+ default: false
363
+
364
+ option :enable,
282
365
  desc: "Set `launchd` *Disabled* key to `false`",
283
- type: :boolean
366
+ type: :boolean,
367
+ default: false
368
+
284
369
  def start pattern
285
- find_only!( pattern ).start
370
+ find_only!( pattern ).start **option_kwds( :load, :force, :enable )
286
371
  end
287
372
 
288
373
 
289
374
  desc "stop PATTERN",
290
375
  "Stop an agent"
376
+
291
377
  include_options groups: [:pattern, :stop]
292
- option :write,
378
+
379
+ option :unload,
380
+ desc: "Unload the agent from `launchd` after stopping",
381
+ type: :boolean,
382
+ default: true
383
+
384
+ option :disable,
293
385
  desc: "Set `launchd` *Disabled* key to `true`",
294
- type: :boolean
386
+ type: :boolean,
387
+ default: false
388
+
295
389
  def stop pattern
296
- find_only!( pattern ).stop **option_kwds( :unload )
390
+ find_only!( pattern ).stop **option_kwds( :unload, :disable )
297
391
  end
298
392
 
299
393
 
300
394
  desc "restart PATTERN",
301
395
  "Restart an agent"
396
+
302
397
  include_options groups: [:pattern, :stop]
303
- option :write,
398
+
399
+ option :reload,
400
+ desc: "Unload and reload the agent in `launchd`",
401
+ type: :boolean,
402
+ default: true
403
+
404
+ option :force,
405
+ desc: "Force loading of agent even if it's disabled",
406
+ type: :boolean,
407
+ default: false
408
+
409
+ option :enable,
304
410
  desc: "Set `launchd` *Disabled* key to `false`",
305
- type: :boolean
411
+ type: :boolean,
412
+ default: false
413
+
306
414
  def restart pattern
307
- find_only!( pattern ).restart
415
+ find_only!( pattern ).restart **option_kwds( :reload, :force, :enable )
308
416
  end
309
417
 
418
+
419
+ # Assorted Other Commands
420
+ # ----------------------------------------------------------------------------
421
+
422
+ desc "open PATTERN",
423
+ "Open an agent's URL in the browser"
424
+
425
+ include_options groups: [:pattern, :multi]
426
+
427
+ def open pattern
428
+ find_multi!( pattern ).each do |agent|
429
+ Cmds! "open %s", agent.url
430
+ logger.info "Opened agent `#{ agent.label }` at #{ agent.url }"
431
+ end
432
+ end
433
+
434
+
435
+ desc 'truncate_logs PATTERN',
436
+ "Truncate agent log file(s)."
437
+
438
+ include_options groups: [:pattern, :multi]
439
+
440
+ option :restart,
441
+ desc: "Restart the agent after truncation",
442
+ type: :boolean,
443
+ default: true
444
+
445
+ def truncate_logs pattern
446
+ find_multi!( pattern ).each do |agent|
447
+ log_paths = [agent.out_path, agent.err_path].compact.uniq
448
+
449
+ unless log_paths.empty?
450
+ restart = options[:restart] && agent.running?
451
+
452
+ agent.stop if restart
453
+
454
+ log_paths.each do |log_path|
455
+ begin
456
+ log_path.open( 'w' ) { |f| f.truncate 0 }
457
+ rescue Exception => error
458
+ logger.error "Failed to truncate #{ log_path }", error
459
+ else
460
+ logger.info "Truncated",
461
+ 'file' => log_path.to_s,
462
+ 'agent.label' => agent.label
463
+ end
464
+ end # each log_path
465
+
466
+ agent.start if restart
467
+ end # unless log_paths.empty?
468
+ end # each agent
469
+ end # #truncate_logs
470
+
471
+
472
+ desc 'tail [OPTIONS] PATTERN [-- TAIL_OPTIONS]',
473
+ "Tail agent logs"
474
+
475
+ include_options groups: :pattern
476
+
477
+ option :stream,
478
+ desc: "Stream to tail. May omit if uses single log file.",
479
+ aliases: ['-s'],
480
+ type: :string,
481
+ enum: ['out', 'err']
482
+
483
+ def tail pattern, *tail_options
484
+ agent = find_only! pattern
485
+
486
+ path = case options[:stream]
487
+ when nil
488
+ paths = agent.log_paths
489
+
490
+ unless paths.length == 1
491
+ raise Thor::RequiredArgumentMissingError.new binding.erb <<~END
492
+ Agent `<%= agent.label %>` has multiple log files.
493
+
494
+ out: <%= agent.out_path.to_s %>
495
+ err: <%= agent.err_path.to_s %>
496
+
497
+ Must specify one via the `--stream` option.
498
+
499
+ END
500
+ end
501
+
502
+ paths[0]
503
+
504
+ when 'out'
505
+ agent.out_path
506
+
507
+ when 'err'
508
+ agent.err_path
509
+
510
+ else
511
+ raise "WTF"
512
+ end
513
+
514
+ exec "tail", *tail_options, path.to_s
515
+ end # #tail
516
+
310
517
  end # class Locd::CLI::Command::Agent