diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index b6179b1..0670dc0 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -46,6 +46,19 @@ jobs: New-Item -ItemType Directory -Force -Path $pluginDir New-Item -ItemType Junction -Path "$pluginDir\yarn" -Target $PWD + # Validate version listing + - name: Validate mise ls-remote works + shell: bash + run: | + echo "Listing available yarn versions..." + mise ls-remote yarn | head -20 + echo "" + echo "Checking for v1 versions..." + mise ls-remote yarn | grep -E '^1\.' | head -5 || (echo "ERROR: No v1 versions found!" && exit 1) + echo "" + echo "Checking for v2+ versions..." + mise ls-remote yarn | grep -E '^[234]\.' | head -5 || (echo "ERROR: No v2+ versions found!" && exit 1) + # Test Yarn v1 (Classic) - name: Test Yarn v1 installation shell: bash diff --git a/hooks/available.lua b/hooks/available.lua index d1637e2..5520b0a 100644 --- a/hooks/available.lua +++ b/hooks/available.lua @@ -1,55 +1,117 @@ --- List all available versions PLUGIN = {} -local function execCommand(cmd) - local handle = io.popen(cmd .. " 2>/dev/null") - if handle then - local result = handle:read("*a") - handle:close() - return result - end - return "" +local function fetch_github_tags(repo_url) + -- Use git ls-remote to get tags + local cmd = 'git ls-remote --refs --tags "' .. repo_url .. '"' + + -- Detect Windows + local is_windows = package.config:sub(1,1) == '\\' + + -- Redirect stderr appropriately for the platform + if is_windows then + cmd = cmd .. " 2>NUL" + else + cmd = cmd .. " 2>/dev/null" + end + + local handle = io.popen(cmd) + if not handle then + return {} + end + + local result = handle:read("*a") + handle:close() + + -- If result is empty or nil, return empty table + if not result or result == "" then + return {} + end + + local tags = {} + for line in result:gmatch("[^\r\n]+") do + -- Extract tag name from refs/tags/... + local tag = line:match("refs/tags/(.+)$") + if tag then + table.insert(tags, tag) + end + end + + return tags +end + +local function version_compare(a, b) + -- Simple version comparison for sorting + local function parse_version(v) + local parts = {} + for part in string.gmatch(v, "[^%.]+") do + table.insert(parts, tonumber(part) or 0) + end + return parts + end + + local a_parts = parse_version(a) + local b_parts = parse_version(b) + + for i = 1, math.max(#a_parts, #b_parts) do + local a_val = a_parts[i] or 0 + local b_val = b_parts[i] or 0 + if a_val ~= b_val then + return a_val > b_val -- Descending order + end + end + + return false end function PLUGIN:Available(ctx) local versions = {} - -- Get Yarn Berry versions (v2.x+) FIRST in DESCENDING order - -- When mise reverses the list, these will appear LAST in ASCENDING order - local berry_output = execCommand([[ - git ls-remote --refs --tags "https://github.com/yarnpkg/berry.git" | - grep '@yarnpkg/cli' | - sed -E 's|^.+refs/tags/@yarnpkg/cli/||g' | - sort -rV - ]]) - - for version in berry_output:gmatch("[^\n]+") do - if version and version ~= "" then - table.insert(versions, { - version = version - }) + -- Get Yarn Berry versions (v2.x+) + local berry_tags = fetch_github_tags("https://github.com/yarnpkg/berry.git") + local berry_versions = {} + + for _, tag in ipairs(berry_tags) do + -- Extract version from @yarnpkg/cli/X.X.X format + local version = tag:match("@yarnpkg/cli/(.+)$") + if version then + table.insert(berry_versions, version) end end - -- Get Yarn Classic versions (v1.x) SECOND in DESCENDING order - -- When mise reverses the list, these will appear FIRST in ASCENDING order - local classic_output = execCommand([[ - git ls-remote --refs --tags "https://github.com/yarnpkg/yarn.git" | - sed -E 's|^.+refs/tags/||g' | - grep -E '^v' | - sed -E 's|^v||g' | - grep -Ev '^0\.' | - sort -rV - ]]) - - for version in classic_output:gmatch("[^\n]+") do - if version and version ~= "" then - table.insert(versions, { - version = version - }) + -- Sort Berry versions in descending order + table.sort(berry_versions, version_compare) + + -- Add Berry versions to the list + for _, version in ipairs(berry_versions) do + table.insert(versions, { + version = version + }) + end + + -- Get Yarn Classic versions (v1.x) + local classic_tags = fetch_github_tags("https://github.com/yarnpkg/yarn.git") + local classic_versions = {} + + for _, tag in ipairs(classic_tags) do + -- Remove 'v' prefix if present + local version = tag:match("^v(.+)$") or tag + -- Only include 1.x versions (exclude 0.x) + if version:match("^1%.") then + table.insert(classic_versions, version) end end + -- Sort Classic versions in descending order + table.sort(classic_versions, version_compare) + + -- Add Classic versions to the list + for _, version in ipairs(classic_versions) do + table.insert(versions, { + version = version + }) + end + return versions end diff --git a/hooks/post_install.lua b/hooks/post_install.lua index 0870409..ec319f0 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -2,13 +2,17 @@ PLUGIN = {} local function download_file(url, output_path) - -- Try wget first, then curl - local wget_cmd = "wget -q -O " .. output_path .. " " .. url .. " 2>/dev/null" - local curl_cmd = "curl -sSL -o " .. output_path .. " " .. url .. " 2>/dev/null" + -- Detect Windows + local is_windows = package.config:sub(1,1) == '\\' + local stderr_redirect = is_windows and " 2>NUL" or " 2>/dev/null" - if os.execute(wget_cmd) == 0 then + -- Try curl first (more likely to be available on Windows via Git Bash) + local curl_cmd = "curl -sSL -o " .. output_path .. " " .. url .. stderr_redirect + local wget_cmd = "wget -q -O " .. output_path .. " " .. url .. stderr_redirect + + if os.execute(curl_cmd) == 0 then return true - elseif os.execute(curl_cmd) == 0 then + elseif os.execute(wget_cmd) == 0 then return true end return false @@ -44,17 +48,54 @@ function PLUGIN:PostInstall(ctx) -- Yarn Berry (v2.x+) - download single JS file local yarn_url = "https://repo.yarnpkg.com/" .. version .. "/packages/yarnpkg-cli/bin/yarn.js" - -- Create bin directory - os.execute("mkdir -p " .. install_path .. "/bin") + -- Detect Windows + local is_windows = package.config:sub(1,1) == '\\' + + -- Create bin directory (cross-platform) + local bin_dir = install_path .. "/bin" + if is_windows then + os.execute('mkdir "' .. bin_dir .. '" 2>NUL') + else + os.execute("mkdir -p " .. bin_dir) + end -- Download yarn.js - local yarn_file = install_path .. "/bin/yarn" - if not download_file(yarn_url, yarn_file) then + local yarn_js_file = bin_dir .. "/yarn.js" + if not download_file(yarn_url, yarn_js_file) then error("Failed to download Yarn v2+") end - -- Make executable - os.execute("chmod +x " .. yarn_file) + -- Create wrapper script + if is_windows then + -- Create yarn.cmd wrapper for Windows + local yarn_cmd = bin_dir .. "/yarn.cmd" + local cmd_file = io.open(yarn_cmd, "w") + if cmd_file then + cmd_file:write("@echo off\n") + cmd_file:write('node "%~dp0yarn.js" %*\n') + cmd_file:close() + end + + -- Also create yarn without extension for Git Bash + local yarn_sh = bin_dir .. "/yarn" + local sh_file = io.open(yarn_sh, "w") + if sh_file then + sh_file:write("#!/bin/sh\n") + sh_file:write('exec node "$(dirname "$0")/yarn.js" "$@"\n') + sh_file:close() + end + else + -- Create shell wrapper for Unix + local yarn_file = bin_dir .. "/yarn" + local wrapper_file = io.open(yarn_file, "w") + if wrapper_file then + wrapper_file:write("#!/bin/sh\n") + wrapper_file:write('exec node "$(dirname "$0")/yarn.js" "$@"\n') + wrapper_file:close() + end + -- Make executable + os.execute("chmod +x " .. yarn_file) + end end return {} diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index 6f6b202..ea9152b 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -9,9 +9,12 @@ function PLUGIN:PreInstall(ctx) -- Yarn Classic (v1.x) - return tarball URL for mise to handle local archive_url = "https://classic.yarnpkg.com/downloads/" .. version .. "/yarn-v" .. version .. ".tar.gz" - -- Note about GPG verification - if os.getenv("MISE_YARN_SKIP_GPG") == nil then - local gpg_check = io.popen("command -v gpg 2>/dev/null") + -- Note about GPG verification (skip on Windows) + local is_windows = package.config:sub(1,1) == '\\' + if os.getenv("MISE_YARN_SKIP_GPG") == nil and not is_windows then + local stderr_redirect = " 2>/dev/null" + + local gpg_check = io.popen("command -v gpg" .. stderr_redirect) local has_gpg = gpg_check and gpg_check:read("*a"):match("%S") if gpg_check then gpg_check:close() end diff --git a/metadata.lua b/metadata.lua index 7312813..70822f9 100644 --- a/metadata.lua +++ b/metadata.lua @@ -3,10 +3,10 @@ PLUGIN = { name = "yarn", - author = "vfox-yarn contributors", + author = "mise-plugins", version = "0.1.0", - description = "Yarn package manager plugin for vfox", - homepage = "https://github.com/version-fox/vfox-yarn", + description = "Yarn package manager plugin for mise", + homepage = "https://github.com/mise-plugins/mise-yarn", license = "MIT", notes = [[ This plugin installs Yarn package manager.