pennyworth 10.0.1 → 11.1.1
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.adoc +352 -31
- data/bin/pennyworth +1 -3
- data/lib/pennyworth.rb +11 -2
- data/lib/pennyworth/cli/actions/config.rb +35 -0
- data/lib/pennyworth/cli/actions/encodings.rb +23 -0
- data/lib/pennyworth/cli/actions/git_hub.rb +27 -0
- data/lib/pennyworth/cli/actions/http_statuses.rb +23 -0
- data/lib/pennyworth/cli/actions/ruby_gems.rb +27 -0
- data/lib/pennyworth/cli/actions/system/errors.rb +25 -0
- data/lib/pennyworth/cli/actions/system/signals.rb +25 -0
- data/lib/pennyworth/cli/actions/text.rb +23 -0
- data/lib/pennyworth/cli/configuration/content.rb +28 -0
- data/lib/pennyworth/cli/configuration/defaults.yml +25 -0
- data/lib/pennyworth/cli/configuration/loader.rb +37 -0
- data/lib/pennyworth/cli/parsers.rb +11 -0
- data/lib/pennyworth/cli/parsers/assembler.rb +32 -0
- data/lib/pennyworth/cli/parsers/core.rb +100 -0
- data/lib/pennyworth/cli/parsers/git_hub.rb +51 -0
- data/lib/pennyworth/cli/parsers/ruby_gems.rb +35 -0
- data/lib/pennyworth/cli/shell.rb +81 -0
- data/lib/pennyworth/container.rb +40 -0
- data/lib/pennyworth/identity.rb +2 -1
- data/lib/pennyworth/inflector.rb +18 -0
- data/lib/pennyworth/integrations/git_hub/client.rb +59 -0
- data/lib/pennyworth/integrations/git_hub/response.rb +33 -0
- data/lib/pennyworth/integrations/ruby_gems/client.rb +29 -0
- data/lib/pennyworth/loaders/encodings.rb +31 -0
- data/lib/pennyworth/loaders/http_statuses.rb +28 -0
- data/lib/pennyworth/loaders/projects.rb +22 -0
- data/lib/pennyworth/loaders/system/errors.rb +33 -0
- data/lib/pennyworth/loaders/system/signals.rb +21 -0
- data/lib/pennyworth/loaders/text.rb +27 -0
- data/lib/pennyworth/models/encoding.rb +8 -0
- data/lib/pennyworth/models/http_status.rb +8 -0
- data/lib/pennyworth/models/system/error.rb +10 -0
- data/lib/pennyworth/models/system/signal.rb +10 -0
- data/lib/pennyworth/models/text.rb +8 -0
- data/lib/pennyworth/presenters/encoding.rb +22 -0
- data/lib/pennyworth/presenters/gem.rb +48 -0
- data/lib/pennyworth/presenters/http_status.rb +41 -0
- data/lib/pennyworth/presenters/repository.rb +35 -0
- data/lib/pennyworth/presenters/system/error.rb +28 -0
- data/lib/pennyworth/presenters/system/signal.rb +22 -0
- data/lib/pennyworth/presenters/text.rb +25 -0
- data/lib/pennyworth/processor.rb +68 -0
- data/lib/pennyworth/serializers/encoding.rb +33 -0
- data/lib/pennyworth/serializers/http_status.rb +41 -0
- data/lib/pennyworth/serializers/project.rb +52 -0
- data/lib/pennyworth/serializers/system/error.rb +46 -0
- data/lib/pennyworth/serializers/system/signal.rb +40 -0
- data/lib/pennyworth/serializers/text.rb +31 -0
- metadata +110 -19
- metadata.gz.sig +0 -0
- data/lib/pennyworth/cli.rb +0 -140
- data/lib/pennyworth/templates/workflows/user.workflow.B59B22A2-1880-4765-9358-412791BE9202/icon.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.B59B22A2-1880-4765-9358-412791BE9202/info.plist +0 -159
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/icon.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/info.plist +0 -272
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-camelcase.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-capitalize.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-lowercase.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-size.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-snakecase.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-titleize.png +0 -0
- data/lib/pennyworth/templates/workflows/user.workflow.C431E56A-0EC2-47EE-94D5-D67D9FE323BE/string-uppercase.png +0 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/structs"
|
4
|
+
|
5
|
+
module Pennyworth
|
6
|
+
module CLI
|
7
|
+
module Parsers
|
8
|
+
# Handles parsing of Command Line Interface (CLI) GitHub options.
|
9
|
+
class GitHub
|
10
|
+
using Refinements::Structs
|
11
|
+
|
12
|
+
def self.call(...) = new(...).call
|
13
|
+
|
14
|
+
def initialize configuration = Configuration::Loader.call, client: CLIENT
|
15
|
+
@configuration = configuration
|
16
|
+
@client = client
|
17
|
+
end
|
18
|
+
|
19
|
+
def call arguments = []
|
20
|
+
client.separator "\nGITHUB OPTIONS:\n"
|
21
|
+
private_methods.sort.grep(/add_/).each { |method| __send__ method }
|
22
|
+
arguments.empty? ? arguments : client.parse!(arguments)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :configuration, :client
|
28
|
+
|
29
|
+
def add_organization
|
30
|
+
client.on(
|
31
|
+
"--organization [HANDLE]",
|
32
|
+
%(Set organization. Default: "#{organization}".)
|
33
|
+
) do |handle|
|
34
|
+
configuration.merge! action_git_hub: :organization,
|
35
|
+
git_hub_organization: handle || organization
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_user
|
40
|
+
client.on "--user [HANDLE]", %(Set user. Default: "#{user}".) do |handle|
|
41
|
+
configuration.merge! action_git_hub: :user, git_hub_user: handle || user
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def organization = configuration.git_hub_organization
|
46
|
+
|
47
|
+
def user = configuration.git_hub_user
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module CLI
|
5
|
+
module Parsers
|
6
|
+
# Handles parsing of Command Line Interface (CLI) RubyGems options.
|
7
|
+
class RubyGems
|
8
|
+
def self.call(...) = new(...).call
|
9
|
+
|
10
|
+
def initialize configuration = Configuration::Loader.call, client: CLIENT
|
11
|
+
@configuration = configuration
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
def call arguments = []
|
16
|
+
client.separator "\nRUBYGEMS OPTIONS:\n"
|
17
|
+
add_owner
|
18
|
+
arguments.empty? ? arguments : client.parse!(arguments)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :configuration, :client
|
24
|
+
|
25
|
+
def add_owner
|
26
|
+
client.on "--owner [HANDLE]", %(Set owner. Default: "#{owner}".) do |handle|
|
27
|
+
configuration.ruby_gems_owner = handle || owner
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def owner = configuration.ruby_gems_owner
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module CLI
|
5
|
+
# The main Command Line Interface (CLI) object.
|
6
|
+
class Shell
|
7
|
+
ACTIONS = {
|
8
|
+
config: Actions::Config.new,
|
9
|
+
encodings: Actions::Encodings.new,
|
10
|
+
git_hub: Actions::GitHub.new,
|
11
|
+
http_statuses: Actions::HTTPStatuses.new,
|
12
|
+
ruby_gems: Actions::RubyGems.new,
|
13
|
+
system_errors: Actions::System::Errors.new,
|
14
|
+
system_signals: Actions::System::Signals.new,
|
15
|
+
text: Actions::Text.new
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def initialize parser: Parsers::Assembler.new, actions: ACTIONS, container: Container
|
19
|
+
@parser = parser
|
20
|
+
@actions = actions
|
21
|
+
@container = container
|
22
|
+
end
|
23
|
+
|
24
|
+
def call arguments = []
|
25
|
+
perform parser.call(arguments)
|
26
|
+
rescue OptionParser::ParseError, KeyError => error
|
27
|
+
logger.error error.message
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :parser, :actions, :container
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/MethodLength
|
35
|
+
def perform configuration
|
36
|
+
case configuration
|
37
|
+
in action_config: Symbol => action then config action
|
38
|
+
in action_encodings: true then encodings
|
39
|
+
in action_git_hub: :organization then git_hub_organization configuration
|
40
|
+
in action_git_hub: :user then git_hub_user configuration
|
41
|
+
in action_http_statuses: true then http_statuses
|
42
|
+
in action_ruby_gems: true then ruby_gems configuration
|
43
|
+
in action_system_signals: true then system_signals
|
44
|
+
in action_system_errors: true then system_errors
|
45
|
+
in action_text: String => content then text content
|
46
|
+
in action_version: String => version then logger.info version
|
47
|
+
else usage
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# rubocop:enable Metrics/MethodLength
|
51
|
+
|
52
|
+
def config(action) = actions.fetch(__method__).call(action)
|
53
|
+
|
54
|
+
def encodings = actions.fetch(__method__).call
|
55
|
+
|
56
|
+
def git_hub_organization configuration
|
57
|
+
actions.fetch(:git_hub).call("orgs/#{configuration.git_hub_organization}/repos")
|
58
|
+
end
|
59
|
+
|
60
|
+
def git_hub_user configuration
|
61
|
+
actions.fetch(:git_hub).call("users/#{configuration.git_hub_user}/repos")
|
62
|
+
end
|
63
|
+
|
64
|
+
def http_statuses = actions.fetch(__method__).call
|
65
|
+
|
66
|
+
def ruby_gems configuration
|
67
|
+
actions.fetch(__method__).call("owners/#{configuration.ruby_gems_owner}/gems.json")
|
68
|
+
end
|
69
|
+
|
70
|
+
def system_errors = actions.fetch(__method__).call
|
71
|
+
|
72
|
+
def system_signals = actions.fetch(__method__).call
|
73
|
+
|
74
|
+
def text(content) = actions.fetch(__method__).call(content)
|
75
|
+
|
76
|
+
def usage = logger.unknown { parser.to_s }
|
77
|
+
|
78
|
+
def logger = container[__method__]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-container"
|
4
|
+
require "logger"
|
5
|
+
require "http"
|
6
|
+
require "pastel"
|
7
|
+
|
8
|
+
module Pennyworth
|
9
|
+
# Provides a global gem container for injection into other objects.
|
10
|
+
module Container
|
11
|
+
extend Dry::Container::Mixin
|
12
|
+
|
13
|
+
register(:configuration) { CLI::Configuration::Loader.call }
|
14
|
+
register(:environment) { ENV }
|
15
|
+
register(:kernel) { Kernel }
|
16
|
+
register(:http) { HTTP }
|
17
|
+
register(:colorizer) { Pastel.new enabled: $stdout.tty? }
|
18
|
+
|
19
|
+
register :log_colors do
|
20
|
+
{
|
21
|
+
"DEBUG" => self[:colorizer].white.detach,
|
22
|
+
"INFO" => self[:colorizer].green.detach,
|
23
|
+
"WARN" => self[:colorizer].yellow.detach,
|
24
|
+
"ERROR" => self[:colorizer].red.detach,
|
25
|
+
"FATAL" => self[:colorizer].white.bold.on_red.detach,
|
26
|
+
"ANY" => self[:colorizer].white.bold.detach
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
register :logger do
|
31
|
+
Logger.new $stdout,
|
32
|
+
level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
|
33
|
+
formatter: (
|
34
|
+
lambda do |severity, _at, _name, message|
|
35
|
+
self[:log_colors][severity].call "#{message}\n"
|
36
|
+
end
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/pennyworth/identity.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
# Overrides any string to desired form if matched, otherwise answers the original string.
|
5
|
+
class Inflector
|
6
|
+
DEFAULTS = Array(CLI::Configuration::Loader.call.inflections).reduce({}, :merge)
|
7
|
+
|
8
|
+
def initialize overrides = DEFAULTS
|
9
|
+
@overrides = overrides
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(key) = overrides.fetch(key) { |fallback| fallback }
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :overrides
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/arrays"
|
4
|
+
|
5
|
+
module Pennyworth
|
6
|
+
module Integrations
|
7
|
+
module GitHub
|
8
|
+
# Provides low-level API access to the GitHub API.
|
9
|
+
class Client
|
10
|
+
using Refinements::Arrays
|
11
|
+
|
12
|
+
HEADERS = {
|
13
|
+
accept: "application/vnd.github.v3+json",
|
14
|
+
content_type: "Content-Type: application/json; charset=utf-8"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
def initialize headers: HEADERS, container: Container
|
18
|
+
@headers = headers
|
19
|
+
@container = container
|
20
|
+
end
|
21
|
+
|
22
|
+
def get endpoint, parameters: {}
|
23
|
+
paginate ->(page) { sole_get endpoint, parameters: parameters.merge(page: page) }
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :headers, :container
|
29
|
+
|
30
|
+
# :reek:FeatureEnvy
|
31
|
+
def paginate request, records: [], page: 1
|
32
|
+
response = request.call page
|
33
|
+
|
34
|
+
records.including(response.records)
|
35
|
+
.then do |updated_records|
|
36
|
+
return updated_records if response.last_page.zero?
|
37
|
+
|
38
|
+
paginate request, records: updated_records, page: response.next_page
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def sole_get endpoint, parameters: {}
|
43
|
+
http.headers(headers)
|
44
|
+
.basic_auth(user: api_login, pass: api_token)
|
45
|
+
.get("#{api_url}/#{endpoint}", params: parameters)
|
46
|
+
.then { |response| Response.new response }
|
47
|
+
end
|
48
|
+
|
49
|
+
def http = container[__method__]
|
50
|
+
|
51
|
+
def api_url = container[:configuration].git_hub_api_url
|
52
|
+
|
53
|
+
def api_login = container[:environment].fetch("GITHUB_API_LOGIN")
|
54
|
+
|
55
|
+
def api_token = container[:environment].fetch("GITHUB_API_TOKEN")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module Integrations
|
5
|
+
module GitHub
|
6
|
+
# Represents a generic GitHub API response.
|
7
|
+
class Response
|
8
|
+
def initialize raw
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def next_page = page("next")
|
13
|
+
|
14
|
+
def last_page = page("last")
|
15
|
+
|
16
|
+
def records = success? ? JSON(raw.body, symbolize_names: true) : []
|
17
|
+
|
18
|
+
def success? = raw.status.success?
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :raw
|
23
|
+
|
24
|
+
def page reference
|
25
|
+
links.find { |link| link.include? reference }
|
26
|
+
.then { |link| String(link)[/page=(?<page>\d+)/, :page].to_i }
|
27
|
+
end
|
28
|
+
|
29
|
+
def links = String(raw.headers["Link"]).split(", ")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module Integrations
|
5
|
+
module RubyGems
|
6
|
+
# Provides low-level API access to the RubyGems API.
|
7
|
+
class Client
|
8
|
+
def initialize container: Container
|
9
|
+
@container = container
|
10
|
+
end
|
11
|
+
|
12
|
+
def get endpoint
|
13
|
+
http.get("#{api_url}/#{endpoint}")
|
14
|
+
.then do |response|
|
15
|
+
response.status.success? ? JSON(response.body.to_s, symbolize_names: true) : {}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :container
|
22
|
+
|
23
|
+
def http = container[__method__]
|
24
|
+
|
25
|
+
def api_url = container[:configuration].ruby_gems_api_url
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/arrays"
|
4
|
+
|
5
|
+
module Pennyworth
|
6
|
+
module Loaders
|
7
|
+
# Loads an array of encoding records.
|
8
|
+
class Encodings
|
9
|
+
using Refinements::Arrays
|
10
|
+
|
11
|
+
def initialize encoding: Encoding, model: Models::Encoding
|
12
|
+
@encoding = encoding
|
13
|
+
@model = model
|
14
|
+
end
|
15
|
+
|
16
|
+
def call _omit = nil
|
17
|
+
encoding.aliases
|
18
|
+
.map { |_second, first| first }
|
19
|
+
.uniq
|
20
|
+
.sort
|
21
|
+
.map do |first|
|
22
|
+
model[name: first, aliases: encoding.find(first).names.sort.excluding(first)]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :encoding, :model
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
|
5
|
+
module Pennyworth
|
6
|
+
module Loaders
|
7
|
+
# Loads an array of HTTP status records.
|
8
|
+
class HTTPStatuses
|
9
|
+
def initialize codes: Rack::Utils::HTTP_STATUS_CODES,
|
10
|
+
model: Models::HTTPStatus,
|
11
|
+
configuration: CLI::Configuration::Loader.call
|
12
|
+
@codes = codes
|
13
|
+
@model = model
|
14
|
+
@configuration = configuration
|
15
|
+
end
|
16
|
+
|
17
|
+
def call _omit = nil
|
18
|
+
codes.map { |(code, label)| model[code: code, label: label, url: "#{url}/#{code}"] }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :codes, :model, :configuration
|
24
|
+
|
25
|
+
def url = configuration.http_statuses_url
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pennyworth
|
4
|
+
module Loaders
|
5
|
+
# Loads projects from API requests for further processing.
|
6
|
+
class Projects
|
7
|
+
def self.for_git_hub = new(client: Integrations::GitHub::Client.new)
|
8
|
+
|
9
|
+
def self.for_ruby_gems = new(client: Integrations::RubyGems::Client.new)
|
10
|
+
|
11
|
+
def initialize client:
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(endpoint) = client.get(endpoint)
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :client
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|