Skip to content

Commit

Permalink
feat(github): use proxy API for fetching latest release (#521)
Browse files Browse the repository at this point in the history
This uses a globally distributed, edge-cached, proxy [1] for a very common
touchpoint with the GitHub API. This is already done for fetching
the latest tag, now expanding to latest release as well.

[1]: https://github.com/williamboman/github-api-proxy
  • Loading branch information
williamboman authored Oct 6, 2022
1 parent e93a299 commit fb73733
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 78 deletions.
60 changes: 22 additions & 38 deletions lua/mason-core/managers/github/client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ end

M.api_call = api_call

---@param path string
---@param opts { params: table<string, any>? }?
---@return Result # JSON decoded response.
local function proxy_api_call(path, opts)
if opts and opts.params then
local params = _.join("&", _.map(_.join "=", _.sort_by(_.head, _.to_pairs(opts.params))))
path = ("%s?%s"):format(path, params)
end
-- https://github.com/williamboman/github-api-proxy
return fetch(("https://github-api-proxy.redwill.se/%s"):format(path))
end

---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<GitHubRelease[]>
Expand All @@ -51,46 +63,20 @@ function M.fetch_release(repo, tag_name)
end)
end

---@param opts {include_prerelease: boolean, tag_name_pattern: string}
function M.release_predicate(opts)
local is_not_draft = _.prop_eq("draft", false)
local is_not_prerelease = _.prop_eq("prerelease", false)
local tag_name_matches = _.prop_satisfies(_.matches(opts.tag_name_pattern), "tag_name")

return _.all_pass {
_.if_else(_.always(opts.include_prerelease), _.T, is_not_prerelease),
_.if_else(_.always(opts.tag_name_pattern), tag_name_matches, _.T),
is_not_draft,
}
end

---@alias FetchLatestGithubReleaseOpts {tag_name_pattern:string?, include_prerelease: boolean}
---@alias FetchLatestGithubReleaseOpts {include_prerelease: boolean}

---@async
---@param repo string The GitHub repo ("username/repo").
---@param opts FetchLatestGithubReleaseOpts?
---@return Result # Result<GitHubRelease>
function M.fetch_latest_release(repo, opts)
opts = opts or {
tag_name_pattern = nil,
include_prerelease = false,
}
return M.fetch_releases(repo):map_catching(
---@param releases GitHubRelease[]
function(releases)
local is_stable_release = M.release_predicate(opts)
---@type GitHubRelease|nil
local latest_release = _.find_first(is_stable_release, releases)

if not latest_release then
log.fmt_info("Failed to find latest release. repo=%s, opts=%s", repo, opts)
error "Failed to find latest release."
end

log.fmt_debug("Resolved latest version repo=%s, tag_name=%s", repo, latest_release.tag_name)
return latest_release
end
)
opts = opts or { include_prerelease = false }
local path = ("api/repo/%s/latest-release"):format(repo)
return proxy_api_call(path, {
params = {
include_prerelease = opts.include_prerelease and "true" or "false",
},
}):map_catching(vim.json.decode)
end

---@async
Expand All @@ -107,10 +93,8 @@ end
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<string> The latest tag name.
function M.fetch_latest_tag(repo)
-- https://github.com/williamboman/vercel-github-api-latest-tag-proxy
return fetch(("https://latest-github-tag.redwill.se/api/repo/%s/latest-tag"):format(repo))
:map_catching(vim.json.decode)
:map(_.prop "tag")
local path = ("api/repo/%s/latest-tag"):format(repo)
return proxy_api_call(path):map_catching(vim.json.decode):map(_.prop "tag")
end

---@async
Expand Down
2 changes: 1 addition & 1 deletion lua/mason-core/managers/github/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function M.check_outdated_primary_package_release(receipt)
if source.type ~= "github_release" and source.type ~= "github_release_file" then
return Result.failure "Receipt does not have a primary source of type (github_release|github_release_file)."
end
return client.fetch_latest_release(source.repo, { tag_name_pattern = source.tag_name_pattern }):map_catching(
return client.fetch_latest_release(source.repo):map_catching(
---@param latest_release GitHubRelease
function(latest_release)
if source.release ~= latest_release.tag_name then
Expand Down
39 changes: 0 additions & 39 deletions tests/mason-core/managers/github_client_spec.lua
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
local spy = require "luassert.spy"
local stub = require "luassert.stub"
local client = require "mason-core.managers.github.client"
local spawn = require "mason-core.spawn"
local Result = require "mason-core.result"

describe("github client", function()
---@type GitHubRelease
local release = {
tag_name = "v0.1.0",
prerelease = false,
draft = false,
assets = {},
}

local function stub_release(mock)
return setmetatable(mock, { __index = release })
end

it("should identify stable prerelease", function()
local predicate = client.release_predicate {
include_prerelease = false,
}

assert.is_false(predicate(stub_release { prerelease = true }))
assert.is_true(predicate(stub_release { prerelease = false }))
end)

it("should identify stable release with tag name pattern", function()
local predicate = client.release_predicate {
tag_name_pattern = "^lsp%-server.*$",
}

assert.is_false(predicate(stub_release { tag_name = "v0.1.0" }))
assert.is_true(predicate(stub_release { tag_name = "lsp-server-v0.1.0" }))
end)

it("should identify stable release", function()
local predicate = client.release_predicate {}

assert.is_true(predicate(stub_release { tag_name = "v0.1.0" }))
assert.is_false(predicate(stub_release { prerelease = true }))
assert.is_false(predicate(stub_release { draft = true }))
end)

it("should provide query parameters in api calls", function()
stub(spawn, "gh")
spawn.gh.returns(Result.success { stdout = "response data" })
Expand Down

0 comments on commit fb73733

Please sign in to comment.