Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,16 @@ jobs:
strategy:
fail-fast: false
matrix:
# Windows validation is temporarily paused until its checks are reliable.
os: [ubuntu-latest, macos-latest]
# Windows runs a focused contract; full capability coverage remains on Linux/macOS.
os: [ubuntu-latest, macos-latest, windows-latest]
node: ["24"]
include:
- os: ubuntu-latest
node: "24"
profile: full
- os: windows-latest
node: "24"
profile: windows
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ pnpm install --frozen-lockfile
pnpm verify
```

`pnpm verify` runs the complete gate list in the same order as GitHub CI. Normal PR and main-branch checks use Node.js 24 on Linux and macOS. Windows CI is temporarily paused while its checks are made reliable. The Linux job runs this full profile; the macOS job uses `pnpm verify --profile platform`, which omits only the duplicate TypeScript compiler check. Both jobs still export source, build, and test their own platform artifacts. Gates that depend on platform behaviour are selected by platform rather than skipped silently; run `pnpm verify --list` or `pnpm verify --profile platform --list` to inspect either plan. Individual gates remain available as their own scripts, such as `pnpm typecheck` or `pnpm test:byok`, while you iterate.
`pnpm verify` runs the complete gate list in the same order as GitHub CI. Normal PR and main-branch checks use Node.js 24 on Linux and macOS. The Linux job runs the full profile; the macOS job uses `pnpm verify --profile platform`, which omits only the duplicate TypeScript compiler check. Windows runs the focused `pnpm verify --profile windows` contract on PRs; the profile is Windows-only and fails closed elsewhere. It checks source inventory, release tooling, build boundaries, artifacts, and Windows-specific tests without running the full capability suite. Gates that depend on platform behaviour are selected by platform rather than skipped silently; run `pnpm verify --list`, `pnpm verify --profile platform --list`, or `pnpm verify --profile windows --list` to inspect each plan. Individual gates remain available as their own scripts, such as `pnpm typecheck` or `pnpm test:byok`, while you iterate.

CI writes per-gate timing and exit metadata to the Job Summary and a seven-day `verification-<os>-node-<version>-<attempt>` artifact. For a local report, set `MCODE_VERIFY_REPORT_DIR` to a directory outside the repository. Reports distinguish `PASS`, `FAIL`, intentional `SKIP`, and `NOT_RUN` after a failure. JSON is checkpointed before and after each gate; a cancelled run may leave `RUNNING`, which is not a pass. If installation fails before verification starts, no verification report is available. Reports do not collect command output, environment variables, or runtime data; read the corresponding gate's job log for failure details, including the existing bounded BYOK timeout diagnostics. CI jobs have a 15-minute verification limit and a 10-minute release-audit limit.

Existing README files, `CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`, Markdown under `docs/`, and media directly under `docs/assets/` use the `docs` profile when they are the only changed paths. That profile checks the source inventory and generated paths, exports the committed source, and tests release tooling. History and source-snapshot secret scans still run; platform builds and distribution scans are skipped. Mixed changes, unknown paths, missing comparisons, and any `release/` inventory change get full CI. Documentation-only changes skip the platform matrix entirely. The `verification` aggregate check always runs and rejects failed, cancelled, or unexpectedly skipped jobs. Use it together with `source-history-artifact` as required checks when configuring branch protection; this repository's automation does not change administrative settings.

`Node compatibility` runs weekly and on demand against macOS and Linux with Node 22.19.0, 24.2.0, 25 and 26. Windows is also temporarily excluded from this matrix. It does not run automatically on PRs. Dispatch it on the selected branch for changes to supported Node versions, native dependencies or compatibility-sensitive verification tooling, and before a source release. This covers the minimum versions of the two supported ranges and the additional supported majors. Deferring those versions from ordinary PR checks can delay regression discovery; a known failure in a supported version still needs resolution before release. Dependabot proposes weekly Actions and npm updates, grouping Actions and development-tool minor/patch updates. External Actions use reviewed full commit SHAs, while local actions and reusable workflows come from the same checked-out revision.
`Node compatibility` runs weekly and on demand against macOS and Linux with Node 22.19.0, 24.2.0, 25 and 26. Windows remains excluded from this matrix; the PR Windows contract is a focused Node 24 check, not a substitute for the full compatibility matrix. It does not run automatically on PRs. Dispatch it on the selected branch for changes to supported Node versions, native dependencies or compatibility-sensitive verification tooling, and before a source release. This covers the minimum versions of the two supported ranges and the additional supported majors. Deferring those versions from ordinary PR checks can delay regression discovery; a known failure in a supported version still needs resolution before release. Dependabot proposes weekly Actions and npm updates, grouping Actions and development-tool minor/patch updates. External Actions use reviewed full commit SHAs, while local actions and reusable workflows come from the same checked-out revision.

Source candidates are requested independently through the `Source candidate` workflow; ordinary PRs and main pushes do not produce them. Its Linux/macOS archive validation is described in [Releasing](docs/releasing.md). `CLI release` builds npm-installable tar.gz packages from version tags and validates the same archive before attaching it to a GitHub Release. Its `package` verification profile tests installation of an existing archive; it does not replace full source verification. npm registry and official installer publication remain separate release processes.

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ The [small, reproducible project](examples/clamp) is the same task used in the d

## Build from source

To develop MCode or run this source checkout, you need Git, **Node.js 22.19+ (22.x), 24.2+ (24.x), 25, or 26**, and **pnpm 9.12.0**.

To develop MCode or run this source checkout, you need Git, **Node.js 22.19+ (22.x), 24.2+ (24.x), 25, or 26**, and **pnpm 9.12.0**. On Windows, keep the checkout on a local NTFS volume and outside cloud-synced folders; the preflight command below checks the volume before pnpm creates workspace links.
```bash
git clone https://github.com/MiniMax-AI/minimax-code.git
cd minimax-code
node scripts/check-windows-source-location.mjs
pnpm install --frozen-lockfile
pnpm build
pnpm mcode
Expand Down
4 changes: 2 additions & 2 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ profile 使用 `~/.minimax-<profile>`;`MINIMAX_DATA_DIR` 或 `MAVIS_DATA_DIR`

## 从源码构建

开发 MCode 或运行本仓库源码需要 Git、Node.js **22.19+(22 系列)、24.2+(24 系列)、25 或 26**,以及 **pnpm 9.12.0**。

开发 MCode 或运行本仓库源码需要 Git、Node.js **22.19+(22 系列)、24.2+(24 系列)、25 或 26**,以及 **pnpm 9.12.0**。在 Windows 上,请将源码放在本地 NTFS 卷上,并避开云同步目录;下面的预检命令会在 pnpm 创建 workspace link 前检查卷类型。
```bash
git clone https://github.com/MiniMax-AI/minimax-code.git
cd minimax-code
node scripts/check-windows-source-location.mjs
pnpm install --frozen-lockfile
pnpm build
pnpm mcode
Expand Down
3 changes: 3 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ This guide builds the 0.4.12 source preview. Workspace/local build manifests rem

For a source build, you need Git, Node.js 22.19+ (22.x), 24.2+ (24.x), 25, or 26, and pnpm 9.12.0. Regular CI uses Node.js 24 across Linux and macOS. The weekly and manual compatibility matrix covers Node.js 22.19.0, 24.2.0, 25, and 26 on both platforms. Windows CI and source-candidate validation are temporarily paused while their checks are made reliable. Initial installation and build require access to public npm.

On Windows, check out this repository on a local NTFS volume before running `pnpm install`. The repository uses pnpm workspace links for vendored packages, and those links require NTFS junctions. FAT32/exFAT volumes, network shares, and other non-local Windows volumes cannot create the required junctions. The preflight command below verifies the volume and stops with a clear message before pnpm creates workspace links; run it immediately before `pnpm install`. A local NTFS volume can still contain a cloud-synced folder, which the preflight cannot identify reliably; keep the checkout outside OneDrive, Google Drive, Dropbox, and similar synced folders.

Node 24.0 and 24.1 are unsupported: their bundled libuv can return inconsistent Windows file identity metadata, causing safe configuration reads to fail. [Node 24.2.0](https://nodejs.org/en/blog/release/v24.2.0) includes libuv 1.51.0 with the [upstream fix](https://github.com/libuv/libuv/commit/82cdfb75f). Use a current patch release of a supported Node line.

```bash
git clone https://github.com/MiniMax-AI/minimax-code.git
cd minimax-code
corepack enable
corepack prepare pnpm@9.12.0 --activate
node scripts/check-windows-source-location.mjs
pnpm install --frozen-lockfile
pnpm build
pnpm mcode --help
Expand Down
2 changes: 1 addition & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Before npm or installer distribution, separately validate the published package

The `Source candidate` workflow exports the selected commit without Git history, verifies its receipt, and scans both repository history and the extracted source. Linux and macOS runners authenticate the same archive, install from public npm into fresh stores, and run the archive verification profile.

Windows validation is temporarily paused across source verification, Node compatibility, and source candidates. Candidate reports cover only Linux and macOS; a successful candidate does not establish Windows acceptance. Restore the Windows workflow matrices and the required report set in `scripts/source-candidate.mjs` together when Windows checks are reliable again.
Windows full validation is temporarily paused for Node compatibility and source candidates. Ordinary pull requests run a focused Windows source-verification contract, but candidate reports still cover only Linux and macOS; a successful candidate does not establish Windows acceptance. Restore the Windows compatibility/source-candidate matrices and the required report set in `scripts/source-candidate.mjs` together when those checks are reliable again.

After both platform jobs pass, the workflow creates a `source-candidate-<full-SHA>` artifact containing:

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"test:status-contract": "node scripts/run-vitest-suite.mjs status-contract",
"check:source": "node scripts/source-inventory.mjs",
"test:capabilities": "node scripts/run-vitest-suite.mjs capability",
"test:windows": "node scripts/run-vitest-suite.mjs windows",
"test:artifact": "node --test test/public-artifact.test.mjs",
"test:release-tools": "node --test test/source-sync.test.mjs"
},
Expand Down
14 changes: 9 additions & 5 deletions packages/tui/test/unit/wsl-attachment-paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ vi.mock("node:child_process", async (importOriginal) => ({
let workspaceDir: string;
let imagePath: string;
const windowsPath = String.raw`D:\Users\demo\Documents\Screen shots\截图.png`;
const skipWslConversion = process.platform === "win32";

beforeEach(async () => {
host.platform.mockReturnValue("linux");
Expand All @@ -48,8 +49,11 @@ afterEach(async () => {
await rm(workspaceDir, { recursive: true, force: true });
});

// The mocked wslpath output is the host temp path. Native Windows correctly
// rejects that as a Linux absolute path, so only WSL conversion cases skip there;
// native path handling remains covered.
describe("WSL attachment paths", () => {
it.each([
it.skipIf(process.platform === "win32").each([
windowsPath,
`"${windowsPath}"`,
`'${windowsPath}'`,
Expand Down Expand Up @@ -91,7 +95,7 @@ describe("WSL attachment paths", () => {
},
);

it("resolves headless --file through the same conversion before realpath", async () => {
it.skipIf(skipWslConversion)("resolves headless --file through the same conversion before realpath", async () => {
const invocation = await resolveTuiExecInvocation(
"describe",
{ cwd: workspaceDir, file: [windowsPath] },
Expand All @@ -109,7 +113,7 @@ describe("WSL attachment paths", () => {
expect(host.executeFile).toHaveBeenCalledOnce();
});

it.each(["WSL_DISTRO_NAME", "WSL_INTEROP", "WSLENV"])(
it.skipIf(skipWslConversion).each(["WSL_DISTRO_NAME", "WSL_INTEROP", "WSLENV"])(
"detects WSL via %s even without a Microsoft kernel name",
async (name) => {
host.release.mockReturnValue("custom-kernel");
Expand Down Expand Up @@ -156,7 +160,7 @@ describe("WSL attachment paths", () => {
expect(host.executeFile).not.toHaveBeenCalled();
});

it.each([
it.skipIf(skipWslConversion).each([
String.raw`d:\Screen shots\$(touch marker);'截图'.png`,
String.raw`\\server\share\截图.png`,
])("passes Windows paths as a literal argument: %s", async (reference) => {
Expand Down Expand Up @@ -221,7 +225,7 @@ describe("WSL attachment paths", () => {
},
);

it("still rejects missing files and directories after conversion", async () => {
it.skipIf(skipWslConversion)("still rejects missing files and directories after conversion", async () => {
host.executeFile.mockResolvedValue({
stdout: `${join(workspaceDir, "missing.png")}\n`,
});
Expand Down
2 changes: 2 additions & 0 deletions release/public-source.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions scripts/check-windows-source-location.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import path from "node:path";

const LOCAL_NTFS_REQUIREMENT =
"Windows source checkouts must be on a local NTFS volume. pnpm workspace links require NTFS junctions; FAT32/exFAT volumes and network shares are not supported. Keep cloud-synced folders outside the checkout.";

function fail(reason) {
return { ok: false, reason: `${LOCAL_NTFS_REQUIREMENT} ${reason}` };
}

/**
* Validate the Windows volume before pnpm attempts to create workspace links.
*
* The function is exported so the platform-specific policy can be tested without
* requiring a Windows host. Non-Windows platforms are intentionally a no-op.
*/
export function checkWindowsSourceLocation({
platform = process.platform,
cwd = process.cwd(),
execFile = execFileSync,
allowNonFixed = process.env.GITHUB_ACTIONS === "true",
} = {}) {
if (platform !== "win32") return { ok: true, skipped: true };

const pathApi = platform === "win32" ? path.win32 : path;
const root = pathApi.parse(pathApi.resolve(cwd)).root;
if (!/^[a-z]:\\$/iu.test(root) || root.startsWith("\\\\")) {
return fail("The checkout root is not a local drive-letter path.");
}
// `fsutil` accepts a drive letter more reliably than a root path with a
// trailing backslash across Windows runner images.
const volume = root.slice(0, 2);

let driveType;
let volumeInfo;
try {
driveType = execFile("fsutil", ["fsinfo", "drivetype", volume], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
volumeInfo = execFile("fsutil", ["fsinfo", "volumeinfo", volume], {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
} catch (error) {
const detail = error instanceof Error ? ` (${error.message})` : "";
return fail(`Windows could not verify the checkout volume${detail}.`);
}

if (!allowNonFixed && !/:\s*DRIVE_FIXED(?:\r?\n|$)/iu.test(driveType)) {
return fail("The checkout volume is not a local fixed drive.");
}
if (!/:\s*NTFS(?:\r?\n|$)/iu.test(volumeInfo)) {
return fail("The checkout volume is not formatted as NTFS.");
}

return { ok: true, skipped: false };
}

export function runWindowsSourceLocationCheck({
platform = process.platform,
cwd = process.cwd(),
execFile = execFileSync,
allowNonFixed = process.env.GITHUB_ACTIONS === "true",
report = (message) => console.error(`[source-check] ${message}`),
} = {}) {
const result = checkWindowsSourceLocation({ platform, cwd, execFile, allowNonFixed });
if (!result.ok) report(result.reason);
return result;
}

const scriptPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
if (scriptPath === fileURLToPath(import.meta.url)) {
const result = runWindowsSourceLocationCheck();
if (!result.ok) process.exitCode = 1;
}
19 changes: 12 additions & 7 deletions scripts/verify.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ const { values } = parseArgs({
},
});
const profile = values.profile;
if (!["full", "platform", "docs", "archive", "package"].includes(profile))
if (!["full", "platform", "windows", "docs", "archive", "package"].includes(profile))
throw new Error(`Unknown verification profile: ${profile}`);
if (profile === "windows" && process.platform !== "win32")
throw new Error("Windows verification profile requires a Windows host");
if (profile === 'package' && !['darwin', 'linux'].includes(process.platform))
throw new Error('Package verification currently supports Linux and macOS only.');
// Listing must not leave a temporary export directory behind.
Expand All @@ -36,22 +38,24 @@ const temporary = values.list
const preview = path.join(temporary ?? tmpdir(), "minimax-code-source.tar.gz");

const steps = [
{ name: "check:source", script: "check:source", docs: true },
{ name: "check:tsconfig", script: "check:tsconfig", docs: true },
{ name: "check:source", script: "check:source", docs: true, windows: true },
{ name: "check:tsconfig", script: "check:tsconfig", docs: true, windows: true },
{
name: "export source preview",
docs: true,
windows: true,
requiresGit: true,
command: ["scripts/export-source-preview.mjs", "--out", preview],
},
{ name: "test:release-tools", script: "test:release-tools", docs: true },
{ name: "test:release-tools", script: "test:release-tools", docs: true, windows: true },
// Compiler inputs are identical across the matrix. One Linux job runs this;
// all platforms still build and validate native artifacts on their own platform.
{ name: "typecheck", script: "typecheck", fullOnly: true },
{ name: "build", script: "build" },
{ name: "check:standalone", script: "check:standalone" },
{ name: "test:artifact", script: "test:artifact" },
{ name: "build", script: "build", windows: true },
{ name: "check:standalone", script: "check:standalone", windows: true },
{ name: "test:artifact", script: "test:artifact", windows: true },
{ name: "test:capabilities", script: "test:capabilities" },
{ name: "test:windows", script: "test:windows", platforms: ["win32"], windows: true },
{ name: "test:status-contract", script: "test:status-contract" },
{ name: "test:smoke", script: "test:smoke" },
{ name: "test:byok", script: "test:byok" },
Expand All @@ -74,6 +78,7 @@ const steps = [
function skipReason(step) {
if (profile === 'package' && !step.packageOnly) return 'validating an npm release archive';
if (profile !== 'package' && step.packageOnly) return 'requires an npm release archive';
if (profile === "windows" && !step.windows) return "not part of Windows contract";
if (profile === "docs" && !step.docs) return "documentation-only change";
if (profile === "archive" && step.requiresGit)
return "validating an already exported archive";
Expand Down
Loading
Loading