Class: Canopus::Plugins::Registry
- Inherits:
-
Object
- Object
- Canopus::Plugins::Registry
- Defined in:
- lib/canopus/plugins/registry.rb,
sig/canopus.rbs
Constant Summary collapse
- API_VERSION =
"1"- NAME_PATTERN =
/\Acanopus-plugin-[a-z0-9][a-z0-9-]*\z/- PERMISSIONS =
Plugins::PERMISSIONS
Instance Method Summary collapse
- #close ⇒ void
- #disable(name) ⇒ true
- #disabled?(name) ⇒ Boolean
- #enable(name) ⇒ true
-
#initialize(workspace, state_path: nil) ⇒ Registry
constructor
A new instance of Registry.
- #install(name, version: nil) ⇒ Object
- #load(path, trusted: false, permissions: [], isolated: true, timeout: 2) ⇒ Object
- #load_gem(name, trusted: false, permissions: [], isolated: true, timeout: 2) ⇒ Object
- #search(query, limit: 20) ⇒ Array[Hash[Symbol, untyped]]
- #update(name) ⇒ Object
Constructor Details
#initialize(workspace, state_path: nil) ⇒ Registry
Returns a new instance of Registry.
14 15 16 17 18 |
# File 'lib/canopus/plugins/registry.rb', line 14 def initialize(workspace, state_path: nil) @workspace, @loaded = workspace, [] @state_path = state_path || File.join(ENV["XDG_STATE_HOME"] || File.("~/.local/state"), "canopus", "plugins.json") @disabled = load_state end |
Instance Method Details
#close ⇒ void
This method returns an undefined value.
97 |
# File 'lib/canopus/plugins/registry.rb', line 97 def close = @loaded.each(&:close) |
#disable(name) ⇒ true
52 53 54 55 56 57 |
# File 'lib/canopus/plugins/registry.rb', line 52 def disable(name) name = validate_name(name) @disabled[name] = true save_state true end |
#disabled?(name) ⇒ Boolean
66 |
# File 'lib/canopus/plugins/registry.rb', line 66 def disabled?(name) = @disabled.key?(validate_name(name)) |
#enable(name) ⇒ true
59 60 61 62 63 64 |
# File 'lib/canopus/plugins/registry.rb', line 59 def enable(name) name = validate_name(name) @disabled.delete(name) save_state true end |
#install(name, version: nil) ⇒ Object
37 38 39 40 41 42 43 44 45 46 |
# File 'lib/canopus/plugins/registry.rb', line 37 def install(name, version: nil) require "rubygems/dependency_installer" name = validate_name(name) specifications = Gem::DependencyInstaller.new.install(name, version || Gem::Requirement.default) specification = specifications.reverse.find { |candidate| candidate.name == name } validate_specification!(specification) specification rescue Gem::Exception => error raise Canopus::Error, "plugin install failed: #{error.}" end |
#load(path, trusted: false, permissions: [], isolated: true, timeout: 2) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/canopus/plugins/registry.rb', line 82 def load(path, trusted: false, permissions: [], isolated: true, timeout: 2) raise PermissionDenied, "plugins execute Ruby code; explicitly mark this plugin trusted" unless trusted = .map { || .to_s == "process" ? "exec" : .to_s }.uniq raise PermissionDenied, "unknown plugin permission" unless ( - PERMISSIONS).empty? source = File.read(path, 256 * 1024 + 1, encoding: "UTF-8") raise Error, "plugin source exceeds 256KB" if source.bytesize > 256 * 1024 plugin = if isolated IsolatedRuntime.new(@workspace, source, path, , timeout: timeout) else LocalRuntime.new(@workspace, source, path, ) end @loaded << plugin plugin end |
#load_gem(name, trusted: false, permissions: [], isolated: true, timeout: 2) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/canopus/plugins/registry.rb', line 68 def load_gem(name, trusted: false, permissions: [], isolated: true, timeout: 2) name = validate_name(name) raise Canopus::Error, "plugin is disabled: #{name}" if disabled?(name) specification = validate_specification!(Gem::Specification.find_all_by_name(name).max_by(&:version)) entrypoint = specification.["canopus_plugin_entrypoint"] || "lib/#{name}.rb" path = File.realpath(File.(entrypoint, specification.full_gem_path)) prefix = File.realpath(specification.full_gem_path) + File::SEPARATOR raise Canopus::Error, "plugin entrypoint escapes gem directory" unless path.start_with?(prefix) load(path, trusted: trusted, permissions: , isolated: isolated, timeout: timeout) rescue Errno::ENOENT raise Canopus::Error, "plugin entrypoint not found: #{name}" end |
#search(query, limit: 20) ⇒ Array[Hash[Symbol, untyped]]
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/canopus/plugins/registry.rb', line 20 def search(query, limit: 20) require "rubygems/spec_fetcher" query = String(query) raise ArgumentError, "plugin search query must not be empty" if query.strip.empty? raise ArgumentError, "invalid plugin search limit" unless limit.is_a?(Integer) && limit.between?(1, 100) dependency = Gem::Dependency.new(query.start_with?("canopus-plugin-") ? query : "canopus-plugin-#{query}", Gem::Requirement.default) Gem::SpecFetcher.fetcher.search_for_dependency(dependency).first.filter_map do |entry, _source| spec = entry.is_a?(Array) ? entry.first : entry spec = spec.spec if spec.respond_to?(:spec) name = spec.respond_to?(:name) ? spec.name : nil next unless plugin_name?(name) {name: name, version: spec.respond_to?(:version) ? spec.version.to_s : "", summary: spec.respond_to?(:summary) ? spec.summary.to_s : "", api_version: spec.respond_to?(:metadata) ? spec.["canopus_plugin_api_version"] : nil} end.uniq { |plugin| plugin[:name] }.first(limit) end |
#update(name) ⇒ Object
48 49 50 |
# File 'lib/canopus/plugins/registry.rb', line 48 def update(name) install(name) end |