github / github/copilot-cli

sea-loader.js: lexicographical localeCompare in fi() causes -9 to be selected over -10 / -11 in package cache

未关闭
#4,611 0 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

area:installation
主要语言
Shell
星标
11.2k
派生
1.9k
平均合并
14 小时 16 分钟
30 天内合并 PR
6

描述

Summary

In sea-loader.js, the local version comparison helper fi(e, t) uses e.localeCompare(t) to compare prerelease / build tag suffixes when major/minor/patch segments match.

Because "1.0.81-9".localeCompare("1.0.81-11") returns 1 (lexicographical sorting where '9' > '1'), the bootstrap loader determines that 1.0.81-9 is newer than 1.0.81-10 or 1.0.81-11. Consequently, even after copilot update successfully downloads and extracts 1.0.81-11 into the package cache (%LOCALAPPDATA%\copilot\pkg\win32-x64\1.0.81-11), launching copilot continues to execute the cached 1.0.81-9 version.


Root Cause Analysis

In sea-loader.js:

function fi(e, t) {
  let i = kr(e), r = kr(t);
  if (!i && !r) return 0;
  if (!i) return -1;
  if (!r) return 1;
  for (let o = 0; o < 3; o++) {
    if (i[o] !== r[o]) return i[o] - r[o];
  }
  let s = e.includes("-"), n = t.includes("-");
  return s !== n ? (s ? -1 : 1) : e.localeCompare(t);
}
  1. kr() parses the first 3 dot-delimited integer segments [major, minor, patch].
  2. When comparing 1.0.81-9 and 1.0.81-11, the numeric segments [1, 0, 81] match.
  3. Both strings include -, so s !== n is false.
  4. The fallback is e.localeCompare(t).
  5. "1.0.81-9".localeCompare("1.0.81-11") evaluates to 1 (since '9' > '1').
  6. In mi("index.js", ...):
    i.sort((r, s) => {
      let n = fi(basename(s), basename(r));
      return n !== 0 ? n : ...;
    });
    
    The version list is sorted descending using fi(), placing 1.0.81-9 at the top before 1.0.81-11.
  7. hh() finds 1.0.81-9 and executes its index.js.

Steps to Reproduce
  1. Have both 1.0.81-9 and 1.0.81-11 in %LOCALAPPDATA%\copilot\pkg\win32-x64\.
  2. Run copilot --version.
  3. Observed: Outputs GitHub Copilot CLI 1.0.81-9..
  4. Explicitly passing --prefer-version 1.0.81-11 (copilot --prefer-version 1.0.81-11 --version) outputs GitHub Copilot CLI 1.0.81-11..
  5. Renaming/removing 1.0.81-9 immediately allows default copilot --version to output GitHub Copilot CLI 1.0.81-11..

Suggested Fix

Implement SemVer-compliant prerelease identifier comparison in fi() by splitting the prerelease tag (e.g. on . or -) and comparing numeric identifiers as integers and string identifiers lexicographically:

function comparePrerelease(a, b) {
  const parsePart = (p) => /^\d+$/.test(p) ? parseInt(p, 10) : p;
  const partsA = a.replace(/^[^-]*-/, '').split('.').map(parsePart);
  const partsB = b.replace(/^[^-]*-/, '').split('.').map(parsePart);
  const len = Math.max(partsA.length, partsB.length);
  for (let i = 0; i < len; i++) {
    if (partsA[i] === undefined) return -1;
    if (partsB[i] === undefined) return 1;
    if (partsA[i] === partsB[i]) continue;
    if (typeof partsA[i] === 'number' && typeof partsB[i] === 'number') {
      return partsA[i] - partsB[i];
    }
    return String(partsA[i]).localeCompare(String(partsB[i]));
  }
  return 0;
}

Environment
  • Platform: Windows 11 (win32-x64)
  • Copilot CLI: 1.0.81-11 binary loading 1.0.81-9 cached package

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从 sea-loader.js 中的 fi(e, t) 开始,然后跟踪它在 mi() 和 hh() 中的使用。使用缓存版本 1.0.81-9 和 1.0.81-11 重现问题,并比较 copilot --version 与 --prefer-version。当默认 loader 选择 1.0.81-11 而不是按字典序更高的 1.0.81-9 时,即表示完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript
领域
cli
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
活跃
描述清晰度
基本清楚
新手友好度
72/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。