mediasoup: npm postinstall fails in Windows: rd /s /q worker\out\msys => The system cannot find the file specified

Bug Report

This is a recent error happening in Windows CI: https://github.com/versatica/mediasoup/actions/runs/6536142903/job/17747178461?pr=1178

npm-scripts [INFO] [postinstall] executeCmd(): rd /s /q worker\out\msys
The system cannot find the file specified.
npm-scripts [ERROR] [postinstall] executeCmd() failed, exiting: Error: Command failed: rd /s /q worker\out\msys

Nothing has changed so this is related to Windows. No idea. We need to make it work reliably in all supported Windows versions.

Somehow the issue doesn’t make sense. When installing mediasoup on Windows (and forcing it to compile the worker instead of fetching a prebuilt worker), this is the flow:

  1. MEDIASOUP_SKIP_WORKER_PREBUILT_DOWNLOAD=true npm install mediasoup --foreground-scripts (well, whichever syntax is needed in Windows to set environment variables).
  2. The npm ‘postinstall’ task will run the ‘postinstall’ section in npm-scripts.mjs.
  3. It will first fun buildWorker():
function buildWorker()
{
  logInfo('buildWorker()');

  if (IS_WINDOWS)
  {
  	if (!fs.existsSync('worker/out/msys/bin/make.exe'))
  	{
  		installMsysMake();
  	}

  	const msysPath = `${process.cwd()}\\worker\\out\\msys\\bin`;

  	if (!process.env.PATH.includes(msysPath))
  	{
  		process.env.PATH = `${msysPath};${process.env.PATH}`;
  	}
  }

  executeCmd(`${MAKE} -C worker`);
}
  1. Note that if worker/out/msys/bin/make.exe doesn’t exist then it calls installMsysMake() which does this:
function installMsysMake()
{
  logInfo('installMsysMake()');

  let res = spawnSync('where', [ 'python3.exe' ]);

  if (res.status !== 0)
  {
  	res = spawnSync('where', [ 'python.exe' ]);

  	if (res.status !== 0)
  	{
  		logError('`installMsysMake() | cannot find Python executable');

  		exitWithError();
  	}
  }

  executeCmd(`${String(res.stdout).trim()} worker\\scripts\\getmake.py`);
}
  1. So here it ensures that we have python3.exe and then runs worker/scripts/getmake.py which does this:
import io, hashlib, tarfile, urllib.request

def get(url, digest):
  data = urllib.request.urlopen(url).read()
  assert hashlib.sha256(data).hexdigest() == digest
  tar = tarfile.open(fileobj=io.BytesIO(data))
  tar.extractall('worker/out/msys')
  tar.close()

get('https://sourceforge.net/projects/mingw/files/MSYS/Base/msys-core/msys-1.0.19-1/msysCORE-1.0.19-1-msys-1.0.19-bin.tar.xz/download', '8c4157d739a460f85563bc4451e9f1bbd42b13c4f63770d43b9f45a781f07858')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/libiconv/libiconv-1.14-1/libiconv-1.14-1-msys-1.0.17-dll-2.tar.lzma/download', '196921e8c232259c8e6a6852b9ee8d9ab2d29a91419f0c8dc27ba6f034231683')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/gettext/gettext-0.18.1.1-1/libintl-0.18.1.1-1-msys-1.0.17-dll-8.tar.lzma/download', '29db8c969661c511fbe2a341ab25c993c5f9c555842a75d6ddbcfa70dec16910')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/coreutils/coreutils-5.97-3/coreutils-5.97-3-msys-1.0.13-bin.tar.lzma/download', 'f8c7990416ea16a74ac336dcfe0f596bc46b8724b2d58cf8a3509414220b2366')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/regex/regex-1.20090805-2/libregex-1.20090805-2-msys-1.0.13-dll-1.tar.lzma/download', '85dd8c1e27a90675c5f867be57ba7ae2bb55dde8cd2d19f284c896be134bd3d1')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/termcap/termcap-0.20050421_1-2/libtermcap-0.20050421_1-2-msys-1.0.13-dll-0.tar.lzma/download', '62b58fe0880f0972fcc84a819265989b02439c1c5185870227bd25f870f7adb6')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/bash/bash-3.1.23-1/bash-3.1.23-1-msys-1.0.18-bin.tar.xz/download', '38da5419969ab883058a96322bb0f51434dd4e9f71de09cd4f75b96750944533')
get('https://sourceforge.net/projects/mingw/files/MSYS/Base/make/make-3.81-3/make-3.81-3-msys-1.0.13-bin.tar.lzma/download', '847f0cbbf07135801c8e67bf692d29b1821e816ad828753c997fa869a9b89988')
  1. Here it clearly extracts things in worker/out/msys folder, notice the tar.extractall('worker/out/msys') line.
  2. So then buildWorker() continues. It adds ${process.cwd()}\\worker\\out\\msys\\bin to the PATH and runs make to compile the worker.
  3. And then buildWorker() completes so cleanWorkerArtifacts() is called which does this:
function cleanWorkerArtifacts()
{
  logInfo('cleanWorkerArtifacts()');

  // Clean build artifacts except `mediasoup-worker`.
  executeCmd(`${MAKE} clean-build -C worker`);
  // Clean downloaded dependencies.
  executeCmd(`${MAKE} clean-subprojects -C worker`);
  // Clean PIP/Meson/Ninja.
  executeCmd(`${MAKE} clean-pip -C worker`);

  if (IS_WINDOWS)
  {
  	executeCmd('rd /s /q worker\\out\\msys');
  }
}
  1. And somehow the last command rd /s /q worker\\out\\msys fails only in some cases (see https://github.com/versatica/mediasoup/issues/1179#issuecomment-1770463999) with this error:
npm-scripts [INFO] [postinstall] executeCmd(): rd /s /q worker\out\msys
The system cannot find the file specified.
npm-scripts [ERROR] [postinstall] executeCmd() failed, exiting: Error: Command failed: rd /s /q worker\out\msys
  1. How is it possible? Obviously worker\out\msys folder DOES exist, it’s guaranteed by steps before.

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Comments: 26 (13 by maintainers)

Commits related to this issue

Most upvoted comments

So @nazar-pc, I fail to understand that possible reason. It’s not possible.

I do not know how/what/where, but it is clear that the files are not there. Or maybe they are extracted into worker/out/msys, but it is not worker/out/msys that we think it is or something (maybe pwd inside of Python script is different, so it extracts files elsewhere, providing an absolute path would fix that particular issue).

Asking to a workmate.

@jmillan @nazar-pc are we ok with the temporary solution I suggest in my comment above?

executeCmd('rd /s /q worker\\out\\msys', /* exitOnError */ false);