|
1 | 1 | const childProcess = require('child_process'); |
| 2 | +const fs = require('fs'); |
2 | 3 | const path = require('path'); |
3 | 4 |
|
| 5 | +const SHELL_ERR_CMD_NOT_FOUND = 127; |
| 6 | +const { CI } = process.env; |
| 7 | + |
| 8 | +/** |
| 9 | + * Matches 'deno 2.3.1' or 'Deno 2.7.11-alpha3.24' or even 'some deno and-anything in between 1.43.5' (as long as everything is in the same line) |
| 10 | + * and extracts the correct version string from those ('2.3.1', '2.7.11' and '1.43.5' respectively). |
| 11 | + * |
| 12 | + * Doesn't match 'denoing 2.3.1' or 'deno2.3.1' or 'mydeno 2.7.11alpha3.24' or 'deno\n1.43.5' |
| 13 | + * |
| 14 | + * The expression gets a bit complicated because the word boundary assertion (\b) identifies the dash (-) as a valid word boundary, |
| 15 | + * but that is not the case for use, as we don't want to match "make-deno" for instance. So, for correctness, we use a negative lookbehind |
| 16 | + * assertion ("(?<!)") BEFORE words to make sure our match is not preceded by a word character (\w), a dash (-) or a dot (.), e.g. it won't |
| 17 | + * match "mydeno", "my-deno", "my.deno", etc. The negative lookbehind also allows us to match "deno" in the start of the input, something that |
| 18 | + * simply using the expression itself wouldn't match. In most other cases, these would be replaced by simply "\b" |
| 19 | + * |
| 20 | + * The exact expression tries find the first line to match a sequence as follows: |
| 21 | + * - A character "D" or "d" that is not preceded by a word character, dash or dot (negative lookbehind) |
| 22 | + * - Followed by the literal sequence "eno" |
| 23 | + * - Followed by any character that is not a word character, dash or dot |
| 24 | + * - Followed by a sequence of zero or more occurrences of any character (non multi line) |
| 25 | + * - Followed by a sequence of one or more numbers, then a dot, then one or more numbers, then a dot, then one or more numbers ("version" capture group) |
| 26 | + * that is NOT preceded by a word character, a dash, or dot (negative lookbehind) |
| 27 | + * - Followed by a word boundary (here we're less picky with the "\b" assertion, as we're out of the capture group) |
| 28 | + */ |
| 29 | +const extractDenoVersion = (input) => /(?<![\w-.])[Dd]eno[^\w-.].*(?<![\w-.])(?<version>\d+\.\d+\.\d+)\b/.exec(input)?.groups?.version; |
| 30 | + |
4 | 31 | try { |
5 | | - childProcess.execSync('deno info'); |
| 32 | + const toolVersionsPath = path.resolve(__dirname, '..', '..', '..', '.tool-versions'); |
| 33 | + const denoToolVersion = extractDenoVersion(fs.readFileSync(toolVersionsPath).toString()); |
| 34 | + |
| 35 | + if (!denoToolVersion) { |
| 36 | + throw new Error(`Invalid Deno version in ${toolVersionsPath}, aborting...`); |
| 37 | + } |
| 38 | + |
| 39 | + const installedVersion = extractDenoVersion(childProcess.execSync('deno --version').toString()); |
| 40 | + |
| 41 | + if (!installedVersion) { |
| 42 | + throw new Error( |
| 43 | + `Couldn't determine version of installed Deno. Try validating the version with 'deno --version' and make sure it is a valid Deno installation`, |
| 44 | + ); |
| 45 | + } |
| 46 | + |
| 47 | + if (installedVersion !== denoToolVersion) { |
| 48 | + const message = `Incorrect Deno version. Required '${denoToolVersion}', found '${installedVersion}'.${CI ? '' : " The server will likely work, but it may cause your deno.lock to change - do not commit it. Make sure your Deno version matches the required one so you don't see this message again."}`; |
| 49 | + |
| 50 | + if (CI) { |
| 51 | + throw new Error(message); |
| 52 | + } |
| 53 | + |
| 54 | + // We don't need to fail if a dev environment doesn't have a matching Deno version, just the warning is enough |
| 55 | + console.warn(message); |
| 56 | + } |
6 | 57 | } catch (e) { |
7 | | - console.error( |
8 | | - 'Could not execute "deno" in the system. It is now a requirement for the Apps-Engine framework, and Rocket.Chat apps will not work without it.\n', |
9 | | - 'Make sure to install Deno and run the installation process for the Apps-Engine again. More info on https://docs.deno.com/runtime/manual/getting_started/installation', |
10 | | - ); |
| 58 | + if (e.status === SHELL_ERR_CMD_NOT_FOUND) { |
| 59 | + console.error( |
| 60 | + new Error( |
| 61 | + [ |
| 62 | + 'Could not execute "deno" in the system. It is now a requirement for the Apps-Engine framework, and Rocket.Chat apps will not work without it.', |
| 63 | + 'Make sure to install Deno and run the installation process for the Apps-Engine again. More info on https://docs.deno.com/runtime/manual/getting_started/installation', |
| 64 | + ].join('\n'), |
| 65 | + { cause: e }, |
| 66 | + ), |
| 67 | + ); |
| 68 | + } else { |
| 69 | + console.error(e); |
| 70 | + } |
| 71 | + |
11 | 72 | process.exit(1); |
12 | 73 | } |
13 | 74 |
|
14 | 75 | const rootPath = path.join(__dirname, '..'); |
15 | 76 | const denoRuntimePath = path.join(rootPath, 'deno-runtime'); |
16 | 77 | const DENO_DIR = process.env.DENO_DIR ?? path.join(rootPath, '.deno-cache'); |
17 | 78 |
|
18 | | -childProcess.execSync('deno cache main.ts', { |
| 79 | +// In CI envs, break if lockfile changes; in dev envs, it's alright |
| 80 | +const commandLine = CI ? 'deno install --frozen --entrypoint main.ts' : 'deno install --entrypoint main.ts'; |
| 81 | + |
| 82 | +childProcess.execSync(commandLine, { |
19 | 83 | cwd: denoRuntimePath, |
20 | 84 | env: { |
21 | 85 | ...process.env, |
|
0 commit comments