diff options
| author | William Boman <william@redwill.se> | 2023-09-11 00:37:05 +0200 |
|---|---|---|
| committer | William Boman <william@redwill.se> | 2025-02-16 09:48:59 +0100 |
| commit | d0119c18adff184c5c75f7ec59b6f12b301d268d (patch) | |
| tree | 3f196ef7cdde464392c0680edcd57fc6fe47825a /lua/mason-core | |
| parent | refactor!: remove old managers (#1497) (diff) | |
| download | mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.tar mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.tar.gz mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.tar.bz2 mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.tar.lz mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.tar.xz mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.tar.zst mason-d0119c18adff184c5c75f7ec59b6f12b301d268d.zip | |
refactor!: consolidate Lua registry sources and the Package API (#1498)
**This removes the following APIs:**
- `Package:check_new_version()`. Instead use the new `Package:get_latest_version()`.
**This has a breaking change in the following APIs:**
- `Package:get_installed_version()` now no longer takes a callback but instead returns the installed version or `nil` if
not installed.
<details>
<summary>To handle these breaking changes in plugins, leverage the `mason.version` module, for example:</summary>
```lua
local mason_version = require("mason.version")
local registry = require("mason-registry")
local pkg = registry.get_package("rust-analyzer")
if mason_version.MAJOR_VERSION < 2 then
-- before
pkg:check_new_version(function (success, new_version)
-- …
end)
pkg:get_installed_version(function (success, installed_version)
-- …
end)
else
-- after
local new_version = pkg:get_latest_version()
local installed_version = pkg:get_installed_version()
fi
```
</details>
---
<details>
<summary>This change also introduces breaking changes for Lua registry sources, by consolidating the package schema with the
registry.</summary>
The following is an example of a package defined in a Lua registry, following the new schema:
```lua
local Pkg = require("mason-core.package")
return Pkg.new {
schema = "registry+v1",
name = "ripgrep",
description = "ripgrep recursively searches directories for a regex pattern while respecting your gitignore.",
homepage = "https://github.com/BurntSushi/ripgrep",
licenses = { Pkg.License.MIT },
languages = {},
categories = {},
source = {
id = "pkg:mason/ripgrep@13.0.0",
---@param ctx InstallContext
---@param purl Purl
install = function(ctx, purl)
-- Arbitrary installation code.
end,
},
bin = {
rg = "./bin/rg",
},
}
```
</details>
Diffstat (limited to 'lua/mason-core')
| -rw-r--r-- | lua/mason-core/installer/context.lua | 2 | ||||
| -rw-r--r-- | lua/mason-core/installer/init.lua | 8 | ||||
| -rw-r--r-- | lua/mason-core/installer/registry/init.lua | 17 | ||||
| -rw-r--r-- | lua/mason-core/installer/registry/providers/mason.lua | 43 | ||||
| -rw-r--r-- | lua/mason-core/package/init.lua | 142 | ||||
| -rw-r--r-- | lua/mason-core/package/version-check.lua | 80 |
6 files changed, 99 insertions, 193 deletions
diff --git a/lua/mason-core/installer/context.lua b/lua/mason-core/installer/context.lua index 21c9c26f..a991cd9f 100644 --- a/lua/mason-core/installer/context.lua +++ b/lua/mason-core/installer/context.lua @@ -202,7 +202,7 @@ function InstallContext.new(handle, opts) local cwd_manager = CwdManager.new(path.install_prefix()) return setmetatable({ cwd = cwd_manager, - spawn = ContextualSpawn.new(cwd_manager, handle, not handle.package:is_registry_spec()), + spawn = ContextualSpawn.new(cwd_manager, handle, false), handle = handle, package = handle.package, -- for convenience fs = ContextualFs.new(cwd_manager), diff --git a/lua/mason-core/installer/init.lua b/lua/mason-core/installer/init.lua index 994ab847..961c5c47 100644 --- a/lua/mason-core/installer/init.lua +++ b/lua/mason-core/installer/init.lua @@ -74,6 +74,7 @@ end ---@async ---@param context InstallContext function M.prepare_installer(context) + local installer = require "mason-core.installer.registry" return Result.try(function(try) local package_build_prefix = path.package_build_prefix(context.package.name) if fs.async.dir_exists(package_build_prefix) then @@ -82,12 +83,7 @@ function M.prepare_installer(context) try(Result.pcall(fs.async.mkdirp, package_build_prefix)) context.cwd:set(package_build_prefix) - if context.package:is_registry_spec() then - local registry_installer = require "mason-core.installer.registry" - return try(registry_installer.compile(context.handle.package.spec, context.opts)) - else - return context.package.spec.install - end + return try(installer.compile(context.handle.package.spec, context.opts)) end) end diff --git a/lua/mason-core/installer/registry/init.lua b/lua/mason-core/installer/registry/init.lua index e7c796e9..961e94ea 100644 --- a/lua/mason-core/installer/registry/init.lua +++ b/lua/mason-core/installer/registry/init.lua @@ -35,9 +35,10 @@ M.register_provider("nuget", _.lazy_require "mason-core.installer.registry.provi M.register_provider("opam", _.lazy_require "mason-core.installer.registry.providers.opam") M.register_provider("openvsx", _.lazy_require "mason-core.installer.registry.providers.openvsx") M.register_provider("pypi", _.lazy_require "mason-core.installer.registry.providers.pypi") +M.register_provider("mason", _.lazy_require "mason-core.installer.registry.providers.mason") ---@param purl Purl -local function get_provider(purl) +function M.get_provider(purl) return Optional.of_nilable(PROVIDERS[purl.type]):ok_or(("Unknown purl type: %s"):format(purl.type)) end @@ -127,7 +128,7 @@ function M.parse(spec, opts) end ---@type InstallerProvider - local provider = try(get_provider(purl)) + local provider = try(M.get_provider(purl)) log.trace("Found provider for purl.", source.id) local parsed_source = try(provider.parse(source, purl, opts)) log.trace("Parsed source for purl.", source.id, parsed_source) @@ -213,16 +214,4 @@ function M.compile(spec, opts) end) end ----@async ----@param spec RegistryPackageSpec -function M.get_versions(spec) - return Result.try(function(try) - ---@type Purl - local purl = try(Purl.parse(spec.source.id)) - ---@type InstallerProvider - local provider = try(get_provider(purl)) - return provider.get_versions(purl, spec.source) - end) -end - return M diff --git a/lua/mason-core/installer/registry/providers/mason.lua b/lua/mason-core/installer/registry/providers/mason.lua new file mode 100644 index 00000000..3490ebaa --- /dev/null +++ b/lua/mason-core/installer/registry/providers/mason.lua @@ -0,0 +1,43 @@ +local Result = require "mason-core.result" +local _ = require "mason-core.functional" + +local M = {} + +---@param source RegistryPackageSource +---@param purl Purl +function M.parse(source, purl) + if type(source.install) ~= "function" and type((getmetatable(source.install) or {}).__call) ~= "function" then + return Result.failure "source.install is not a function." + end + + ---@class ParsedMasonSource : ParsedPackageSource + local parsed_source = { + purl = purl, + ---@type async fun(ctx: InstallContext, purl: Purl) + install = source.install, + } + + return Result.success(parsed_source) +end + +---@async +---@param ctx InstallContext +---@param source ParsedMasonSource +function M.install(ctx, source) + ctx.spawn.strict_mode = true + return Result.pcall(source.install, ctx, source.purl) + :on_success(function() + ctx.spawn.strict_mode = false + end) + :on_failure(function() + ctx.spawn.strict_mode = false + end) +end + +---@async +---@param purl Purl +function M.get_versions(purl) + return Result.failure "Unimplemented." +end + +return M diff --git a/lua/mason-core/package/init.lua b/lua/mason-core/package/init.lua index 899370fa..4a7f3d70 100644 --- a/lua/mason-core/package/init.lua +++ b/lua/mason-core/package/init.lua @@ -10,13 +10,9 @@ local path = require "mason-core.path" local platform = require "mason-core.platform" local registry = require "mason-registry" -local is_not_nil = _.complement(_.is_nil) -local is_registry_schema_id = _.matches "^registry%+v[1-9]+$" -local is_registry_spec = _.prop_satisfies(_.all_pass { is_not_nil, is_registry_schema_id }, "schema") - ---@class Package : EventEmitter ---@field name string ----@field spec RegistryPackageSpec | PackageSpec +---@field spec RegistryPackageSpec ---@field private handle InstallHandle The currently associated handle. local Package = setmetatable({}, { __index = EventEmitter }) @@ -47,15 +43,17 @@ Package.Cat = { Formatter = "Formatter", } -local PackageMt = { __index = Package } +---@alias PackageLicense string ----@class PackageSpec ----@field name string ----@field desc string ----@field homepage string ----@field categories PackageCategory[] ----@field languages PackageLanguage[] ----@field install async fun(ctx: InstallContext) +---@type table<PackageLicense, PackageLicense> +Package.License = setmetatable({}, { + __index = function(s, license) + s[license] = license + return s[license] + end, +}) + +local PackageMt = { __index = Package } ---@class RegistryPackageSourceVersionOverride : RegistryPackageSource ---@field constraint string @@ -86,32 +84,24 @@ local PackageMt = { __index = Package } ---@field share table<string, string>? ---@field opt table<string, string>? ----@param spec PackageSpec | RegistryPackageSpec +---@param spec RegistryPackageSpec local function validate_spec(spec) if platform.cached_features["nvim-0.11"] ~= 1 then return end - if is_registry_spec(spec) then - vim.validate("name", spec.name, "string") - vim.validate("description", spec.description, "string") - vim.validate("homepage", spec.homepage, "string") - vim.validate("licenses", spec.licenses, "table") - vim.validate("categories", spec.categories, "table") - vim.validate("languages", spec.languages, "table") - vim.validate("source", spec.source, "table") - vim.validate("bin", spec.bin, { "table", "nil" }) - vim.validate("share", spec.share, { "table", "nil" }) - else - vim.validate("name", spec.name, "string") - vim.validate("desc", spec.desc, "string") - vim.validate("homepage", spec.homepage, "string") - vim.validate("categories", spec.categories, "table") - vim.validate("languages", spec.languages, "table") - vim.validate("install", spec.install, "function") - end + vim.validate("schema", spec.schema, _.equals "registry+v1", "registry+v1") + vim.validate("name", spec.name, "string") + vim.validate("description", spec.description, "string") + vim.validate("homepage", spec.homepage, "string") + vim.validate("licenses", spec.licenses, "table") + vim.validate("categories", spec.categories, "table") + vim.validate("languages", spec.languages, "table") + vim.validate("source", spec.source, "table") + vim.validate("bin", spec.bin, { "table", "nil" }) + vim.validate("share", spec.share, { "table", "nil" }) end ----@param spec PackageSpec | RegistryPackageSpec +---@param spec RegistryPackageSpec function Package.new(spec) validate_spec(spec) return EventEmitter.init(setmetatable({ @@ -241,67 +231,40 @@ function Package:get_receipt() return Optional.empty() end ----@param callback fun(success: boolean, version_or_err: string) -function Package:get_installed_version(callback) - self:get_receipt() - :if_present( +---@return string? +function Package:get_installed_version() + return self:get_receipt() + :and_then( ---@param receipt InstallReceipt function(receipt) - if is_registry_schema_id(receipt.primary_source.type) then - local resolve = _.curryN(callback, 2) - Purl.parse(receipt.primary_source.id) - :map(_.prop "version") - :on_success(resolve(true)) - :on_failure(resolve(false)) - else - a.run(function() - local version_checks = require "mason-core.package.version-check" - return version_checks.get_installed_version(receipt, self:get_install_path()):get_or_throw() - end, callback) - end + return Purl.parse(receipt.primary_source.id):map(_.prop "version"):ok() end ) - :if_not_present(function() - callback(false, "Unable to get receipt.") - end) + :or_else(nil) end ----@param callback fun(success: boolean, result_or_err: NewPackageVersion) -function Package:check_new_version(callback) - if self:is_registry_spec() then - self:get_installed_version(_.scheduler_wrap(function(success, installed_version) - if not success then - return callback(false, installed_version) - end - local resolve = _.curryN(callback, 2) - Result.try(function(try) - -- This is a bit goofy, but it's done to verify that a new version is supported by the - -- current platform (parse fails if it's not). We don't want to surface new versions that - -- are unsupported. - try(require("mason-core.installer.registry").parse(self.spec, {})) +---@return string +function Package:get_latest_version() + return Purl.parse(self.spec.source.id) + :map(_.prop "version") + :get_or_throw(("Unable to retrieve version from malformed purl: %s."):format(self.spec.source.id)) +end - ---@type Purl - local purl = try(Purl.parse(self.spec.source.id)) - if purl.version and installed_version ~= purl.version then - return { - name = purl.name, - current_version = installed_version, - latest_version = purl.version, - } - else - return Result.failure "Package is not outdated." - end - end) - :on_success(resolve(true)) - :on_failure(resolve(false)) - end)) - else - a.run(function() - local receipt = self:get_receipt():or_else_throw "Unable to get receipt." - local version_checks = require "mason-core.package.version-check" - return version_checks.get_new_version(receipt, self:get_install_path()):get_or_throw() - end, callback) - end +---@param opts? PackageInstallOpts +function Package:is_installable(opts) + return require("mason-core.installer.registry").parse(self.spec, opts or {}):is_success() +end + +---@return Result # Result<string[]> +function Package:get_all_versions() + local registry_installer = require "mason-core.installer.registry" + return Result.try(function(try) + ---@type Purl + local purl = try(Purl.parse(self.spec.source.id)) + ---@type InstallerProvider + local provider = try(registry_installer.get_provider(purl)) + return provider.get_versions(purl, self.spec.source) + end) end function Package:get_lsp_settings_schema() @@ -314,11 +277,6 @@ function Package:get_lsp_settings_schema() return Optional.empty() end ----@return boolean -function Package:is_registry_spec() - return is_registry_spec(self.spec) -end - function PackageMt.__tostring(self) return ("Package(name=%s)"):format(self.name) end diff --git a/lua/mason-core/package/version-check.lua b/lua/mason-core/package/version-check.lua deleted file mode 100644 index 66d9ad13..00000000 --- a/lua/mason-core/package/version-check.lua +++ /dev/null @@ -1,80 +0,0 @@ -local Result = require "mason-core.result" -local cargo = require "mason-core.managers.cargo" -local composer = require "mason-core.managers.composer" -local gem = require "mason-core.managers.gem" -local git = require "mason-core.managers.git" -local github = require "mason-core.managers.github" -local go = require "mason-core.managers.go" -local log = require "mason-core.log" -local luarocks = require "mason-core.managers.luarocks" -local npm = require "mason-core.managers.npm" -local pip3 = require "mason-core.managers.pip3" - ----@param field_name string -local function version_in_receipt(field_name) - ---@param receipt InstallReceipt - ---@return Result - return function(receipt) - return Result.success(receipt.primary_source[field_name]) - end -end - ----@type table<InstallReceiptSourceType, async fun(receipt: InstallReceipt, install_dir: string): Result> -local get_installed_version_by_type = { - ["npm"] = npm.get_installed_primary_package_version, - ["pip3"] = pip3.get_installed_primary_package_version, - ["gem"] = gem.get_installed_primary_package_version, - ["cargo"] = cargo.get_installed_primary_package_version, - ["composer"] = composer.get_installed_primary_package_version, - ["git"] = git.get_installed_revision, - ["go"] = go.get_installed_primary_package_version, - ["luarocks"] = luarocks.get_installed_primary_package_version, - ["github_release_file"] = version_in_receipt "release", - ["github_release"] = version_in_receipt "release", - ["github_tag"] = version_in_receipt "tag", -} - ----@class NewPackageVersion ----@field name string ----@field current_version string ----@field latest_version string - -local get_new_version_by_type = { - ["npm"] = npm.check_outdated_primary_package, - ["pip3"] = pip3.check_outdated_primary_package, - ["git"] = git.check_outdated_git_clone, - ["cargo"] = cargo.check_outdated_primary_package, - ["composer"] = composer.check_outdated_primary_package, - ["gem"] = gem.check_outdated_primary_package, - ["go"] = go.check_outdated_primary_package, - ["luarocks"] = luarocks.check_outdated_primary_package, - ["github_release_file"] = github.check_outdated_primary_package_release, - ["github_release"] = github.check_outdated_primary_package_release, - ["github_tag"] = github.check_outdated_primary_package_tag, -} - ----@param provider_mapping table<string, async fun(receipt: InstallReceipt, install_dir: string): Result> -local function version_check(provider_mapping) - ---@param receipt InstallReceipt - ---@param install_dir string - return function(receipt, install_dir) - local check = provider_mapping[receipt.primary_source.type] - if not check then - return Result.failure( - ("Packages installed via %s does not yet support version check."):format(receipt.primary_source.type) - ) - end - return check(receipt, install_dir) - :on_success(function(version) - log.debug("Version check", version) - end) - :on_failure(function(failure) - log.debug("Version check failed", tostring(failure)) - end) - end -end - -return { - get_installed_version = version_check(get_installed_version_by_type), - get_new_version = version_check(get_new_version_by_type), -} |
