
This commit fixes an issue where we added a new global artifact and then updated a cask to make use of that new artifact. This caused a number of `brew cask` commands to fail for users who had the cask installed before the artifact was added. When loading the definition of an installed cask, we configure it using a snapshot from install time, e. g. `/usr/local/Caskroom/markdownmdimporter/.metadata/config.json`. The snapshot looks like this: ``` { "default": { "appdir": "/Applications", "prefpanedir": "/Users/claudia/Library/PreferencePanes", "qlplugindir": "/Users/claudia/Library/QuickLook", "dictionarydir": "/Users/claudia/Library/Dictionaries", "fontdir": "/Users/claudia/Library/Fonts", "colorpickerdir": "/Users/claudia/Library/ColorPickers", "servicedir": "/Users/claudia/Library/Services", "input_methoddir": "/Users/claudia/Library/Input Methods", "internet_plugindir": "/Users/claudia/Library/Internet Plug-Ins", "audio_unit_plugindir": "/Users/claudia/Library/Audio/Plug-Ins/Components", "vst_plugindir": "/Users/claudia/Library/Audio/Plug-Ins/VST", "vst3_plugindir": "/Users/claudia/Library/Audio/Plug-Ins/VST3", "screen_saverdir": "/Users/claudia/Library/Screen Savers" }, "env": {}, "explicit": {} } ``` Note that there is no `mdimporterdir` because the cask was installed before the artifact was added. The root cause is that the cask loading code still expects the snapshot to contain directory configuration for all artifact types. Since the snapshot never learned about the new artifact type, cask loading would fail. The fix applied in this commit is to fall back to the global default whenever the `default` directory map of a configuration snapshot is incomplete. See also: - https://github.com/Homebrew/brew/pull/7286#issuecomment-613376568 - https://discourse.brew.sh/t/cask-definition-is-invalid-invalid-mdimporter-stanza-key-not-found-mdimporterdir
124 lines
3.1 KiB
Ruby
124 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "json"
|
|
|
|
require "extend/hash_validator"
|
|
using HashValidator
|
|
|
|
module Cask
|
|
class Config
|
|
DEFAULT_DIRS = {
|
|
appdir: "/Applications",
|
|
prefpanedir: "~/Library/PreferencePanes",
|
|
qlplugindir: "~/Library/QuickLook",
|
|
mdimporterdir: "~/Library/Spotlight",
|
|
dictionarydir: "~/Library/Dictionaries",
|
|
fontdir: "~/Library/Fonts",
|
|
colorpickerdir: "~/Library/ColorPickers",
|
|
servicedir: "~/Library/Services",
|
|
input_methoddir: "~/Library/Input Methods",
|
|
internet_plugindir: "~/Library/Internet Plug-Ins",
|
|
audio_unit_plugindir: "~/Library/Audio/Plug-Ins/Components",
|
|
vst_plugindir: "~/Library/Audio/Plug-Ins/VST",
|
|
vst3_plugindir: "~/Library/Audio/Plug-Ins/VST3",
|
|
screen_saverdir: "~/Library/Screen Savers",
|
|
}.freeze
|
|
|
|
def self.global
|
|
@global ||= new
|
|
end
|
|
|
|
def self.clear
|
|
@global = nil
|
|
end
|
|
|
|
def self.for_cask(cask)
|
|
if cask.config_path.exist?
|
|
from_file(cask.config_path)
|
|
else
|
|
global
|
|
end
|
|
end
|
|
|
|
def self.from_file(path)
|
|
config = begin
|
|
JSON.parse(File.read(path))
|
|
rescue JSON::ParserError => e
|
|
raise e, "Cannot parse #{path}: #{e}", e.backtrace
|
|
end
|
|
|
|
new(
|
|
default: config.fetch("default", {}),
|
|
env: config.fetch("env", {}),
|
|
explicit: config.fetch("explicit", {}),
|
|
)
|
|
end
|
|
|
|
def self.canonicalize(config)
|
|
config.map do |k, v|
|
|
key = k.to_sym
|
|
|
|
if DEFAULT_DIRS.key?(key)
|
|
[key, Pathname(v).expand_path]
|
|
else
|
|
[key, v]
|
|
end
|
|
end.to_h
|
|
end
|
|
|
|
attr_accessor :explicit
|
|
|
|
def initialize(default: nil, env: nil, explicit: {})
|
|
@default = DEFAULT_DIRS.merge(self.class.canonicalize(default)) if default
|
|
@env = self.class.canonicalize(env) if env
|
|
@explicit = self.class.canonicalize(explicit)
|
|
|
|
@env&.assert_valid_keys!(*DEFAULT_DIRS.keys)
|
|
@explicit.assert_valid_keys!(*DEFAULT_DIRS.keys)
|
|
end
|
|
|
|
def default
|
|
@default ||= self.class.canonicalize(DEFAULT_DIRS)
|
|
end
|
|
|
|
def env
|
|
@env ||= self.class.canonicalize(
|
|
Shellwords.shellsplit(ENV.fetch("HOMEBREW_CASK_OPTS", ""))
|
|
.select { |arg| arg.include?("=") }
|
|
.map { |arg| arg.split("=", 2) }
|
|
.map { |(flag, value)| [flag.sub(/^\-\-/, ""), value] },
|
|
)
|
|
end
|
|
|
|
def binarydir
|
|
@binarydir ||= HOMEBREW_PREFIX/"bin"
|
|
end
|
|
|
|
def manpagedir
|
|
@manpagedir ||= HOMEBREW_PREFIX/"share/man"
|
|
end
|
|
|
|
DEFAULT_DIRS.each_key do |dir|
|
|
define_method(dir) do
|
|
explicit.fetch(dir, env.fetch(dir, default.fetch(dir)))
|
|
end
|
|
|
|
define_method(:"#{dir}=") do |path|
|
|
explicit[dir] = Pathname(path).expand_path
|
|
end
|
|
end
|
|
|
|
def merge(other)
|
|
self.class.new(explicit: other.explicit.merge(explicit))
|
|
end
|
|
|
|
def to_json(*args)
|
|
{
|
|
default: default,
|
|
env: env,
|
|
explicit: explicit,
|
|
}.to_json(*args)
|
|
end
|
|
end
|
|
end
|