kanseishitsu 0.1.1 → 0.1.2

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
  SHA256:
3
- metadata.gz: 25050a62712f4707b8f1b388647b618d7d722fc15f9a7713a6a2b06d504236d6
4
- data.tar.gz: adf5c5eba9308caeb08cec65eefa966caf9f9ab6784eeef243d50030be5a0f79
3
+ metadata.gz: 76738d937dbb0737e7c75c45c902dd5738c5458983747152b8da87a69aa76b3f
4
+ data.tar.gz: 807fa8f7ac548fac96fcbdbdb447296a882c0213ddaa503878cc38419e59d33a
5
5
  SHA512:
6
- metadata.gz: 1ec5b00e46746dbf649876030ca6b7825471c2e4d22ea1f8ba5af2ca7adbdc03328ae61be34b74f81537c99aa4cc75be9dcd60425f44a60584ec433121c6f8a2
7
- data.tar.gz: 6f89365ab7c0d2cc4d724ec71fae360cc66d70bb94492b209b66dba2cb23f470d22fcb634b7eb1776190774c8aa36ea0131304c9060c35a7e569a69a54f80eda
6
+ metadata.gz: b14232a69f39ce7ce7454e4b04c9c8c3d1a7c4d229cdeebbb425d8a3a895c14a5388edd982d015f62ea0bf4215aaf3f0063a2cbd51333e14200e0267388e706e
7
+ data.tar.gz: 88070b3cd9c37891f816d841d118ec7488127849414ce01e23c605b5f60f7d1d48601f1530f9ca436feb63e3c5455c1c8c173774f57dd80fadaecf71258e0ef0
data/bin/login.rb ADDED
@@ -0,0 +1,27 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ # encoding: utf-8
4
+ # frozen_string_literal: false
5
+
6
+ # -*- mode: ruby -*-
7
+ # vi: set ft=ruby :
8
+
9
+ # Copyright Nels Nelson 2024 but freely usable (see license)
10
+
11
+ # Usage: watch.rb <path> [handler_program_path]
12
+ #
13
+ # Options:
14
+ # --show-all Show watchers
15
+ # -l, --list List launch agent labels
16
+ # -r, --remove=<label> Remove a watcher by label
17
+ #
18
+ # Examples:
19
+ #
20
+ # watch.rb ~/Desktop ~/.local/usr/bin/desktop.rb
21
+ # watch.rb --show-all
22
+ # watch.rb --list
23
+ # watch.rb --remove=com.local.desktop
24
+
25
+ require_relative '../lib/kanseishitsu'
26
+
27
+ Object.new.extend(Login).main
@@ -172,7 +172,11 @@ module LaunchAgentManagementInstanceMethods
172
172
  def show_all_launch_agents
173
173
  Dir.glob(File.join(LAUNCH_AGENTS_DIR_PATH, '*.plist')).map do |plist_path|
174
174
  job = parse_plist(plist_path)
175
- puts "#{job[:schedule]} #{job[:command]}"
175
+ if job[:command].empty?
176
+ puts "@disabled #{plist_path}"
177
+ else
178
+ puts "#{job[:schedule]} #{job[:command]}"
179
+ end
176
180
  end
177
181
  end
178
182
 
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # Copyright Nels Nelson 2024 but freely usable (see license)
8
+
9
+ require 'optparse'
10
+
11
+ require_relative '../which'
12
+ require_relative '../version'
13
+
14
+ # Define module Login
15
+ module Login
16
+ # Define the ArgumentsParser class
17
+ class ArgumentsParser
18
+ UNDERSCORE_PATTERN = %r{_}
19
+ HYPHEN_STRING = '-'.freeze
20
+ FLAGS = %i[banner show_all list remove verbose].freeze
21
+ POSITIONAL = %i[watch_path executable_path_with_args].freeze
22
+ attr_reader :parser, :options
23
+
24
+ def initialize(parser = OptionParser.new)
25
+ @parser = parser
26
+ @options = {}
27
+ FLAGS.each { |method_name| self.method(method_name).call }
28
+ end
29
+
30
+ def banner
31
+ @parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} " \
32
+ '<watch_path> <executable_path_with_args>'
33
+ @parser.separator ''
34
+ @parser.separator 'Options:'
35
+ end
36
+
37
+ def show_all
38
+ @parser.on('--show-all', 'Show login jobs') do
39
+ @options[:show_all] = true
40
+ end
41
+ end
42
+
43
+ def list
44
+ @parser.on('-l', '--list', 'List login job labels') do
45
+ @options[:list] = true
46
+ end
47
+ end
48
+
49
+ def remove
50
+ @parser.on('-r', '--remove=<label>', 'Remove a login job by label') do |label|
51
+ @options[:remove] = label
52
+ end
53
+ end
54
+
55
+ def verbose
56
+ @options[:log_level] ||= Logger::INFO
57
+ @parser.on_tail('-v', '--verbose', 'Increase verbosity') do
58
+ @options[:log_level] -= 1
59
+ end
60
+ end
61
+
62
+ def version
63
+ @parser.on_tail('--version', 'Show version') do
64
+ puts "#{File.basename($PROGRAM_NAME)} version #{Kanseishitsu::VERSION}"
65
+ exit
66
+ end
67
+ end
68
+
69
+ def watch_path(args)
70
+ return if (v = args.shift).nil?
71
+
72
+ watch_path = File.expand_path(v)
73
+ unless File.exist?(watch_path) &&
74
+ File.directory?(watch_path)
75
+ message = "Directory not found: #{watch_path}"
76
+ raise OptionParser::InvalidArgument, message
77
+ end
78
+ @options[:watch_path] = [watch_path]
79
+ end
80
+
81
+ def executable_path_with_args(args)
82
+ executable, *args = args
83
+ return if executable.nil?
84
+
85
+ executable_path = Object.new.extend(Which).which(executable)
86
+ if executable_path.nil?
87
+ message = "Executable not found: #{executable_path}"
88
+ raise OptionParser::InvalidArgument, message
89
+ end
90
+ @options[:executable_path_with_args] = [executable_path] + args
91
+ end
92
+
93
+ def demand(arg, positional: false)
94
+ return @options[arg] unless @options[arg].nil?
95
+
96
+ required_arg = if positional then "<#{arg}>"
97
+ else "--#{arg.to_s.gsub(UNDERSCORE_PATTERN, HYPHEN_STRING)}"
98
+ end
99
+ raise OptionParser::MissingArgument, "Required argument: #{required_arg}"
100
+ end
101
+
102
+ def positional!(args)
103
+ POSITIONAL.each do |opt|
104
+ self.method(opt).call(args)
105
+ self.demand(opt, positional: true)
106
+ end
107
+ end
108
+
109
+ def options?
110
+ ArgumentsParser::FLAGS.any? { |flag| @options.include?(flag) }
111
+ end
112
+
113
+ def usage!
114
+ puts @parser
115
+ exit
116
+ end
117
+
118
+ # rubocop: disable Metrics/MethodLength
119
+ def self.parse(args = ARGV, _file_path = ARGF, arguments_parser = ArgumentsParser.new)
120
+ arguments_parser.parser.parse!(args)
121
+ if !arguments_parser.options? && ARGV.length != 2
122
+ message = 'A directory and executable handler is required'
123
+ raise OptionParser::MissingArgument, message
124
+ elsif !args.empty?
125
+ arguments_parser.positional!(args)
126
+ end
127
+ arguments_parser.options
128
+ rescue OptionParser::AmbiguousOption => e
129
+ abort e.message
130
+ rescue OptionParser::ParseError => e
131
+ puts e.message
132
+ arguments_parser.usage!
133
+ end
134
+ # rubocop: enable Metrics/MethodLength
135
+ end
136
+ # class ArgumentsParser
137
+ end
138
+ # module Login
@@ -10,60 +10,6 @@ require 'rexml/document'
10
10
 
11
11
  require_relative 'cron'
12
12
 
13
- # Define the PlistParser class
14
- # class PlistParser
15
- # LOCAL_STRING_XPATH = './string'.freeze
16
- # PROGRAM_ARGUMENTS_XPATH =
17
- # '//key[text()="ProgramArguments"]/following-sibling::array[1]'.freeze
18
- # LABEL_XPATH = '//key[text()="Label"]/following-sibling::*[1]'.freeze
19
- # CALENDAR_XPATH = '//key[text()="StartCalendarInterval"]/following-sibling::*[1]'.freeze
20
- # SCHEDULE_XPATH_TEMPLATE = './/key[text()="%<key>s"]/following-sibling::integer'.freeze
21
- # ON_LOGIN = '@login'.freeze
22
-
23
- # # Parse the Plist file at the given path
24
- # def parse(plist_path)
25
- # plist = {}
26
- # doc = REXML::Document.new(File.read(plist_path))
27
- # label = parse_plist_label(doc)
28
- # command = parse_program_arguments(doc, label)
29
- # cron_schedule = parse_calendar_interval(doc)
30
- # plist[:label] = label unless label.nil?
31
- # plist[:command] = command unless command.nil?
32
- # plist[:schedule] = cron_schedule unless cron_schedule.nil?
33
- # plist
34
- # end
35
-
36
- # # Parse the label from the Plist file document
37
- # def parse_plist_label(doc)
38
- # label_element = REXML::XPath.first(doc, LABEL_XPATH)
39
- # return nil if label_element.nil?
40
-
41
- # label = label_element.text
42
- # return nil if label.empty?
43
-
44
- # label
45
- # end
46
-
47
- # # Parse the program arguments from the Plist file document
48
- # def parse_program_arguments(doc, label)
49
- # program_args_elements = REXML::XPath.match(doc, PROGRAM_ARGUMENTS_XPATH)
50
- # program_args = program_args_elements.flat_map do |node|
51
- # REXML::XPath.match(node, LOCAL_STRING_XPATH).map(&:text)
52
- # end
53
- # program_args.empty? ? label : program_args.join(' ')
54
- # end
55
-
56
- # # Parse the calendar interval from the Plist file document
57
- # def parse_calendar_interval(doc)
58
- # intervals = doc.xpath(CALENDAR_XPATH).first
59
- # return ON_LOGIN unless intervals
60
-
61
- # Cron::INTERVALS.map do |interval|
62
- # intervals.at_xpath(format(SCHEDULE_XPATH_TEMPLATE, key: interval)).text rescue '*'
63
- # end.join(' ')
64
- # end
65
- # end
66
-
67
13
  # Define class PlistParser
68
14
  class PlistParser
69
15
  EMPTY_STRING = ''.freeze
@@ -7,5 +7,5 @@
7
7
  # Copyright Nels Nelson 2024 but freely usable (see license)
8
8
 
9
9
  module Kanseishitsu
10
- VERSION = '0.1.1'.freeze
10
+ VERSION = '0.1.2'.freeze
11
11
  end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kanseishitsu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nels Nelson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-05-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: logger
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 1.7.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 1.7.0
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: nokogiri
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -24,7 +37,6 @@ dependencies:
24
37
  - - "~>"
25
38
  - !ruby/object:Gem::Version
26
39
  version: 1.16.4
27
- force_ruby_platform: false
28
40
  - !ruby/object:Gem::Dependency
29
41
  name: syslog
30
42
  requirement: !ruby/object:Gem::Requirement
@@ -39,12 +51,12 @@ dependencies:
39
51
  - - "~>"
40
52
  - !ruby/object:Gem::Version
41
53
  version: 0.1.2
42
- force_ruby_platform: false
43
- description: Simplified LaunchAgents management for macOS. This gem packageprovides
54
+ description: Simplified LaunchAgents management for macOS. This gem package provides
44
55
  command-line tools for managing macOS launch agents.
45
56
  email: nels@nelsnelson.org
46
57
  executables:
47
58
  - cron.rb
59
+ - login.rb
48
60
  - watch.rb
49
61
  extensions: []
50
62
  extra_rdoc_files: []
@@ -52,6 +64,7 @@ files:
52
64
  - LICENSE.md
53
65
  - README.md
54
66
  - bin/cron.rb
67
+ - bin/login.rb
55
68
  - bin/watch.rb
56
69
  - lib/kanseishitsu.rb
57
70
  - lib/kanseishitsu/cron.rb
@@ -60,6 +73,7 @@ files:
60
73
  - lib/kanseishitsu/launch_agent_constants.rb
61
74
  - lib/kanseishitsu/launch_agent_manager.rb
62
75
  - lib/kanseishitsu/logging.rb
76
+ - lib/kanseishitsu/login/argument_parser.rb
63
77
  - lib/kanseishitsu/plist_parser.rb
64
78
  - lib/kanseishitsu/version.rb
65
79
  - lib/kanseishitsu/watch.rb
@@ -74,7 +88,6 @@ metadata:
74
88
  source_code_uri: https://gitlab.com/nelsnelson/kanseishitsu
75
89
  bug_tracker_uri: https://gitlab.com/nelsnelson/kanseishitsu/issues
76
90
  rubygems_mfa_required: 'true'
77
- post_install_message:
78
91
  rdoc_options: []
79
92
  require_paths:
80
93
  - lib
@@ -89,8 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
102
  - !ruby/object:Gem::Version
90
103
  version: '0'
91
104
  requirements: []
92
- rubygems_version: 3.5.9
93
- signing_key:
105
+ rubygems_version: 3.6.7
94
106
  specification_version: 4
95
107
  summary: Simplified LaunchAgents management for macOS packaged as a gem.
96
108
  test_files: []