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 +4 -4
- data/bin/login.rb +27 -0
- data/lib/kanseishitsu/launch_agent_manager.rb +5 -1
- data/lib/kanseishitsu/login/argument_parser.rb +138 -0
- data/lib/kanseishitsu/plist_parser.rb +0 -54
- data/lib/kanseishitsu/version.rb +1 -1
- metadata +21 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76738d937dbb0737e7c75c45c902dd5738c5458983747152b8da87a69aa76b3f
|
4
|
+
data.tar.gz: 807fa8f7ac548fac96fcbdbdb447296a882c0213ddaa503878cc38419e59d33a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
data/lib/kanseishitsu/version.rb
CHANGED
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.
|
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:
|
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
|
-
|
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.
|
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: []
|