Skip to content

Commit

Permalink
write install receipts (chipsalliance#379)
Browse files Browse the repository at this point in the history
  • Loading branch information
williamboman authored Jan 3, 2022
1 parent 63f6b21 commit e8530f4
Show file tree
Hide file tree
Showing 51 changed files with 522 additions and 199 deletions.
59 changes: 59 additions & 0 deletions lua/nvim-lsp-installer/core/fetch.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
local log = require "nvim-lsp-installer.log"
local process = require "nvim-lsp-installer.process"
local platform = require "nvim-lsp-installer.platform"

---@param url string The url to fetch.
---@param callback fun(err: string|nil, raw_data: string)
local function fetch(url, callback)
local stdio = process.in_memory_sink()
log.fmt_debug("Fetching URL %s", url)
local on_exit = function(success)
if success then
log.fmt_debug("Successfully fetched URL %s", url)
callback(nil, table.concat(stdio.buffers.stdout, ""))
else
local stderr = table.concat(stdio.buffers.stderr, "")
log.fmt_warn("Failed to fetch URL %s. stderr=%s", url, stderr)
callback(("Failed to fetch url %q.\n%s"):format(url, stderr), nil)
end
end

local job_variants = {
process.lazy_spawn("wget", {
args = { "-nv", "-O", "-", url },
stdio_sink = stdio.sink,
}),
process.lazy_spawn("curl", {
args = { "-fsSL", url },
stdio_sink = stdio.sink,
}),
}

if platform.is_win then
local ps_script = {
"$ProgressPreference = 'SilentlyContinue'",
("Write-Output (iwr -UseBasicParsing -Uri %q).Content"):format(url),
}
table.insert(
job_variants,
1,
process.lazy_spawn("powershell.exe", {
args = { "-NoProfile", "-Command", table.concat(ps_script, ";") },
stdio_sink = stdio.sink,
env = process.graft_env({}, { "PSMODULEPATH" }),
})
)
end

process.attempt {
jobs = job_variants,
on_iterate = function()
log.debug "Flushing stdout/stderr buffers."
stdio.buffers.stdout = {}
stdio.buffers.stderr = {}
end,
on_finish = on_exit,
}
end

return fetch
134 changes: 134 additions & 0 deletions lua/nvim-lsp-installer/core/receipt.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
local M = {}

---@alias InstallerReceiptSource table

---@class InstallReceiptBuilder
---@field private secondary_sources InstallerReceiptSource[]
---@field private epoch_time number
local InstallReceiptBuilder = {}
InstallReceiptBuilder.__index = InstallReceiptBuilder

function InstallReceiptBuilder.new()
return setmetatable({
secondary_sources = {},
}, InstallReceiptBuilder)
end

---@param name string
function InstallReceiptBuilder:with_name(name)
self.name = name
return self
end

---@alias InstallerReceiptSchemaVersion
---| '"1.0"'

---@param version InstallerReceiptSchemaVersion
function InstallReceiptBuilder:with_schema_version(version)
self.schema_version = version
return self
end

---@param source InstallerReceiptSource
function InstallReceiptBuilder:with_primary_source(source)
self.primary_source = source
return self
end

---@param source InstallerReceiptSource
function InstallReceiptBuilder:with_secondary_source(source)
table.insert(self.secondary_sources, source)
return self
end

---@param seconds integer
---@param microseconds integer
local function to_ms(seconds, microseconds)
return (seconds * 1000) + math.floor(microseconds / 1000)
end

---vim.loop.gettimeofday()
---@param seconds integer
---@param microseconds integer
function InstallReceiptBuilder:with_completion_time(seconds, microseconds)
self.completion_time = to_ms(seconds, microseconds)
return self
end

---vim.loop.gettimeofday()
---@param seconds integer
---@param microseconds integer
function InstallReceiptBuilder:with_start_time(seconds, microseconds)
self.start_time = to_ms(seconds, microseconds)
return self
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")
return {
name = self.name,
schema_version = self.schema_version,
metrics = {
start_time = self.start_time,
completion_time = self.completion_time,
},
primary_source = self.primary_source,
secondary_sources = self.secondary_sources,
}
end

---@param type string
local function package_source(type)
---@param package string
return function(package)
return { type = type, package = package }
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.unmanaged = { type = "unmanaged" }

---@param dependency string
function InstallReceiptBuilder.system(dependency)
return { type = "system", dependency = dependency }
end

---@param remote_url string
---@param revision string
function InstallReceiptBuilder.git_remote(remote_url, revision)
return { type = "git", remote = remote_url, revision = revision }
end

---@param ctx ServerInstallContext
---@param opts UseGithubReleaseOpts|nil
function InstallReceiptBuilder.github_release_file(ctx, opts)
opts = opts or {}
return {
type = "github_release_file",
repo = ctx.github_repo,
file = ctx.github_release_file,
release = ctx.requested_server_version,
tag_name_pattern = opts.tag_name_pattern,
}
end

function InstallReceiptBuilder.github_tag(ctx)
return {
type = "github_tag",
repo = ctx.github_repo,
tag = ctx.requested_server_version,
}
end

M.InstallReceiptBuilder = InstallReceiptBuilder

return M
10 changes: 0 additions & 10 deletions lua/nvim-lsp-installer/data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,6 @@ function Data.list_any(list, predicate)
return false
end

---@param data string @The JSON data to decode/deserialize.
---@return table
function Data.json_decode(data)
if vim.json and vim.json.decode then
return vim.json.decode(data)
else
return vim.fn.json_decode(data)
end
end

---@generic T : fun(...)
---@param fn T
---@param cache_key_generator fun(...): string | nil
Expand Down
36 changes: 3 additions & 33 deletions lua/nvim-lsp-installer/installers/composer.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
local path = require "nvim-lsp-installer.path"
local fs = require "nvim-lsp-installer.fs"
local Data = require "nvim-lsp-installer.data"
local installers = require "nvim-lsp-installer.installers"
local std = require "nvim-lsp-installer.installers.std"
local platform = require "nvim-lsp-installer.platform"
Expand All @@ -21,37 +18,10 @@ local function ensure_composer(installer)
}
end

---@param packages string[] @The Gem packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
function M.packages(packages)
return ensure_composer(
---@type ServerInstallerFunction
function(_, callback, context)
local c = process.chain {
cwd = context.install_dir,
stdio_sink = context.stdio_sink,
}

if not (fs.file_exists(path.concat { context.install_dir, "composer.json" })) then
c.run(M.composer_cmd, { "init", "--no-interaction", "--stability=dev" })
c.run(M.composer_cmd, { "config", "prefer-stable", "true" })
end

local pkgs = Data.list_copy(packages or {})
if context.requested_server_version then
-- The "head" package is the recipient for the requested version. It's.. by design... don't ask.
pkgs[1] = ("%s:%s"):format(pkgs[1], context.requested_server_version)
end

c.run(M.composer_cmd, vim.list_extend({ "require" }, pkgs))
c.spawn(callback)
end
)
end

function M.install()
return ensure_composer(
---@type ServerInstallerFunction
function(_, callback, context)
function(_, callback, ctx)
process.spawn(M.composer_cmd, {
args = {
"install",
Expand All @@ -60,8 +30,8 @@ function M.install()
"--optimize-autoloader",
"--classmap-authoritative",
},
cwd = context.install_dir,
stdio_sink = context.stdio_sink,
cwd = ctx.install_dir,
stdio_sink = ctx.stdio_sink,
}, callback)
end
)
Expand Down
73 changes: 16 additions & 57 deletions lua/nvim-lsp-installer/installers/context.lua
Original file line number Diff line number Diff line change
@@ -1,66 +1,15 @@
local Data = require "nvim-lsp-installer.data"
local log = require "nvim-lsp-installer.log"
local process = require "nvim-lsp-installer.process"
local installers = require "nvim-lsp-installer.installers"
local platform = require "nvim-lsp-installer.platform"
local fs = require "nvim-lsp-installer.fs"
local path = require "nvim-lsp-installer.path"
local fetch = require "nvim-lsp-installer.core.fetch"
local Data = require "nvim-lsp-installer.data"

local M = {}

---@param url string @The url to fetch.
---@param callback fun(err: string|nil, raw_data: string)
local function fetch(url, callback)
local stdio = process.in_memory_sink()
log.fmt_debug("Fetching URL %s", url)
local on_exit = function(success)
if success then
log.fmt_debug("Successfully fetched URL %s", url)
callback(nil, table.concat(stdio.buffers.stdout, ""))
else
local stderr = table.concat(stdio.buffers.stderr, "")
log.fmt_warn("Failed to fetch URL %s. stderr=%s", url, stderr)
callback(("Failed to fetch url %q.\n%s"):format(url, stderr), nil)
end
end

local job_variants = {
process.lazy_spawn("wget", {
args = { "-nv", "-O", "-", url },
stdio_sink = stdio.sink,
}),
process.lazy_spawn("curl", {
args = { "-fsSL", url },
stdio_sink = stdio.sink,
}),
}

if platform.is_win then
local ps_script = {
"$ProgressPreference = 'SilentlyContinue'",
("Write-Output (iwr -UseBasicParsing -Uri %q).Content"):format(url),
}
table.insert(
job_variants,
1,
process.lazy_spawn("powershell.exe", {
args = { "-NoProfile", "-Command", table.concat(ps_script, ";") },
stdio_sink = stdio.sink,
env = process.graft_env({}, { "PSMODULEPATH" }),
})
)
end
local list_find_first = Data.list_find_first

process.attempt {
jobs = job_variants,
on_iterate = function()
log.debug "Flushing stdout/stderr buffers."
stdio.buffers.stdout = {}
stdio.buffers.stderr = {}
end,
on_finish = on_exit,
}
end
local M = {}

---@param repo string @The GitHub repo ("username/repo").
function M.use_github_latest_tag(repo)
Expand All @@ -84,13 +33,14 @@ function M.use_github_latest_tag(repo)
return
end

local data = Data.json_decode(raw_data)
local data = vim.json.decode(raw_data)
if vim.tbl_count(data) == 0 then
context.stdio_sink.stderr("No tags found for GitHub repo %s.\n", repo)
callback(false)
return
end
context.requested_server_version = data[1].name
context.github_repo = repo
callback(true)
end)
)
Expand Down Expand Up @@ -123,7 +73,7 @@ function M.use_github_release(repo, opts)
return callback(false)
end

local latest_release = Data.list_find_first(Data.json_decode(response), function(release)
local latest_release = list_find_first(vim.json.decode(response), function(release)
local is_stable_release = not release.prerelease and not release.draft
if opts.tag_name_pattern then
return is_stable_release and release.tag_name:match(opts.tag_name_pattern)
Expand All @@ -138,6 +88,7 @@ function M.use_github_release(repo, opts)
end
log.debug("Resolved latest version", server.name, repo, latest_release.tag_name)
context.requested_server_version = latest_release.tag_name
context.github_repo = repo
callback(true)
end)
)
Expand Down Expand Up @@ -211,6 +162,14 @@ function M.capture(fn)
end
end

---@param fn fun(receipt_builder: InstallReceiptBuilder, ctx: ServerInstallContext)
function M.receipt(fn)
return M.capture(function(ctx)
fn(ctx.receipt, ctx)
return installers.noop
end)
end

---Update the context object.
---@param fn fun(context: ServerInstallContext): ServerInstallerFunction
function M.set(fn)
Expand Down
Loading

0 comments on commit e8530f4

Please sign in to comment.