Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 67 additions & 9 deletions lua/mason-core/installer/compiler/compilers/nuget.lua
Original file line number Diff line number Diff line change
@@ -1,25 +1,83 @@
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.compiler.expr"
local nuget = require "mason-core.installer.managers.nuget"
local util = require "mason-core.installer.compiler.util"

local M = {}

---@param source RegistryPackageSource
---@class NugetPackageSource : RegistryPackageSource
---@field download FileDownloadSpec

---@param source NugetPackageSource
---@param purl Purl
function M.parse(source, purl)
---@class ParsedNugetSource : ParsedPackageSource
local parsed_source = {
package = purl.name,
version = purl.version,
}
return Result.try(function(try)
local repository_url = _.path({ "qualifiers", "repository_url" }, purl)

local download_item = nil
if source.download then
if not repository_url then
-- if not set we need to provide repository url because we need it for
-- download url discovery
repository_url = "https://api.nuget.org/v3/index.json"
end

local index_file = try(nuget.fetch_nuget_endpoint(repository_url))

local resource = vim.iter(index_file.resources):find(function (v)
return v['@type'] == 'PackageBaseAddress/3.0.0'
end)

assert(resource, "could not get PackageBaseAddress resource from nuget index file")

local package_base_address = resource["@id"]
local package_lowercase = purl.name:lower()

local nupkg_download_url = string.format(
"%s%s/%s/%s.%s.nupkg",
package_base_address,
package_lowercase,
purl.version,
package_lowercase,
purl.version
)

local expr_ctx = { version = purl.version }

---@type FileDownloadSpec
local download_spec = try(util.coalesce_by_target(try(expr.tbl_interpolate(source.download, expr_ctx)), {}))

download_item = {
download_url = nupkg_download_url,
out_file = download_spec.file,
}
end

---@class ParsedNugetSource : ParsedPackageSource
---@field download? DownloadItem
---@field repository_url string Custom repository URL to pull from
local parsed_source = {
package = purl.name,
version = purl.version,
download = download_item,
repository_url = repository_url,
}

return Result.success(parsed_source)
return parsed_source
end)
end

---@async
---@param ctx InstallContext
---@param source ParsedNugetSource
function M.install(ctx, source)
local nuget = require "mason-core.installer.managers.nuget"
return nuget.install(source.package, source.version)
if source.download then
return common.download_files(ctx, { source.download })
else
return nuget.install(source.package, source.version, source.repository_url)
end
end

---@async
Expand Down
31 changes: 27 additions & 4 deletions lua/mason-core/installer/managers/nuget.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local Result = require "mason-core.result"
local fetch = require "mason-core.fetch"
local installer = require "mason-core.installer"
local log = require "mason-core.log"
local platform = require "mason-core.platform"
Expand All @@ -8,19 +9,41 @@ local M = {}
---@async
---@param package string
---@param version string
---@param repository_url string
---@nodiscard
function M.install(package, version)
function M.install(package, version, repository_url)
log.fmt_debug("nuget: install %s %s", package, version)
local ctx = installer.context()
ctx.stdio_sink:stdout(("Installing nuget package %s@%s…\n"):format(package, version))
return ctx.spawn.dotnet {
ctx.stdio_sink.stdout(("Installing nuget package %s@%s…\n"):format(package, version))
local args = {
"tool",
"update",
"--tool-path",
".",
{ "--version", version },
package,
}

if repository_url then
table.insert(args, { "--add-source", repository_url })
end

table.insert(args, package)

return ctx.spawn.dotnet(args)
end

---@alias NugetIndexResource { '@id': string, '@type': string}
---@alias NugetIndexFile { version: string, resources: NugetIndexResource[]}

---@async
---@param repository_url string
---@return Result # Result<NugetIndexFile>
function M.fetch_nuget_endpoint(repository_url)
return fetch(repository_url, {
headers = {
Accept = "application/json",
},
}):map_catching(vim.json.decode)
end

---@param bin string
Expand Down
1 change: 1 addition & 0 deletions lua/mason-core/installer/managers/std.lua
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ local unpack_by_filename = _.cond {
{ _.matches "%.tar%.zst$", untar_zst },
{ _.matches "%.zip$", unzip },
{ _.matches "%.vsix$", unzip },
{ _.matches "%.nupkg$", unzip },
{ _.matches "%.gz$", gunzip },
{ _.T, _.compose(Result.success, _.format "%q doesn't need unpacking.") },
}
Expand Down
6 changes: 3 additions & 3 deletions tests/fixtures/purl-test-suite-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,13 @@
},
{
"description": "nuget names are case sensitive",
"purl": "pkg:Nuget/[email protected]",
"canonical_purl": "pkg:nuget/[email protected]",
"purl": "pkg:Nuget/[email protected]?repository_url=https://api.nuget.org/v3/index.json",
"canonical_purl": "pkg:nuget/[email protected]?repository_url=https://api.nuget.org/v3/index.json",
"type": "nuget",
"namespace": null,
"name": "EnterpriseLibrary.Common",
"version": "6.0.1304",
"qualifiers": null,
"qualifiers": { "repository_url": "https://api.nuget.org/v3/index.json" },
"subpath": null,
"is_invalid": false
},
Expand Down
6 changes: 4 additions & 2 deletions tests/mason-core/installer/compiler/compilers/nuget_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ local test_helpers = require "mason-test.helpers"

---@param overrides Purl
local function purl(overrides)
local purl = Purl.parse("pkg:nuget/[email protected]"):get_or_throw()
local purl = Purl.parse("pkg:nuget/[email protected]?repository_url=https://api.nuget.org/v3/index.json"):get_or_throw()
if not overrides then
return purl
end
Expand All @@ -19,6 +19,7 @@ describe("nuget compiler :: parsing", function()
Result.success {
package = "package",
version = "2.2.0",
repository_url = "https://api.nuget.org/v3/index.json",
},
nuget.parse({}, purl())
)
Expand All @@ -45,11 +46,12 @@ describe("nuget compiler :: installing", function()
return nuget.install(ctx, {
package = "package",
version = "1.5.0",
repository_url = "https://api.nuget.org/v3/index.json",
})
end)

assert.is_true(result:is_success())
assert.spy(manager.install).was_called(1)
assert.spy(manager.install).was_called_with("package", "1.5.0")
assert.spy(manager.install).was_called_with("package", "1.5.0", "https://api.nuget.org/v3/index.json")
end)
end)