Ctrl K
ComunidadGuías…

scripts/release/npm-wrapper-smoke.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node

const fs = require("fs");
const fsp = require("fs/promises");
const http = require("http");
const os = require("os");
const path = require("path");
const { spawn } = require("child_process");

const repoRoot = path.resolve(__dirname, "..", "..");
const packageDir = path.join(repoRoot, "npm", "codewhale");
const prepareAssetsScript = path.join(
  repoRoot,
  "scripts",
  "release",
  "prepare-local-release-assets.js",
);
const CLEANUP_MAX_RETRIES = 8;
const CLEANUP_RETRY_DELAY_MS = 250;

function shellQuote(value) {
  return /\s/.test(value) ? JSON.stringify(value) : value;
}

function usesWindowsCommandShim(command) {
  return process.platform === "win32" && (command === "npm" || command === "npx");
}

function runCommand(command, args, options = {}) {
  const cwd = options.cwd || repoRoot;
  console.log(`$ ${[command, ...args].map(shellQuote).join(" ")}`);
  const child = spawn(command, args, {
    cwd,
    env: {
      ...process.env,
      ...(options.env || {}),
    },
    encoding: "utf8",
    shell: usesWindowsCommandShim(command),
    stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit",
    windowsHide: true,
  });

  if (!options.capture) {
    return new Promise((resolve, reject) => {
      child.once("error", reject);
      child.once("close", (status) => {
        if (status === 0) {
          resolve({ stdout: "", stderr: "" });
        } else {
          reject(new Error(`${command} exited with status ${status}`));
        }
      });
    });
  }

  let stdout = "";
  let stderr = "";
  child.stdout.setEncoding("utf8");
  child.stderr.setEncoding("utf8");
  child.stdout.on("data", (chunk) => {
    stdout += chunk;
  });
  child.stderr.on("data", (chunk) => {
    stderr += chunk;
  });
  return new Promise((resolve, reject) => {
    child.once("error", reject);
    child.once("close", (status) => {
      if (status === 0) {
        resolve({ stdout, stderr });
        return;
      }
      process.stdout.write(stdout);
      process.stderr.write(stderr);
      reject(new Error(`${command} exited with status ${status}`));
    });
  });
}

function serveDirectory(root) {
  const server = http.createServer(async (request, response) => {
    try {
      const requestUrl = new URL(request.url || "/", "http://127.0.0.1");
      const decodedPath = decodeURIComponent(requestUrl.pathname);
      const filePath = path.resolve(root, `.${decodedPath}`);
      const relative = path.relative(root, filePath);
      if (relative.startsWith("..") || path.isAbsolute(relative)) {
        response.writeHead(403);
        response.end("forbidden");
        return;
      }

      const fileStat = await fsp.stat(filePath);
      if (!fileStat.isFile()) {
        response.writeHead(404);
        response.end("not found");
        return;
      }

      response.writeHead(200, {
        "Content-Length": fileStat.size,
        "Content-Type": "application/octet-stream",
      });
      fs.createReadStream(filePath).pipe(response);
    } catch (error) {
      response.writeHead(error && error.code === "ENOENT" ? 404 : 500);
      response.end(error && error.message ? error.message : "not found");
    }
  });

  return new Promise((resolve, reject) => {
    server.once("error", reject);
    server.listen(0, "127.0.0.1", () => {
      const address = server.address();
      resolve({
        baseUrl: `http://127.0.0.1:${address.port}/`,
        server,
      });
    });
  });
}

function parsePackJson(stdout) {
  const trimmed = stdout.trim();
  if (!trimmed) {
    throw new Error("npm pack did not return package metadata");
  }
  const parsed = JSON.parse(trimmed);
  const first = Array.isArray(parsed) ? parsed[0] : parsed;
  if (!first || !first.filename) {
    throw new Error(`npm pack metadata did not include a filename: ${trimmed}`);
  }
  return first.filename;
}

async function removeSmokeWorkspace(tempRoot, remove = fsp.rm) {
  await remove(tempRoot, {
    force: true,
    recursive: true,
    // Windows can report ENOTEMPTY briefly while completed binary downloads
    // are still being released by the filesystem. fs.rm retries ENOTEMPTY
    // (and the other documented transient removal errors) with linear
    // backoff; the finite cap still surfaces a persistent cleanup failure.
    maxRetries: CLEANUP_MAX_RETRIES,
    retryDelay: CLEANUP_RETRY_DELAY_MS,
  });
}

async function main() {
  const tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "codewhale-npm-smoke-"));
  const suppliedAssetsDir = String(
    process.env.CODEWHALE_SMOKE_ASSETS_DIR || "",
  ).trim();
  const releaseAssetsDir = suppliedAssetsDir
    ? path.resolve(suppliedAssetsDir)
    : path.join(tempRoot, "release-assets");
  const packDir = path.join(tempRoot, "pack");
  const installDir = path.join(tempRoot, "install");
  let keepTemp =
    process.env.CODEWHALE_KEEP_SMOKE_DIR === "1" ||
    process.env.DEEPSEEK_TUI_KEEP_SMOKE_DIR === "1";
  let server;

  try {
    await fsp.mkdir(packDir, { recursive: true });
    await fsp.mkdir(installDir, { recursive: true });

    if (suppliedAssetsDir) {
      const assetDirectoryStat = await fsp.stat(releaseAssetsDir);
      if (!assetDirectoryStat.isDirectory()) {
        throw new Error(`CODEWHALE_SMOKE_ASSETS_DIR is not a directory: ${releaseAssetsDir}`);
      }
      console.log(`Using preassembled release assets from ${releaseAssetsDir}`);
    } else {
      const prepareArgs = [prepareAssetsScript, releaseAssetsDir];
      const cargoTargetDir = String(process.env.CARGO_TARGET_DIR || "").trim();
      if (cargoTargetDir) {
        prepareArgs.push(
          path.join(path.resolve(repoRoot, cargoTargetDir), "release"),
        );
      }
      await runCommand(process.execPath, prepareArgs);
    }
    const served = await serveDirectory(releaseAssetsDir);
    server = served.server;

    const env = {
      CODEWHALE_FORCE_DOWNLOAD: "1",
      CODEWHALE_RELEASE_BASE_URL: served.baseUrl,
    };
    const pack = await runCommand(
      "npm",
      ["pack", "--json", "--pack-destination", packDir],
      {
        capture: true,
        cwd: packageDir,
        env,
      },
    );
    const tarball = path.join(packDir, parsePackJson(pack.stdout));

    await runCommand("npm", ["init", "-y"], { cwd: installDir });
    await runCommand("npm", ["install", tarball], { cwd: installDir, env });
    await runCommand("npx", ["--no-install", "codewhale", "doctor", "--help"], {
      cwd: installDir,
      env,
    });
    await runCommand("npx", ["--no-install", "codew", "--version"], {
      cwd: installDir,
      env,
    });

    console.log(`npm wrapper smoke passed with local assets from ${served.baseUrl}`);
  } catch (error) {
    keepTemp = true;
    console.error(`npm wrapper smoke failed: ${error.message}`);
    console.error(`Smoke workspace retained at ${tempRoot}`);
    process.exitCode = 1;
  } finally {
    if (server) {
      await new Promise((resolve) => server.close(resolve));
    }
    if (!keepTemp) {
      await removeSmokeWorkspace(tempRoot);
    }
  }
}

if (require.main === module) {
  main();
}

module.exports = {
  CLEANUP_MAX_RETRIES,
  CLEANUP_RETRY_DELAY_MS,
  removeSmokeWorkspace,
};