diff --git a/.luaurc b/.luaurc index 94fa340..086d9f1 100644 --- a/.luaurc +++ b/.luaurc @@ -1,6 +1,3 @@ { - "languageMode": "strict", - "lint": { - "*": false - } + "languageMode": "strict" } \ No newline at end of file diff --git a/.lune/update_tools.luau b/.lune/update_tools.luau new file mode 100644 index 0000000..fe2e467 --- /dev/null +++ b/.lune/update_tools.luau @@ -0,0 +1,127 @@ +local serde = require("@lune/serde") +local stdio = require("@lune/stdio") +local process = require("@lune/process") +local DateTime = require("@lune/datetime") + +local types = require("../toolchainlib/src/utils/result_option_conv") +local Option = types.Option +type Option = types.Option +local pathfs = require("../lune_packages/pathfs") +local Github = require("../toolchainlib/src/github") + +local function isoDateToTimestamp(isoDate: string): number + return DateTime.fromIsoDate(isoDate).unixTimestamp +end + +local function stripLeadingVersion(version: string): string + local stripped = string.gsub(version, "^v", "") + return stripped +end + +local function confirmAndClear(msg: string, default: boolean?): boolean + local yes = stdio.prompt("confirm", msg, default) + stdio.write( + -- Move to the previous line, clear it, move cursor to start of line, + -- and show cursor (if hidden) + "\x1b[A\x1b[K\x1b[0G\x1b[?25h" + ) + + return yes +end + +local INFO_PREFIX = `{stdio.color("green")}{stdio.style("bold")}info{stdio.color("reset")}:` +local ERROR_PREFIX = `{stdio.color("red")}{stdio.style("bold")}error{stdio.color("reset")}:` + +local function error(msg: string): never + stdio.ewrite(`{ERROR_PREFIX} {msg}\n`) + return process.exit(1) +end + +local function assert(expr: boolean, msg: string) + if not expr then + error(msg) + end +end + +local BINS_SRC_DIR = pathfs.getAbsolutePathOf(pathfs.Path.from("bins")) + +for _, binSrc in pathfs.readDir(BINS_SRC_DIR) do + local absPath = BINS_SRC_DIR:join(binSrc) + local binEntrypoint = absPath:join("init.luau") + local manifestPath = absPath:join("pesde.toml") + + -- Make sure our constructed entrypoint and manifest paths exist + assert( + pathfs.isFile(binEntrypoint) and pathfs.isFile(manifestPath), + "Either binary entrypoint or manifest not found" + ) + + local manifestContents = pathfs.readFile(manifestPath) + -- TODO: Type this + local manifest = serde.decode("toml", manifestContents) + local entrypointContents = pathfs.readFile(binEntrypoint) + + local repoName = string.match(entrypointContents, 'require%("./lune_packages/core"%)%("([^"]+)"') + local version = manifest.version + + -- Make sure we have a repo name and version + assert(repoName and version, `Failed to get repo name and entrypoint for tool {binSrc}`) + + local gh = Github.new(repoName :: string, Option.None :: Option) + local transactions = gh:queueTransactions({ "FetchReleases" }) + + -- Fetch releases, and order them by published date + local releases = transactions[1]:unwrap() :: Github.GithubReleases + table.sort(releases, function(a, b) + return isoDateToTimestamp(a.published_at) < isoDateToTimestamp(b.published_at) + end) + + -- Filter for only versions which are after the current version + local releasesAfter = {} + local versionIdx = math.huge + for idx, release in releases do + if release.tag_name == version then + versionIdx = idx + continue + end + + if idx > versionIdx then + releasesAfter[release.tag_name] = release + end + end + + for newVersion, newRelease in releasesAfter do + print( + `{INFO_PREFIX} Found new tool release {stdio.style("bold")}{binSrc}{stdio.style("reset")}@{stdio.style( + "dim" + )}{newVersion}{stdio.style("reset")}` + ) + + -- HACK: To prevent messing with our existing toml ordering and formatting + -- we just replace the old version field string with the new version field + -- string + local updatedManifest = string.gsub( + manifestContents, + -- Old version field string: + serde.encode("toml", { version = manifest.version }), + -- New version field string: + serde.encode("toml", { version = stripLeadingVersion(newVersion) }), + -- Only replace the first occurrence to be safe + 1 + ) + + local toWrite = table.find(process.args, "--yes") + or table.find(process.args, "-y") + or confirmAndClear(`Update manifest for {binSrc}?`, false) + + if toWrite then + print( + `{INFO_PREFIX} Updated manifest {stdio.style("dim")}{manifestPath:stripPrefix(pathfs.cwd)}{stdio.style( + "reset" + )}` + ) + + pathfs.writeFile(manifestPath, updatedManifest) + end + end +end diff --git a/pesde.lock b/pesde.lock index 4148463..256165f 100644 --- a/pesde.lock +++ b/pesde.lock @@ -23,6 +23,24 @@ lune = "bins/stylua" [workspace."pesde/toolchainlib"] lune = "toolchainlib" +[graph."jiwonz/pathfs"."0.1.0 lune"] +direct = ["pathfs", { name = "jiwonz/pathfs", version = "^0.1.0" }, "standard"] +resolved_ty = "standard" + +[graph."jiwonz/pathfs"."0.1.0 lune".target] +environment = "lune" +lib = "init.luau" + +[graph."jiwonz/pathfs"."0.1.0 lune".pkg_ref] +ref_ty = "pesde" +name = "jiwonz/pathfs" +version = "0.1.0" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."jiwonz/pathfs"."0.1.0 lune".pkg_ref.target] +environment = "lune" +lib = "init.luau" + [graph."lukadev_0/option"."1.2.0 lune"] direct = ["option", { name = "lukadev_0/option", version = "^1.2.0" }, "standard"] resolved_ty = "standard" diff --git a/pesde.toml b/pesde.toml index 45fda03..9623f2f 100644 --- a/pesde.toml +++ b/pesde.toml @@ -9,6 +9,7 @@ environment = "lune" [dependencies] option = { name = "lukadev_0/option", version = "^1.2.0" } +pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" } [indices] default = "https://github.com/daimond113/pesde-index" diff --git a/toolchainlib/src/github.luau b/toolchainlib/src/github.luau index 7d359e5..d636d45 100644 --- a/toolchainlib/src/github.luau +++ b/toolchainlib/src/github.luau @@ -18,7 +18,28 @@ export type Config = { authToken: Option, retries: Option, } -export type GithubOperation = "FetchReleases" | "GetMetadata" | "GetActionArtifacts" +export type GithubOperation = + "FetchReleases" + | "GetMetadata" + | "GetActionArtifacts" + | { type: "Custom", payload: net.FetchParams } + +export type GithubRelease = { + tag_name: string, + created_at: string, + published_at: string, + prerelease: boolean, + draft: boolean, + assets: { + { + name: string, + browser_download_url: string, + size: number, + content_type: string, + } + }, +} +export type GithubReleases = { GithubRelease } local API_BASE_URL = "https://api.github.com" local DEFAULT_MAX_RETRIES = 5 @@ -60,6 +81,10 @@ function Github.queueTransactions(self: Github, operations: { GithubOperation }) req.method = "GET" end + if typeof(operation) == "table" and operation.type == "Custom" then + req = operation.payload + end + -- TODO: Other methods table.insert(queue, function(retries: number) diff --git a/toolchainlib/src/init.luau b/toolchainlib/src/init.luau index 540a20e..6b9c43a 100644 --- a/toolchainlib/src/init.luau +++ b/toolchainlib/src/init.luau @@ -31,21 +31,8 @@ export type ToolId = { version: Option, } -export type GithubReleases = { - { - tag_name: string, - prerelease: boolean, - draft: boolean, - assets: { - { - name: string, - browser_download_url: string, - size: number, - content_type: string, - } - }, - } -} +-- TODO: Remove this in a breaking change +export type GithubReleases = Github.GithubReleases local WARN_PREFIX = `{stdio.color("yellow")}{stdio.style("bold")}warn{stdio.color("reset")}:`