aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lua/mason-core/installer/context.lua2
-rw-r--r--lua/mason-core/installer/managers/build.lua48
-rw-r--r--lua/mason-core/installer/managers/common.lua126
-rw-r--r--lua/mason-core/installer/registry/init.lua1
-rw-r--r--lua/mason-core/installer/registry/providers/generic/build.lua6
-rw-r--r--lua/mason-core/installer/registry/providers/generic/download.lua21
-rw-r--r--lua/mason-core/installer/registry/providers/github/build.lua6
-rw-r--r--lua/mason-core/installer/registry/providers/github/release.lua88
-rw-r--r--lua/mason-core/installer/registry/providers/npm.lua1
-rw-r--r--lua/mason-core/installer/registry/providers/openvsx.lua63
-rw-r--r--lua/mason-core/installer/registry/util.lua6
-rw-r--r--lua/mason-core/providers/init.lua5
-rw-r--r--lua/mason-core/semver.lua2
-rw-r--r--lua/mason-registry/api.lua9
-rw-r--r--lua/mason/providers/client/init.lua1
-rw-r--r--lua/mason/providers/client/openvsx.lua26
-rw-r--r--lua/mason/providers/registry-api/init.lua8
-rw-r--r--tests/mason-core/installer/managers/build_spec.lua6
-rw-r--r--tests/mason-core/installer/managers/common_spec.lua161
-rw-r--r--tests/mason-core/installer/registry/providers/generic/build_spec.lua8
-rw-r--r--tests/mason-core/installer/registry/providers/generic/download_spec.lua43
-rw-r--r--tests/mason-core/installer/registry/providers/github/build_spec.lua40
-rw-r--r--tests/mason-core/installer/registry/providers/github/release_spec.lua71
-rw-r--r--tests/mason-core/installer/registry/providers/openvsx_spec.lua149
-rw-r--r--tests/mason-core/installer/registry/util_spec.lua4
25 files changed, 637 insertions, 264 deletions
diff --git a/lua/mason-core/installer/context.lua b/lua/mason-core/installer/context.lua
index b12b540f..7637209f 100644
--- a/lua/mason-core/installer/context.lua
+++ b/lua/mason-core/installer/context.lua
@@ -241,7 +241,7 @@ function InstallContext:promote_cwd()
end
---@param rel_path string The relative path from the current working directory to change cwd to. Will only restore to the initial cwd after execution of fn (if provided).
----@param fn async (fun())? The function to run in the context of the given path.
+---@param fn async (fun(): any)? The function to run in the context of the given path.
function InstallContext:chdir(rel_path, fn)
local old_cwd = self.cwd:get()
self.cwd:set(path.concat { old_cwd, rel_path })
diff --git a/lua/mason-core/installer/managers/build.lua b/lua/mason-core/installer/managers/build.lua
deleted file mode 100644
index a1549a28..00000000
--- a/lua/mason-core/installer/managers/build.lua
+++ /dev/null
@@ -1,48 +0,0 @@
-local _ = require "mason-core.functional"
-local a = require "mason-core.async"
-local async_uv = require "mason-core.async.uv"
-local installer = require "mason-core.installer"
-local log = require "mason-core.log"
-local platform = require "mason-core.platform"
-local powershell = require "mason-core.managers.powershell"
-
-local M = {}
-
----@class BuildInstruction
----@field target? Platform | Platform[]
----@field run string
----@field staged? boolean
----@field env? table<string, string>
-
----@async
----@param build BuildInstruction
----@return Result
----@nodiscard
-function M.run(build)
- log.fmt_debug("build: run %s", build)
- local ctx = installer.context()
- if build.staged == false then
- ctx:promote_cwd()
- end
- return platform.when {
- unix = function()
- return ctx.spawn.bash {
- on_spawn = a.scope(function(_, stdio)
- local stdin = stdio[1]
- async_uv.write(stdin, "set -euxo pipefail;\n")
- async_uv.write(stdin, build.run)
- async_uv.shutdown(stdin)
- async_uv.close(stdin)
- end),
- env = build.env,
- }
- end,
- win = function()
- return powershell.command(build.run, {
- env = build.env,
- }, ctx.spawn)
- end,
- }
-end
-
-return M
diff --git a/lua/mason-core/installer/managers/common.lua b/lua/mason-core/installer/managers/common.lua
new file mode 100644
index 00000000..c730a3aa
--- /dev/null
+++ b/lua/mason-core/installer/managers/common.lua
@@ -0,0 +1,126 @@
+local Result = require "mason-core.result"
+local _ = require "mason-core.functional"
+local a = require "mason-core.async"
+local async_uv = require "mason-core.async.uv"
+local installer = require "mason-core.installer"
+local log = require "mason-core.log"
+local platform = require "mason-core.platform"
+local powershell = require "mason-core.managers.powershell"
+local std = require "mason-core.installer.managers.std"
+
+local M = {}
+
+---@class DownloadItem
+---@field download_url string
+---@field out_file string
+
+---@class FileDownloadSpec
+---@field file string | string[]
+
+local get_source_file = _.compose(_.head, _.split ":")
+local get_outfile = _.compose(_.last, _.split ":")
+
+---Normalizes file paths from e.g. "file:out-dir/" to "out-dir/file".
+---@param file string
+local function normalize_file_path(file)
+ local source_file = get_source_file(file)
+ local new_path = get_outfile(file)
+
+ -- a dir expression (e.g. "libexec/")
+ if _.matches("/$", new_path) then
+ return new_path .. source_file
+ end
+ return new_path
+end
+
+---@generic T : FileDownloadSpec
+---@type fun(download: T): T
+M.normalize_files = _.evolve {
+ file = _.cond {
+ { _.is "string", normalize_file_path },
+ { _.T, _.map(normalize_file_path) },
+ },
+}
+
+---@param download FileDownloadSpec
+---@param url_generator fun(file: string): string
+---@return DownloadItem[]
+function M.parse_downloads(download, url_generator)
+ local files = download.file
+ if type(files) == "string" then
+ files = { files }
+ end
+
+ return _.map(function(file)
+ local source_file = get_source_file(file)
+ local out_file = normalize_file_path(file)
+ return {
+ download_url = url_generator(source_file),
+ out_file = out_file,
+ }
+ end, files)
+end
+
+---@async
+---@param ctx InstallContext
+---@param downloads DownloadItem[]
+---@nodiscard
+function M.download_files(ctx, downloads)
+ return Result.try(function(try)
+ for __, download in ipairs(downloads) do
+ a.scheduler()
+ local out_dir = vim.fn.fnamemodify(download.out_file, ":h")
+ local out_file = vim.fn.fnamemodify(download.out_file, ":t")
+ if out_dir ~= "." then
+ try(Result.pcall(function()
+ ctx.fs:mkdirp(out_dir)
+ end))
+ end
+ try(ctx:chdir(out_dir, function()
+ return Result.try(function(try)
+ try(std.download_file(download.download_url, out_file))
+ try(std.unpack(out_file))
+ end)
+ end))
+ end
+ end)
+end
+
+---@class BuildInstruction
+---@field target? Platform | Platform[]
+---@field run string
+---@field staged? boolean
+---@field env? table<string, string>
+
+---@async
+---@param build BuildInstruction
+---@return Result
+---@nodiscard
+function M.run_build_instruction(build)
+ log.fmt_debug("build: run %s", build)
+ local ctx = installer.context()
+ if build.staged == false then
+ ctx:promote_cwd()
+ end
+ return platform.when {
+ unix = function()
+ return ctx.spawn.bash {
+ on_spawn = a.scope(function(_, stdio)
+ local stdin = stdio[1]
+ async_uv.write(stdin, "set -euxo pipefail;\n")
+ async_uv.write(stdin, build.run)
+ async_uv.shutdown(stdin)
+ async_uv.close(stdin)
+ end),
+ env = build.env,
+ }
+ end,
+ win = function()
+ return powershell.command(build.run, {
+ env = build.env,
+ }, ctx.spawn)
+ end,
+ }
+end
+
+return M
diff --git a/lua/mason-core/installer/registry/init.lua b/lua/mason-core/installer/registry/init.lua
index 0cec2161..e97a8430 100644
--- a/lua/mason-core/installer/registry/init.lua
+++ b/lua/mason-core/installer/registry/init.lua
@@ -33,6 +33,7 @@ M.register_provider("luarocks", _.lazy_require "mason-core.installer.registry.pr
M.register_provider("npm", _.lazy_require "mason-core.installer.registry.providers.npm")
M.register_provider("nuget", _.lazy_require "mason-core.installer.registry.providers.nuget")
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")
---@param purl Purl
diff --git a/lua/mason-core/installer/registry/providers/generic/build.lua b/lua/mason-core/installer/registry/providers/generic/build.lua
index 6d2769e1..a0d517d8 100644
--- a/lua/mason-core/installer/registry/providers/generic/build.lua
+++ b/lua/mason-core/installer/registry/providers/generic/build.lua
@@ -1,6 +1,6 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
-local build = require "mason-core.installer.managers.build"
+local common = require "mason-core.installer.managers.common"
local expr = require "mason-core.installer.registry.expr"
local util = require "mason-core.installer.registry.util"
@@ -15,7 +15,7 @@ local M = {}
function M.parse(source, purl, opts)
return Result.try(function(try)
---@type BuildInstruction
- local build_instruction = try(util.coalesce_by_target(source.build, opts):ok_or "PLATFORM_UNSUPPORTED")
+ local build_instruction = try(util.coalesce_by_target(source.build, opts))
if build_instruction.env then
local expr_ctx = { version = purl.version, target = build_instruction.target }
@@ -34,7 +34,7 @@ end
---@param ctx InstallContext
---@param source ParsedGenericBuildSource
function M.install(ctx, source)
- return build.run(source.build)
+ return common.run_build_instruction(source.build)
end
return M
diff --git a/lua/mason-core/installer/registry/providers/generic/download.lua b/lua/mason-core/installer/registry/providers/generic/download.lua
index a7e4d046..4622a844 100644
--- a/lua/mason-core/installer/registry/providers/generic/download.lua
+++ b/lua/mason-core/installer/registry/providers/generic/download.lua
@@ -1,5 +1,6 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
+local common = require "mason-core.installer.managers.common"
local expr = require "mason-core.installer.registry.expr"
local util = require "mason-core.installer.registry.util"
@@ -17,15 +18,25 @@ local M = {}
---@param opts PackageInstallOpts
function M.parse(source, purl, opts)
return Result.try(function(try)
- local download = try(util.coalesce_by_target(source.download, opts):ok_or "PLATFORM_UNSUPPORTED")
+ local download = try(util.coalesce_by_target(source.download, opts))
local expr_ctx = { version = purl.version }
---@type { files: table<string, string> }
local interpolated_download = try(expr.tbl_interpolate(download, expr_ctx))
+ ---@type DownloadItem[]
+ local downloads = _.map(function(pair)
+ ---@type DownloadItem
+ return {
+ out_file = pair[1],
+ download_url = pair[2],
+ }
+ end, _.to_pairs(interpolated_download.files))
+
---@class ParsedGenericDownloadSource : ParsedPackageSource
local parsed_source = {
download = interpolated_download,
+ downloads = downloads,
}
return parsed_source
end)
@@ -35,13 +46,7 @@ end
---@param ctx InstallContext
---@param source ParsedGenericDownloadSource
function M.install(ctx, source)
- local std = require "mason-core.installer.managers.std"
- return Result.try(function(try)
- for out_file, url in pairs(source.download.files) do
- try(std.download_file(url, out_file))
- try(std.unpack(out_file))
- end
- end)
+ return common.download_files(ctx, source.downloads)
end
return M
diff --git a/lua/mason-core/installer/registry/providers/github/build.lua b/lua/mason-core/installer/registry/providers/github/build.lua
index bda0b655..1c17bb1a 100644
--- a/lua/mason-core/installer/registry/providers/github/build.lua
+++ b/lua/mason-core/installer/registry/providers/github/build.lua
@@ -1,6 +1,6 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
-local build = require "mason-core.installer.managers.build"
+local common = require "mason-core.installer.managers.common"
local expr = require "mason-core.installer.registry.expr"
local util = require "mason-core.installer.registry.util"
@@ -15,7 +15,7 @@ local M = {}
function M.parse(source, purl, opts)
return Result.try(function(try)
---@type BuildInstruction
- local build_instruction = try(util.coalesce_by_target(source.build, opts):ok_or "PLATFORM_UNSUPPORTED")
+ local build_instruction = try(util.coalesce_by_target(source.build, opts))
local expr_ctx = { version = purl.version }
@@ -44,7 +44,7 @@ function M.install(ctx, source)
local std = require "mason-core.installer.managers.std"
return Result.try(function(try)
try(std.clone(source.repo, { rev = source.rev }))
- try(build.run(source.build))
+ try(common.run_build_instruction(source.build))
end)
end
diff --git a/lua/mason-core/installer/registry/providers/github/release.lua b/lua/mason-core/installer/registry/providers/github/release.lua
index 5fe95cab..8c8a8a8f 100644
--- a/lua/mason-core/installer/registry/providers/github/release.lua
+++ b/lua/mason-core/installer/registry/providers/github/release.lua
@@ -1,17 +1,13 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
-local a = require "mason-core.async"
+local common = require "mason-core.installer.managers.common"
local expr = require "mason-core.installer.registry.expr"
local providers = require "mason-core.providers"
local settings = require "mason.settings"
local util = require "mason-core.installer.registry.util"
----@class GitHubReleaseAsset
----@field target? Platform | Platform[]
----@field file string | string[]
-
---@class GitHubReleaseSource : RegistryPackageSource
----@field asset GitHubReleaseAsset | GitHubReleaseAsset[]
+---@field asset FileDownloadSpec | FileDownloadSpec[]
local M = {}
@@ -20,63 +16,22 @@ local M = {}
---@param opts PackageInstallOpts
function M.parse(source, purl, opts)
return Result.try(function(try)
- local asset = try(util.coalesce_by_target(source.asset, opts):ok_or "PLATFORM_UNSUPPORTED")
-
local expr_ctx = { version = purl.version }
+ ---@type FileDownloadSpec
+ local asset = try(util.coalesce_by_target(try(expr.tbl_interpolate(source.asset, expr_ctx)), opts))
- ---@type { out_file: string, download_url: string }[]
- local downloads = {}
-
- local interpolated_asset = try(expr.tbl_interpolate(asset, expr_ctx))
-
- ---@param file string
- ---@return Result # Result<{ source_file: string, out_file: string }>
- local function parse_asset_file(file)
- return Result.try(function(try)
- local asset_file_components = _.split(":", file)
- local source_file = try(expr.interpolate(_.head(asset_file_components), expr_ctx))
- local out_file = try(expr.interpolate(_.last(asset_file_components), expr_ctx))
-
- if _.matches("/$", out_file) then
- -- out_file is a dir expression (e.g. "libexec/")
- out_file = out_file .. source_file
- end
-
- return {
- source_file = source_file,
- out_file = out_file,
- }
- end)
- end
-
- local get_downloads = _.map(function(asset_file)
- return {
- out_file = asset_file.out_file,
- download_url = settings.current.github.download_url_template:format(
- ("%s/%s"):format(purl.namespace, purl.name),
- purl.version,
- asset_file.source_file
- ),
- }
+ local downloads = common.parse_downloads(asset, function(file)
+ return settings.current.github.download_url_template:format(
+ ("%s/%s"):format(purl.namespace, purl.name),
+ purl.version,
+ file
+ )
end)
- if type(asset.file) == "string" then
- local parsed_asset_file = try(parse_asset_file(asset.file))
- downloads = get_downloads { parsed_asset_file }
- interpolated_asset.file = parsed_asset_file.out_file
- else
- local parsed_asset_files = {}
- for _, file in ipairs(asset.file) do
- table.insert(parsed_asset_files, try(parse_asset_file(file)))
- end
- downloads = get_downloads(parsed_asset_files)
- interpolated_asset.file = _.map(_.prop "out_file", parsed_asset_files)
- end
-
---@class ParsedGitHubReleaseSource : ParsedPackageSource
local parsed_source = {
repo = ("%s/%s"):format(purl.namespace, purl.name),
- asset = interpolated_asset,
+ asset = common.normalize_files(asset),
downloads = downloads,
}
return parsed_source
@@ -87,26 +42,7 @@ end
---@param ctx InstallContext
---@param source ParsedGitHubReleaseSource
function M.install(ctx, source)
- local std = require "mason-core.installer.managers.std"
-
- return Result.try(function(try)
- for __, download in ipairs(source.downloads) do
- a.scheduler()
- local out_dir = vim.fn.fnamemodify(download.out_file, ":h")
- local out_file = vim.fn.fnamemodify(download.out_file, ":t")
- if out_dir ~= "." then
- try(Result.pcall(function()
- ctx.fs:mkdirp(out_dir)
- end))
- end
- try(ctx:chdir(out_dir, function()
- return Result.try(function(try)
- try(std.download_file(download.download_url, out_file))
- try(std.unpack(out_file))
- end)
- end))
- end
- end)
+ return common.download_files(ctx, source.downloads)
end
---@async
diff --git a/lua/mason-core/installer/registry/providers/npm.lua b/lua/mason-core/installer/registry/providers/npm.lua
index d1865b96..e8489fe8 100644
--- a/lua/mason-core/installer/registry/providers/npm.lua
+++ b/lua/mason-core/installer/registry/providers/npm.lua
@@ -1,7 +1,6 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local providers = require "mason-core.providers"
-local util = require "mason-core.installer.registry.util"
---@param purl Purl
local function purl_to_npm(purl)
diff --git a/lua/mason-core/installer/registry/providers/openvsx.lua b/lua/mason-core/installer/registry/providers/openvsx.lua
new file mode 100644
index 00000000..df52807a
--- /dev/null
+++ b/lua/mason-core/installer/registry/providers/openvsx.lua
@@ -0,0 +1,63 @@
+local Result = require "mason-core.result"
+local common = require "mason-core.installer.managers.common"
+local expr = require "mason-core.installer.registry.expr"
+local providers = require "mason-core.providers"
+local util = require "mason-core.installer.registry.util"
+
+local M = {}
+
+---@class OpenVSXSourceDownload : FileDownloadSpec
+---@field target_platform? string
+
+---@class OpenVSXSource : RegistryPackageSource
+---@field download FileDownloadSpec | FileDownloadSpec[]
+
+---@param source OpenVSXSource
+---@param purl Purl
+---@param opts PackageInstallOpts
+function M.parse(source, purl, opts)
+ return Result.try(function(try)
+ local expr_ctx = { version = purl.version }
+ ---@type OpenVSXSourceDownload
+ local download = try(util.coalesce_by_target(try(expr.tbl_interpolate(source.download, expr_ctx)), opts))
+
+ local downloads = common.parse_downloads(download, function(file)
+ if download.target_platform then
+ return ("https://open-vsx.org/api/%s/%s/%s/%s/file/%s"):format(
+ purl.namespace,
+ purl.name,
+ download.target_platform,
+ purl.version,
+ file
+ )
+ else
+ return ("https://open-vsx.org/api/%s/%s/%s/file/%s"):format(
+ purl.namespace,
+ purl.name,
+ purl.version,
+ file
+ )
+ end
+ end)
+
+ ---@class ParsedOpenVSXSource : ParsedPackageSource
+ local parsed_source = {
+ download = common.normalize_files(download),
+ downloads = downloads,
+ }
+ return parsed_source
+ end)
+end
+
+---@param ctx InstallContext
+---@param source ParsedOpenVSXSource
+function M.install(ctx, source)
+ return common.download_files(ctx, source.downloads)
+end
+
+---@param purl Purl
+function M.get_versions(purl)
+ return providers.openvsx.get_all_versions(purl.namespace, purl.name)
+end
+
+return M
diff --git a/lua/mason-core/installer/registry/util.lua b/lua/mason-core/installer/registry/util.lua
index 33aa5c33..d0045a3e 100644
--- a/lua/mason-core/installer/registry/util.lua
+++ b/lua/mason-core/installer/registry/util.lua
@@ -10,10 +10,10 @@ local M = {}
---@generic T : { target: Platform | Platform[] }
---@param candidates T[] | T
---@param opts PackageInstallOpts
----@return Optional # Optional<T>
+---@return Result # Result<T>
function M.coalesce_by_target(candidates, opts)
if not vim.tbl_islist(candidates) then
- return Optional.of(candidates)
+ return Result.success(candidates)
end
return Optional.of_nilable(_.find_first(function(asset)
if opts.target then
@@ -33,7 +33,7 @@ function M.coalesce_by_target(candidates, opts)
return platform.is[asset.target]
end
end
- end, candidates))
+ end, candidates)):ok_or "PLATFORM_UNSUPPORTED"
end
---Checks whether a custom version of a package installation corresponds to a valid version.
diff --git a/lua/mason-core/providers/init.lua b/lua/mason-core/providers/init.lua
index 15e8081c..a97d1b60 100644
--- a/lua/mason-core/providers/init.lua
+++ b/lua/mason-core/providers/init.lua
@@ -44,6 +44,10 @@ local settings = require "mason.settings"
---@class GolangProvider
---@field get_all_versions? async fun(pkg: string): Result # Result<string[]>
+---@class OpenVSXProvider
+---@field get_latest_version? async fun(namespace: string, extension: string): Result # Result<Crate>
+---@field get_all_versions? async fun(namespace: string, extension: string): Result # Result<string[]>
+
---@class Provider
---@field github? GitHubProvider
---@field npm? NpmProvider
@@ -52,6 +56,7 @@ local settings = require "mason.settings"
---@field packagist? PackagistProvider
---@field crates? CratesProvider
---@field golang? GolangProvider
+---@field openvsx? OpenVSXProvider
local function service_mt(service)
return setmetatable({}, {
diff --git a/lua/mason-core/semver.lua b/lua/mason-core/semver.lua
index 635b7e36..4e45567d 100644
--- a/lua/mason-core/semver.lua
+++ b/lua/mason-core/semver.lua
@@ -4,12 +4,14 @@ local semver = require "mason-vendor.semver"
local M = {}
---@param version string
+---@return Semver
function M.new(version)
version = version:gsub("^v", "")
return semver(version)
end
---@param version string
+---@return Result # Result<Semver>
function M.parse(version)
return Result.pcall(M.new, version)
end
diff --git a/lua/mason-registry/api.lua b/lua/mason-registry/api.lua
index 853690b9..b4acea63 100644
--- a/lua/mason-registry/api.lua
+++ b/lua/mason-registry/api.lua
@@ -120,4 +120,13 @@ api.golang = {
},
}
+api.openvsx = {
+ versions = {
+ ---@type ApiSignature<{ namespace: string, extension: string }>
+ latest = get "/api/openvsx/{namespace}/{extension}/versions/latest",
+ ---@type ApiSignature<{ namespace: string, extension: string }>
+ all = get "/api/openvsx/{namespace}/{extension}/versions/all",
+ },
+}
+
return api
diff --git a/lua/mason/providers/client/init.lua b/lua/mason/providers/client/init.lua
index 0bc264fc..8035b7eb 100644
--- a/lua/mason/providers/client/init.lua
+++ b/lua/mason/providers/client/init.lua
@@ -5,4 +5,5 @@ return {
pypi = require "mason.providers.client.pypi",
rubygems = require "mason.providers.client.rubygems",
golang = require "mason.providers.client.golang",
+ openvsx = require "mason.providers.client.openvsx",
}
diff --git a/lua/mason/providers/client/openvsx.lua b/lua/mason/providers/client/openvsx.lua
new file mode 100644
index 00000000..32428be3
--- /dev/null
+++ b/lua/mason/providers/client/openvsx.lua
@@ -0,0 +1,26 @@
+local _ = require "mason-core.functional"
+local fetch = require "mason-core.fetch"
+local semver = require "mason-core.semver"
+
+---@param path string
+local function api_url(path)
+ return ("https://open-vsx.org/api/%s"):format(path)
+end
+
+---@param version string
+local function maybe_semver_sort(version)
+ return semver.parse(version):get_or_else(version)
+end
+
+---@type OpenVSXProvider
+return {
+ get_latest_version = function(namespace, extension)
+ return fetch(api_url("%s/%s"):format(namespace, extension)):map_catching(vim.json.decode):map(_.prop "version")
+ end,
+ get_all_versions = function(namespace, extension)
+ return fetch(api_url("%s/%s/versions"):format(namespace, extension))
+ :map_catching(vim.json.decode)
+ :map(_.compose(_.keys, _.prop "versions"))
+ :map(_.compose(_.reverse, _.sort_by(maybe_semver_sort)))
+ end,
+}
diff --git a/lua/mason/providers/registry-api/init.lua b/lua/mason/providers/registry-api/init.lua
index 0f98a407..d8802124 100644
--- a/lua/mason/providers/registry-api/init.lua
+++ b/lua/mason/providers/registry-api/init.lua
@@ -61,4 +61,12 @@ return {
return api.golang.versions.all { pkg = api.encode_uri_component(pkg) }
end,
},
+ openvsx = {
+ get_latest_version = function(namespace, extension)
+ return api.openvsx.versions.latest { namespace = namespace, extension = extension }
+ end,
+ get_all_versions = function(namespace, extension)
+ return api.openvsx.versions.all { namespace = namespace, extension = extension }
+ end,
+ },
}
diff --git a/tests/mason-core/installer/managers/build_spec.lua b/tests/mason-core/installer/managers/build_spec.lua
index 73dd63c1..e709fdd0 100644
--- a/tests/mason-core/installer/managers/build_spec.lua
+++ b/tests/mason-core/installer/managers/build_spec.lua
@@ -1,5 +1,5 @@
local Result = require "mason-core.result"
-local build = require "mason-core.installer.managers.build"
+local common = require "mason-core.installer.managers.common"
local installer = require "mason-core.installer"
local match = require "luassert.match"
local mock = require "luassert.mock"
@@ -25,7 +25,7 @@ describe("build manager", function()
)
local result = installer.exec_in_context(ctx, function()
- return build.run {
+ return common.run_build_instruction {
run = [[npm install && npm run compile]],
env = {
MASON_VERSION = "2023-03-09",
@@ -57,7 +57,7 @@ describe("build manager", function()
stub(ctx.spawn, "bash", mockx.returns(Result.success()))
local result = installer.exec_in_context(ctx, function()
- return build.run {
+ return common.run_build_instruction {
run = "make",
staged = false,
}
diff --git a/tests/mason-core/installer/managers/common_spec.lua b/tests/mason-core/installer/managers/common_spec.lua
new file mode 100644
index 00000000..e72d7697
--- /dev/null
+++ b/tests/mason-core/installer/managers/common_spec.lua
@@ -0,0 +1,161 @@
+local Result = require "mason-core.result"
+local _ = require "mason-core.functional"
+local common = require "mason-core.installer.managers.common"
+local installer = require "mason-core.installer"
+local match = require "luassert.match"
+local mock = require "luassert.mock"
+local spy = require "luassert.spy"
+local std = require "mason-core.installer.managers.std"
+local stub = require "luassert.stub"
+
+describe("common manager :: download", function()
+ it("should parse download files from common structure", function()
+ local url_generator = _.format "https://example.com/%s"
+
+ assert.same(
+ {
+ {
+ download_url = "https://example.com/abc.jar",
+ out_file = "abc.jar",
+ },
+ },
+ common.parse_downloads({
+ file = "abc.jar",
+ }, url_generator)
+ )
+
+ assert.same(
+ {
+ {
+ download_url = "https://example.com/abc.jar",
+ out_file = "lib/abc.jar",
+ },
+ },
+ common.parse_downloads({
+ file = "abc.jar:lib/",
+ }, url_generator)
+ )
+
+ assert.same(
+ {
+ {
+ download_url = "https://example.com/abc.jar",
+ out_file = "lib/abc.jar",
+ },
+ {
+ download_url = "https://example.com/file.jar",
+ out_file = "lib/nested/new-name.jar",
+ },
+ },
+ common.parse_downloads({
+ file = { "abc.jar:lib/", "file.jar:lib/nested/new-name.jar" },
+ }, url_generator)
+ )
+ end)
+
+ it("should download files", function()
+ local ctx = create_dummy_context()
+ stub(std, "download_file", mockx.returns(Result.success()))
+ stub(std, "unpack", mockx.returns(Result.success()))
+
+ local result = installer.exec_in_context(ctx, function()
+ return common.download_files(ctx, {
+ { out_file = "file.jar", download_url = "https://example.com/file.jar" },
+ { out_file = "LICENSE.md", download_url = "https://example.com/LICENSE" },
+ })
+ end)
+
+ assert.is_true(result:is_success())
+ assert.spy(std.download_file).was_called(2)
+ assert.spy(std.download_file).was_called_with("https://example.com/file.jar", "file.jar")
+ assert.spy(std.download_file).was_called_with("https://example.com/LICENSE", "LICENSE.md")
+ assert.spy(std.unpack).was_called(2)
+ assert.spy(std.unpack).was_called_with "file.jar"
+ assert.spy(std.unpack).was_called_with "LICENSE.md"
+ end)
+
+ it("should download files to specified directory", function()
+ local ctx = create_dummy_context()
+ stub(std, "download_file", mockx.returns(Result.success()))
+ stub(std, "unpack", mockx.returns(Result.success()))
+ stub(ctx.fs, "mkdirp")
+
+ local result = installer.exec_in_context(ctx, function()
+ return common.download_files(ctx, {
+ { out_file = "lib/file.jar", download_url = "https://example.com/file.jar" },
+ { out_file = "doc/LICENSE.md", download_url = "https://example.com/LICENSE" },
+ { out_file = "nested/path/to/file", download_url = "https://example.com/some-file" },
+ })
+ end)
+
+ assert.is_true(result:is_success())
+
+ assert.spy(ctx.fs.mkdirp).was_called(3)
+ assert.spy(ctx.fs.mkdirp).was_called_with(match.is_ref(ctx.fs), "lib")
+ assert.spy(ctx.fs.mkdirp).was_called_with(match.is_ref(ctx.fs), "doc")
+ assert.spy(ctx.fs.mkdirp).was_called_with(match.is_ref(ctx.fs), "nested/path/to")
+ end)
+end)
+
+describe("common manager :: build", function()
+ it("should run build instruction", function()
+ local ctx = create_dummy_context()
+ local uv = require "mason-core.async.uv"
+ spy.on(ctx, "promote_cwd")
+ stub(uv, "write")
+ stub(uv, "shutdown")
+ stub(uv, "close")
+ local stdin = mock.new()
+ stub(
+ ctx.spawn,
+ "bash", ---@param args SpawnArgs
+ function(args)
+ args.on_spawn(mock.new(), { stdin })
+ return Result.success()
+ end
+ )
+
+ local result = installer.exec_in_context(ctx, function()
+ return common.run_build_instruction {
+ run = [[npm install && npm run compile]],
+ env = {
+ MASON_VERSION = "2023-03-09",
+ SOME_VALUE = "here",
+ },
+ }
+ end)
+
+ assert.is_true(result:is_success())
+ assert.spy(ctx.promote_cwd).was_called(0)
+ assert.spy(ctx.spawn.bash).was_called(1)
+ assert.spy(ctx.spawn.bash).was_called_with(match.tbl_containing {
+ on_spawn = match.is_function(),
+ env = match.same {
+ MASON_VERSION = "2023-03-09",
+ SOME_VALUE = "here",
+ },
+ })
+ assert.spy(uv.write).was_called(2)
+ assert.spy(uv.write).was_called_with(stdin, "set -euxo pipefail;\n")
+ assert.spy(uv.write).was_called_with(stdin, "npm install && npm run compile")
+ assert.spy(uv.shutdown).was_called_with(stdin)
+ assert.spy(uv.close).was_called_with(stdin)
+ end)
+
+ it("should promote cwd if not staged", function()
+ local ctx = create_dummy_context()
+ stub(ctx, "promote_cwd")
+ stub(ctx.spawn, "bash", mockx.returns(Result.success()))
+
+ local result = installer.exec_in_context(ctx, function()
+ return common.run_build_instruction {
+ run = "make",
+ staged = false,
+ }
+ end)
+
+ assert.is_true(result:is_success())
+ assert.spy(ctx.promote_cwd).was_called(1)
+ assert.spy(ctx.spawn.bash).was_called(1)
+ end)
+end)
diff --git a/tests/mason-core/installer/registry/providers/generic/build_spec.lua b/tests/mason-core/installer/registry/providers/generic/build_spec.lua
index ccc77ac3..443cb99a 100644
--- a/tests/mason-core/installer/registry/providers/generic/build_spec.lua
+++ b/tests/mason-core/installer/registry/providers/generic/build_spec.lua
@@ -121,8 +121,8 @@ end)
describe("generic provider :: build :: installing", function()
it("should install", function()
local ctx = create_dummy_context()
- local build = require "mason-core.installer.managers.build"
- stub(build, "run", mockx.returns(Result.success()))
+ local common = require "mason-core.installer.managers.common"
+ stub(common, "run_build_instruction", mockx.returns(Result.success()))
local result = installer.exec_in_context(ctx, function()
return generic.install(ctx, {
@@ -134,8 +134,8 @@ describe("generic provider :: build :: installing", function()
end)
assert.is_true(result:is_success())
- assert.spy(build.run).was_called(1)
- assert.spy(build.run).was_called_with {
+ assert.spy(common.run_build_instruction).was_called(1)
+ assert.spy(common.run_build_instruction).was_called_with {
run = "make",
env = { VALUE = "here" },
}
diff --git a/tests/mason-core/installer/registry/providers/generic/download_spec.lua b/tests/mason-core/installer/registry/providers/generic/download_spec.lua
index 1d3583f9..4bcb1976 100644
--- a/tests/mason-core/installer/registry/providers/generic/download_spec.lua
+++ b/tests/mason-core/installer/registry/providers/generic/download_spec.lua
@@ -2,6 +2,7 @@ local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local generic = require "mason-core.installer.registry.providers.generic"
local installer = require "mason-core.installer"
+local match = require "luassert.match"
local stub = require "luassert.stub"
---@param overrides Purl
@@ -17,6 +18,12 @@ describe("generic provider :: download :: parsing", function()
it("should parse single download target", function()
assert.same(
Result.success {
+ downloads = {
+ {
+ out_file = "name.tar.gz",
+ download_url = "https://getpackage.org/downloads/1.2.0/name.tar.gz",
+ },
+ },
download = {
files = {
["name.tar.gz"] = [[https://getpackage.org/downloads/1.2.0/name.tar.gz]],
@@ -36,6 +43,12 @@ describe("generic provider :: download :: parsing", function()
it("should coalesce download target", function()
assert.same(
Result.success {
+ downloads = {
+ {
+ out_file = "name.tar.gz",
+ download_url = "https://getpackage.org/downloads/linux-aarch64/1.2.0/name.tar.gz",
+ },
+ },
download = {
target = "linux_arm64",
files = {
@@ -88,31 +101,33 @@ end)
describe("generic provider :: download :: installing", function()
it("should install generic packages", function()
local ctx = create_dummy_context()
- local std = require "mason-core.installer.managers.std"
- stub(std, "download_file", mockx.returns(Result.success()))
- stub(std, "unpack", mockx.returns(Result.success()))
+ local common = require "mason-core.installer.managers.common"
+ stub(common, "download_files", mockx.returns(Result.success()))
local result = installer.exec_in_context(ctx, function()
return generic.install(ctx, {
+ downloads = {
+ {
+ out_file = "name.tar.gz",
+ download_url = "https://getpackage.org/downloads/linux-aarch64/1.2.0/name.tar.gz",
+ },
+ },
download = {
+ target = "linux_arm64",
files = {
["name.tar.gz"] = [[https://getpackage.org/downloads/linux-aarch64/1.2.0/name.tar.gz]],
- ["archive.tar.gz"] = [[https://getpackage.org/downloads/linux-aarch64/1.2.0/archive.tar.gz]],
},
},
})
end)
assert.is_true(result:is_success())
- assert.spy(std.download_file).was_called(2)
- assert
- .spy(std.download_file)
- .was_called_with("https://getpackage.org/downloads/linux-aarch64/1.2.0/name.tar.gz", "name.tar.gz")
- assert
- .spy(std.download_file)
- .was_called_with("https://getpackage.org/downloads/linux-aarch64/1.2.0/archive.tar.gz", "archive.tar.gz")
- assert.spy(std.unpack).was_called(2)
- assert.spy(std.unpack).was_called_with "name.tar.gz"
- assert.spy(std.unpack).was_called_with "archive.tar.gz"
+ assert.spy(common.download_files).was_called(1)
+ assert.spy(common.download_files).was_called_with(match.is_ref(ctx), {
+ {
+ out_file = "name.tar.gz",
+ download_url = "https://getpackage.org/downloads/linux-aarch64/1.2.0/name.tar.gz",
+ },
+ })
end)
end)
diff --git a/tests/mason-core/installer/registry/providers/github/build_spec.lua b/tests/mason-core/installer/registry/providers/github/build_spec.lua
index b25c26d8..17667d2c 100644
--- a/tests/mason-core/installer/registry/providers/github/build_spec.lua
+++ b/tests/mason-core/installer/registry/providers/github/build_spec.lua
@@ -1,10 +1,6 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local github = require "mason-core.installer.registry.providers.github"
-local installer = require "mason-core.installer"
-local match = require "luassert.match"
-local mock = require "luassert.mock"
-local stub = require "luassert.stub"
---@param overrides Purl
local function purl(overrides)
@@ -60,39 +56,3 @@ describe("github provider :: build :: parsing", function()
)
end)
end)
-
-describe("github provider :: build :: installing", function()
- it("should install github build sources", function()
- local ctx = create_dummy_context()
- local std = require "mason-core.installer.managers.std"
- local build = require "mason-core.installer.managers.build"
- stub(std, "clone", mockx.returns(Result.success()))
- stub(build, "run", mockx.returns(Result.success()))
-
- local result = installer.exec_in_context(ctx, function()
- return github.install(ctx, {
- repo = "namespace/name",
- rev = "2023-03-09",
- build = {
- run = [[npm install && npm run compile]],
- env = {
- MASON_VERSION = "2023-03-09",
- SOME_VALUE = "here",
- },
- },
- }, purl())
- end)
-
- assert.is_true(result:is_success())
- assert.spy(std.clone).was_called(1)
- assert.spy(std.clone).was_called_with("namespace/name", { rev = "2023-03-09" })
- assert.spy(build.run).was_called(1)
- assert.spy(build.run).was_called_with {
- run = [[npm install && npm run compile]],
- env = {
- MASON_VERSION = "2023-03-09",
- SOME_VALUE = "here",
- },
- }
- end)
-end)
diff --git a/tests/mason-core/installer/registry/providers/github/release_spec.lua b/tests/mason-core/installer/registry/providers/github/release_spec.lua
index 909eb36f..a6648b33 100644
--- a/tests/mason-core/installer/registry/providers/github/release_spec.lua
+++ b/tests/mason-core/installer/registry/providers/github/release_spec.lua
@@ -1,10 +1,10 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
+local common = require "mason-core.installer.managers.common"
local github = require "mason-core.installer.registry.providers.github"
local installer = require "mason-core.installer"
local match = require "luassert.match"
local registry_installer = require "mason-core.installer.registry"
-local spy = require "luassert.spy"
local stub = require "luassert.stub"
---@param overrides Purl
@@ -285,6 +285,7 @@ describe("github provider :: release :: installing", function()
local std = require "mason-core.installer.managers.std"
stub(std, "download_file", mockx.returns(Result.success()))
stub(std, "unpack", mockx.returns(Result.success()))
+ stub(common, "download_files", mockx.returns(Result.success()))
local result = installer.exec_in_context(ctx, function()
return github.install(ctx, {
@@ -306,62 +307,16 @@ describe("github provider :: release :: installing", function()
end)
assert.is_true(result:is_success())
- assert.spy(std.download_file).was_called(2)
- assert.spy(std.download_file).was_called_with(
- "https://github.com/namespace/name/releases/download/2023-03-09/file-linux-amd64-2023-03-09.tar.gz",
- "file-linux-amd64-2023-03-09.tar.gz"
- )
- assert.spy(std.download_file).was_called_with(
- "https://github.com/namespace/name/releases/download/2023-03-09/another-file-linux-amd64-2023-03-09.tar.gz",
- "another-file-linux-amd64-2023-03-09.tar.gz"
- )
- assert.spy(std.unpack).was_called(2)
- assert.spy(std.unpack).was_called_with "file-linux-amd64-2023-03-09.tar.gz"
- assert.spy(std.unpack).was_called_with "another-file-linux-amd64-2023-03-09.tar.gz"
- end)
-
- it("should install github release assets into specified output directory", function()
- local ctx = create_dummy_context()
- local std = require "mason-core.installer.managers.std"
- local download_file_cwd, unpack_cwd
- stub(std, "download_file", function()
- download_file_cwd = ctx.cwd:get()
- return Result.success()
- end)
- stub(std, "unpack", function()
- unpack_cwd = ctx.cwd:get()
- return Result.success()
- end)
- stub(ctx.fs, "mkdirp")
- spy.on(ctx, "chdir")
-
- local result = installer.exec_in_context(ctx, function()
- return github.install(ctx, {
- repo = "namespace/name",
- asset = {
- file = "file.zip",
- },
- downloads = {
- {
- out_file = "out/dir/file.zip",
- download_url = "https://github.com/namespace/name/releases/download/2023-03-09/file.zip",
- },
- },
- })
- end)
-
- assert.is_true(result:is_success())
- assert.spy(ctx.fs.mkdirp).was_called(1)
- assert.spy(ctx.fs.mkdirp).was_called_with(match.is_ref(ctx.fs), "out/dir")
- assert.spy(ctx.chdir).was_called(1)
- assert.spy(ctx.chdir).was_called_with(match.is_ref(ctx), "out/dir", match.is_function())
- assert.spy(std.download_file).was_called(1)
- assert.is_true(match.matches "out/dir$"(download_file_cwd))
- assert
- .spy(std.download_file)
- .was_called_with("https://github.com/namespace/name/releases/download/2023-03-09/file.zip", "file.zip")
- assert.spy(std.unpack).was_called(1)
- assert.is_true(match.matches "out/dir$"(unpack_cwd))
- assert.spy(std.unpack).was_called_with "file.zip"
+ assert.spy(common.download_files).was_called(1)
+ assert.spy(common.download_files).was_called_with(match.is_ref(ctx), {
+ {
+ out_file = "file-linux-amd64-2023-03-09.tar.gz",
+ download_url = "https://github.com/namespace/name/releases/download/2023-03-09/file-linux-amd64-2023-03-09.tar.gz",
+ },
+ {
+ out_file = "another-file-linux-amd64-2023-03-09.tar.gz",
+ download_url = "https://github.com/namespace/name/releases/download/2023-03-09/another-file-linux-amd64-2023-03-09.tar.gz",
+ },
+ })
end)
end)
diff --git a/tests/mason-core/installer/registry/providers/openvsx_spec.lua b/tests/mason-core/installer/registry/providers/openvsx_spec.lua
new file mode 100644
index 00000000..1452ea0f
--- /dev/null
+++ b/tests/mason-core/installer/registry/providers/openvsx_spec.lua
@@ -0,0 +1,149 @@
+local Purl = require "mason-core.purl"
+local Result = require "mason-core.result"
+local common = require "mason-core.installer.managers.common"
+local installer = require "mason-core.installer"
+local match = require "luassert.match"
+local openvsx = require "mason-core.installer.registry.providers.openvsx"
+local stub = require "luassert.stub"
+
+---@param overrides Purl
+local function purl(overrides)
+ local purl = Purl.parse("pkg:openvsx/namespace/name@1.10.1"):get_or_throw()
+ if not overrides then
+ return purl
+ end
+ return vim.tbl_deep_extend("force", purl, overrides)
+end
+
+describe("openvsx provider :: download :: parsing", function()
+ it("should parse download source", function()
+ assert.same(
+ Result.success {
+ download = {
+ file = "file-1.10.1.jar",
+ },
+ downloads = {
+ {
+ out_file = "file-1.10.1.jar",
+ download_url = "https://open-vsx.org/api/namespace/name/1.10.1/file/file-1.10.1.jar",
+ },
+ },
+ },
+ openvsx.parse({
+ download = {
+ file = "file-{{version}}.jar",
+ },
+ }, purl())
+ )
+ end)
+
+ it("should parse download source with multiple targets", function()
+ assert.same(
+ Result.success {
+ download = {
+ target = "linux_x64",
+ file = "file-linux-amd64-1.0.0.vsix",
+ },
+ downloads = {
+ {
+ out_file = "file-linux-amd64-1.0.0.vsix",
+ download_url = "https://open-vsx.org/api/namespace/name/1.0.0/file/file-linux-amd64-1.0.0.vsix",
+ },
+ },
+ },
+ openvsx.parse({
+ download = {
+ {
+ target = "win_arm",
+ file = "file-win-arm-{{version}}.vsix",
+ },
+ {
+ target = "linux_x64",
+ file = "file-linux-amd64-{{version}}.vsix",
+ },
+ },
+ }, purl { version = "1.0.0" }, { target = "linux_x64" })
+ )
+ end)
+
+ it("should parse download source with output to different directory", function()
+ assert.same(
+ Result.success {
+ download = {
+ file = "out-dir/file-linux-amd64-1.10.1.vsix",
+ },
+ downloads = {
+ {
+ out_file = "out-dir/file-linux-amd64-1.10.1.vsix",
+ download_url = "https://open-vsx.org/api/namespace/name/1.10.1/file/file-linux-amd64-1.10.1.vsix",
+ },
+ },
+ },
+ openvsx.parse({
+ download = {
+ file = "file-linux-amd64-{{version}}.vsix:out-dir/",
+ },
+ }, purl(), { target = "linux_x64" })
+ )
+ end)
+
+ it("should recognize target_platform when available", function()
+ assert.same(
+ Result.success {
+ download = {
+ file = "file-linux-1.10.1@win32-arm64.vsix",
+ target = "win_arm64",
+ target_platform = "win32-arm64",
+ },
+ downloads = {
+ {
+ out_file = "file-linux-1.10.1@win32-arm64.vsix",
+ download_url = "https://open-vsx.org/api/namespace/name/win32-arm64/1.10.1/file/file-linux-1.10.1@win32-arm64.vsix",
+ },
+ },
+ },
+ openvsx.parse({
+ download = {
+ {
+ target = "win_arm64",
+ file = "file-linux-{{version}}@win32-arm64.vsix",
+ target_platform = "win32-arm64",
+ },
+ },
+ }, purl(), { target = "win_arm64" })
+ )
+ end)
+end)
+
+describe("openvsx provider :: download :: installing", function()
+ it("should install openvsx assets", function()
+ local ctx = create_dummy_context()
+ local std = require "mason-core.installer.managers.std"
+ stub(std, "download_file", mockx.returns(Result.success()))
+ stub(std, "unpack", mockx.returns(Result.success()))
+ stub(common, "download_files", mockx.returns(Result.success()))
+
+ local result = installer.exec_in_context(ctx, function()
+ return openvsx.install(ctx, {
+ download = {
+ file = "file-1.10.1.jar",
+ },
+ downloads = {
+ {
+ out_file = "file-1.10.1.jar",
+ download_url = "https://open-vsx.org/api/namespace/name/1.10.1/file/file-1.10.1.jar",
+ },
+ },
+ })
+ end)
+
+ assert.is_true(result:is_success())
+ assert.spy(common.download_files).was_called(1)
+ assert.spy(common.download_files).was_called_with(match.is_ref(ctx), {
+ {
+ out_file = "file-1.10.1.jar",
+ download_url = "https://open-vsx.org/api/namespace/name/1.10.1/file/file-1.10.1.jar",
+ },
+ })
+ end)
+end)
diff --git a/tests/mason-core/installer/registry/util_spec.lua b/tests/mason-core/installer/registry/util_spec.lua
index 09918943..851164d0 100644
--- a/tests/mason-core/installer/registry/util_spec.lua
+++ b/tests/mason-core/installer/registry/util_spec.lua
@@ -7,7 +7,7 @@ local util = require "mason-core.installer.registry.util"
describe("registry installer util", function()
it("should coalesce single target", function()
local source = { value = "here" }
- local coalesced = util.coalesce_by_target(source, {}):get()
+ local coalesced = util.coalesce_by_target(source, {}):get_or_nil()
assert.is_true(match.is_ref(source)(coalesced))
end)
@@ -19,7 +19,7 @@ describe("registry installer util", function()
value = "here",
},
source,
- }, { target = "VIC64" }):get()
+ }, { target = "VIC64" }):get_or_nil()
assert.is_true(match.is_ref(source)(coalesced))
end)