aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2023-10-11 15:33:10 +0200
committerWilliam Boman <william@redwill.se>2025-02-16 09:49:17 +0100
commit40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa (patch)
tree1c092b13b2c40c693db87f040410411977990cef
parentfix(package): support older receipt structures (#1520) (diff)
downloadmason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.tar
mason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.tar.gz
mason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.tar.bz2
mason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.tar.lz
mason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.tar.xz
mason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.tar.zst
mason-40bb6ddfa84b91f58a53f9d92ce7a3ce0c57b9aa.zip
refactor(receipt): change receipt structure and remove old builder APIs (#1521)
-rw-r--r--lua/mason-core/installer/init.lua6
-rw-r--r--lua/mason-core/installer/linker.lua6
-rw-r--r--lua/mason-core/installer/registry/init.lua3
-rw-r--r--lua/mason-core/package/init.lua10
-rw-r--r--lua/mason-core/receipt.lua124
-rw-r--r--lua/mason/ui/instance.lua2
-rw-r--r--tests/fixtures/receipts/1.0.json23
-rw-r--r--tests/fixtures/receipts/1.1.json27
-rw-r--r--tests/fixtures/receipts/1.2.json19
-rw-r--r--tests/mason-core/installer/installer_spec.lua5
-rw-r--r--tests/mason-core/receipt_spec.lua86
11 files changed, 207 insertions, 104 deletions
diff --git a/lua/mason-core/installer/init.lua b/lua/mason-core/installer/init.lua
index 961c5c47..45bba46b 100644
--- a/lua/mason-core/installer/init.lua
+++ b/lua/mason-core/installer/init.lua
@@ -37,11 +37,7 @@ end
local function build_receipt(context)
return Result.pcall(function()
log.fmt_debug("Building receipt for %s", context.package)
- return context.receipt
- :with_name(context.package.name)
- :with_schema_version("1.1")
- :with_completion_time(vim.loop.gettimeofday())
- :build()
+ return context.receipt:with_name(context.package.name):with_completion_time(vim.loop.gettimeofday()):build()
end)
end
diff --git a/lua/mason-core/installer/linker.lua b/lua/mason-core/installer/linker.lua
index 7a1098fb..83b1f4a5 100644
--- a/lua/mason-core/installer/linker.lua
+++ b/lua/mason-core/installer/linker.lua
@@ -21,12 +21,12 @@ local LinkContext = {
---@param link_context LinkContext
local function unlink(receipt, link_context)
return Result.pcall(function()
- local links = receipt.links[link_context.type]
+ local links = receipt:get_links()[link_context.type]
if not links then
return
end
for linked_file in pairs(links) do
- if receipt.schema_version == "1.0" and link_context == LinkContext.BIN and platform.is.win then
+ if receipt:get_schema_version() == "1.0" and link_context == LinkContext.BIN and platform.is.win then
linked_file = linked_file .. ".cmd"
end
local share_path = link_context.prefix(linked_file)
@@ -39,7 +39,7 @@ end
---@param receipt InstallReceipt
---@nodiscard
function M.unlink(pkg, receipt)
- log.fmt_debug("Unlinking %s", pkg, receipt.links)
+ log.fmt_debug("Unlinking %s", pkg, receipt:get_links())
return Result.try(function(try)
try(unlink(receipt, LinkContext.BIN))
try(unlink(receipt, LinkContext.SHARE))
diff --git a/lua/mason-core/installer/registry/init.lua b/lua/mason-core/installer/registry/init.lua
index c700715e..7376db86 100644
--- a/lua/mason-core/installer/registry/init.lua
+++ b/lua/mason-core/installer/registry/init.lua
@@ -10,6 +10,7 @@ local util = require "mason-core.installer.registry.util"
local M = {}
+---@type table<RegistryPackageSpecSchema, boolean>
M.SCHEMA_CAP = _.set_of {
"registry+v1",
}
@@ -204,7 +205,7 @@ function M.compile(spec, opts)
try(link.opt(ctx, spec, parsed.purl, parsed.source))
end
- ctx.receipt:with_primary_source {
+ ctx.receipt:with_source {
type = ctx.package.spec.schema,
id = Purl.compile(parsed.purl),
}
diff --git a/lua/mason-core/package/init.lua b/lua/mason-core/package/init.lua
index 4af07642..bc98a72a 100644
--- a/lua/mason-core/package/init.lua
+++ b/lua/mason-core/package/init.lua
@@ -69,8 +69,11 @@ local PackageMt = { __index = Package }
---@field since string
---@field message string
+---@alias RegistryPackageSpecSchema
+--- | '"registry+v1"'
+
---@class RegistryPackageSpec
----@field schema '"registry+v1"'
+---@field schema RegistryPackageSpecSchema
---@field name string
---@field description string
---@field homepage string
@@ -237,8 +240,9 @@ function Package:get_installed_version()
:and_then(
---@param receipt InstallReceipt
function(receipt)
- if receipt.primary_source.id then
- return Purl.parse(receipt.primary_source.id):map(_.prop "version"):ok()
+ local source = receipt:get_source()
+ if source.id then
+ return Purl.parse(source.id):map(_.prop "version"):ok()
else
return Optional.empty()
end
diff --git a/lua/mason-core/receipt.lua b/lua/mason-core/receipt.lua
index 4beeb6aa..d9fe9d88 100644
--- a/lua/mason-core/receipt.lua
+++ b/lua/mason-core/receipt.lua
@@ -3,36 +3,20 @@ local M = {}
---@alias InstallReceiptSchemaVersion
---| '"1.0"'
---| '"1.1"'
+---| '"1.2"'
----@alias InstallReceiptSourceType
----| '"npm"'
----| '"pip3"'
----| '"gem"'
----| '"go"'
----| '"cargo"'
----| '"opam"'
----| '"dotnet"'
----| '"unmanaged"'
----| '"system"'
----| '"jdtls"'
----| '"git"'
----| '"github_tag"'
----| '"github_release"'
----| '"github_release_file"'
-
----@alias InstallReceiptSource {type: InstallReceiptSourceType}
+---@alias InstallReceiptSource {type: RegistryPackageSpecSchema, id: string}
---@class InstallReceiptLinks
---@field bin? table<string, string>
---@field share? table<string, string>
---@field opt? table<string, string>
----@class InstallReceipt<T> : { primary_source: T }
+---@class InstallReceipt
---@field public name string
---@field public schema_version InstallReceiptSchemaVersion
---@field public metrics {start_time:integer, completion_time:integer}
----@field public primary_source InstallReceiptSource
----@field public secondary_sources InstallReceiptSource[]
+---@field public source InstallReceiptSource
---@field public links InstallReceiptLinks
local InstallReceipt = {}
InstallReceipt.__index = InstallReceipt
@@ -45,6 +29,32 @@ function InstallReceipt.from_json(json)
return InstallReceipt.new(json)
end
+function InstallReceipt:get_name()
+ return self.name
+end
+
+function InstallReceipt:get_schema_version()
+ return self.schema_version
+end
+
+---@param version string
+function InstallReceipt:is_schema_min(version)
+ local semver = require "mason-vendor.semver"
+ return semver(self.schema_version) >= semver(version)
+end
+
+---@return InstallReceiptSource
+function InstallReceipt:get_source()
+ if self:is_schema_min "1.2" then
+ return self.source
+ end
+ return self.primary_source --[[@as InstallReceiptSource]]
+end
+
+function InstallReceipt:get_links()
+ return self.links
+end
+
---@async
---@param cwd string
function InstallReceipt:write(cwd)
@@ -54,15 +64,12 @@ function InstallReceipt:write(cwd)
end
---@class InstallReceiptBuilder
----@field private secondary_sources InstallReceiptSource[]
----@field private links InstallReceiptLinks
----@field private epoch_time number
+---@field links InstallReceiptLinks
local InstallReceiptBuilder = {}
InstallReceiptBuilder.__index = InstallReceiptBuilder
function InstallReceiptBuilder.new()
return setmetatable({
- secondary_sources = {},
links = {
bin = vim.empty_dict(),
share = vim.empty_dict(),
@@ -77,21 +84,9 @@ function InstallReceiptBuilder:with_name(name)
return self
end
----@param version InstallReceiptSchemaVersion
-function InstallReceiptBuilder:with_schema_version(version)
- self.schema_version = version
- return self
-end
-
----@param source InstallReceiptSource
-function InstallReceiptBuilder:with_primary_source(source)
- self.primary_source = source
- return self
-end
-
---@param source InstallReceiptSource
-function InstallReceiptBuilder:with_secondary_source(source)
- table.insert(self.secondary_sources, source)
+function InstallReceiptBuilder:with_source(source)
+ self.source = source
return self
end
@@ -128,68 +123,21 @@ end
function InstallReceiptBuilder:build()
assert(self.name, "name is required")
- assert(self.schema_version, "schema_version is required")
assert(self.start_time, "start_time is required")
assert(self.completion_time, "completion_time is required")
- assert(self.primary_source, "primary_source is required")
+ assert(self.source, "source is required")
return InstallReceipt.new {
name = self.name,
- schema_version = self.schema_version,
+ schema_version = "1.2",
metrics = {
start_time = self.start_time,
completion_time = self.completion_time,
},
- primary_source = self.primary_source,
- secondary_sources = self.secondary_sources,
+ source = self.source,
links = self.links,
}
end
----@class InstallReceiptPackageSource
----@field type string
----@field package string
-
----@param type InstallReceiptSourceType
-local function package_source(type)
- ---@param pkg string
- ---@return InstallReceiptPackageSource
- return function(pkg)
- return { type = type, package = pkg }
- end
-end
-
-InstallReceiptBuilder.npm = package_source "npm"
-InstallReceiptBuilder.pip3 = package_source "pip3"
-InstallReceiptBuilder.gem = package_source "gem"
-InstallReceiptBuilder.go = package_source "go"
-InstallReceiptBuilder.dotnet = package_source "dotnet"
-InstallReceiptBuilder.cargo = package_source "cargo"
-InstallReceiptBuilder.composer = package_source "composer"
-InstallReceiptBuilder.opam = package_source "opam"
-InstallReceiptBuilder.luarocks = package_source "luarocks"
-
-InstallReceiptBuilder.unmanaged = { type = "unmanaged" }
-
----@param repo string
----@param release string
-function InstallReceiptBuilder.github_release(repo, release)
- return {
- type = "github_release",
- repo = repo,
- release = release,
- }
-end
-
----@param dependency string
-function InstallReceiptBuilder.system(dependency)
- return { type = "system", dependency = dependency }
-end
-
----@param remote_url string
-function InstallReceiptBuilder.git_remote(remote_url)
- return { type = "git", remote = remote_url }
-end
-
M.InstallReceiptBuilder = InstallReceiptBuilder
M.InstallReceipt = InstallReceipt
diff --git a/lua/mason/ui/instance.lua b/lua/mason/ui/instance.lua
index 92cfc587..fb435a4d 100644
--- a/lua/mason/ui/instance.lua
+++ b/lua/mason/ui/instance.lua
@@ -307,7 +307,7 @@ local function hydrate_detailed_package_state(pkg)
---@param receipt InstallReceipt
function(receipt)
mutate_state(function(state)
- state.packages.states[pkg.name].linked_executables = receipt.links.bin
+ state.packages.states[pkg.name].linked_executables = receipt:get_links().bin
end)
end
)
diff --git a/tests/fixtures/receipts/1.0.json b/tests/fixtures/receipts/1.0.json
new file mode 100644
index 00000000..e16d68ef
--- /dev/null
+++ b/tests/fixtures/receipts/1.0.json
@@ -0,0 +1,23 @@
+{
+ "schema_version": "1.0",
+ "primary_source": {
+ "type": "npm",
+ "package": "@angular/language-server"
+ },
+ "links": {
+ "bin": {
+ "ngserver": "node_modules/.bin/ngserver"
+ }
+ },
+ "metrics": {
+ "start_time": 1694752057715,
+ "completion_time": 1694752066467
+ },
+ "secondary_sources": [
+ {
+ "type": "npm",
+ "package": "typescript"
+ }
+ ],
+ "name": "angular-language-server"
+}
diff --git a/tests/fixtures/receipts/1.1.json b/tests/fixtures/receipts/1.1.json
new file mode 100644
index 00000000..87d6905a
--- /dev/null
+++ b/tests/fixtures/receipts/1.1.json
@@ -0,0 +1,27 @@
+{
+ "schema_version": "1.1",
+ "metrics": {
+ "start_time": 1694752380220,
+ "completion_time": 1694752386830
+ },
+ "links": {
+ "share": {},
+ "opt": {},
+ "bin": {
+ "ngserver": "node_modules/.bin/ngserver"
+ }
+ },
+ "name": "angular-language-server",
+ "primary_source": {
+ "type": "registry+v1",
+ "id": "pkg:npm/%40angular/language-server@16.1.8",
+ "source": {
+ "extra_packages": [
+ "typescript@5.1.3"
+ ],
+ "version": "16.1.8",
+ "package": "@angular/language-server"
+ }
+ },
+ "secondary_sources": []
+}
diff --git a/tests/fixtures/receipts/1.2.json b/tests/fixtures/receipts/1.2.json
new file mode 100644
index 00000000..75a14f09
--- /dev/null
+++ b/tests/fixtures/receipts/1.2.json
@@ -0,0 +1,19 @@
+{
+ "name": "angular-language-server",
+ "links": {
+ "bin": {
+ "ngserver": "node_modules/.bin/ngserver"
+ },
+ "opt": {},
+ "share": {}
+ },
+ "metrics": {
+ "completion_time": 1694752770559,
+ "start_time": 1694752764840
+ },
+ "schema_version": "1.2",
+ "source": {
+ "type": "registry+v1",
+ "id": "pkg:npm/%40angular/language-server@16.1.8"
+ }
+}
diff --git a/tests/mason-core/installer/installer_spec.lua b/tests/mason-core/installer/installer_spec.lua
index 04de82ba..3e291308 100644
--- a/tests/mason-core/installer/installer_spec.lua
+++ b/tests/mason-core/installer/installer_spec.lua
@@ -87,12 +87,11 @@ describe("installer", function()
local receipt = vim.json.decode(arg)
assert.is_true(match.tbl_containing {
name = "dummy",
- primary_source = match.same {
+ source = match.same {
type = handle.package.spec.schema,
id = handle.package.spec.source.id,
},
- secondary_sources = match.same {},
- schema_version = "1.1",
+ schema_version = "1.2",
metrics = match.is_table(),
links = match.same {
bin = { executable = "target" },
diff --git a/tests/mason-core/receipt_spec.lua b/tests/mason-core/receipt_spec.lua
new file mode 100644
index 00000000..05ce1439
--- /dev/null
+++ b/tests/mason-core/receipt_spec.lua
@@ -0,0 +1,86 @@
+local InstallReceipt = require("mason-core.receipt").InstallReceipt
+local fs = require "mason-core.fs"
+
+local function fixture(file)
+ return vim.json.decode(fs.sync.read_file(("./tests/fixtures/receipts/%s"):format(file)))
+end
+
+describe("receipt ::", function()
+ it("should parse 1.0 structures", function()
+ local receipt = InstallReceipt.new(fixture "1.0.json")
+
+ assert.equals("angular-language-server", receipt:get_name())
+ assert.equals("1.0", receipt:get_schema_version())
+ assert.same({ type = "npm", package = "@angular/language-server" }, receipt:get_source())
+ assert.same({
+ bin = {
+ ngserver = "node_modules/.bin/ngserver",
+ },
+ }, receipt:get_links())
+ assert.is_true(receipt:is_schema_min "1.0")
+ end)
+
+ it("should parse 1.1 structures", function()
+ local receipt = InstallReceipt.new(fixture "1.1.json")
+
+ assert.equals("angular-language-server", receipt:get_name())
+ assert.equals("1.1", receipt:get_schema_version())
+ assert.same({
+ type = "registry+v1",
+ id = "pkg:npm/%40angular/language-server@16.1.8",
+
+ source = {
+ extra_packages = { "typescript@5.1.3" },
+ version = "16.1.8",
+ package = "@angular/language-server",
+ },
+ }, receipt:get_source())
+ assert.same({
+ bin = {
+ ngserver = "node_modules/.bin/ngserver",
+ },
+ opt = {},
+ share = {},
+ }, receipt:get_links())
+ assert.is_true(receipt:is_schema_min "1.1")
+ end)
+
+ it("should parse 1.2 structures", function()
+ local receipt = InstallReceipt.new(fixture "1.2.json")
+
+ assert.equals("angular-language-server", receipt:get_name())
+ assert.equals("1.2", receipt:get_schema_version())
+ assert.same({
+ type = "registry+v1",
+ id = "pkg:npm/%40angular/language-server@16.1.8",
+ }, receipt:get_source())
+ assert.same({
+ bin = {
+ ngserver = "node_modules/.bin/ngserver",
+ },
+ opt = {},
+ share = {},
+ }, receipt:get_links())
+ assert.is_true(receipt:is_schema_min "1.2")
+ end)
+
+ describe("schema versions ::", function()
+ it("should check minimum compatibility", function()
+ local receipt_1_0 = InstallReceipt.new { schema_version = "1.0" }
+ local receipt_1_1 = InstallReceipt.new { schema_version = "1.1" }
+ local receipt_1_2 = InstallReceipt.new { schema_version = "1.2" }
+
+ assert.is_true(receipt_1_0:is_schema_min "1.0")
+ assert.is_true(receipt_1_1:is_schema_min "1.0")
+ assert.is_true(receipt_1_2:is_schema_min "1.0")
+
+ assert.is_false(receipt_1_0:is_schema_min "1.1")
+ assert.is_true(receipt_1_1:is_schema_min "1.1")
+ assert.is_true(receipt_1_2:is_schema_min "1.1")
+
+ assert.is_false(receipt_1_0:is_schema_min "1.2")
+ assert.is_false(receipt_1_1:is_schema_min "1.2")
+ assert.is_true(receipt_1_2:is_schema_min "1.2")
+ end)
+ end)
+end)