Path to extension in production

To keep with my newly found habit of resuscitating old threads for significant Nova API changes, I’d like to note Nova 9 brought us native file mode handling, including a nifty chmod() function. Handling, however, is a bit … iffy, because FileStats.mode returns a six digit octal that JS helpfülly treats as a decimal, but FileSystem.chmod() expects a 3 digit octal number based on the last 3 digits of aforementioned mode.

The following update of my old code nets you the ability to make extension files executable on Nova versions both older and newer:

async function makeExecutable (...paths) {
  paths = paths.filter(path => nova.fs.stat(path)?.isFile())
  if (paths.length) {
    if (nova.version[0] < 9) {
      const options = { args: ['+x', ...paths] }
      const { code, error } = await runAsync('/bin/chmod', options)
      if (code > 0) throw error
    } else {
      // This is not elegant, but it works and is more readable than converting
      // everything to binary.
      for (const path of paths) {
        const mode = nova.fs.stat(path).mode.toString(8).slice(-3)
        const xbits = []
        for (const oct of mode) { xbits.push(Number(oct) | 1) }
        nova.fs.chmod(path, Number(`0o${xbits.join('')}`))
      }
    }
  }
  return paths
}

Note that, on Nova versions below 9, this will need the “process” entitlement to run chmod and my runAsync module, or an equivalent of your own making. Starting with Nova 9, this uses the native functions and only needs the “filesystem: readwrite” entitlement to work.