|
1 | 1 | import { Manifest } from '../base/Executor' |
2 | 2 | import { Transport } from '../base/Transports' |
| 3 | +import * as path from 'path' |
| 4 | +import * as os from 'os' |
| 5 | +import * as fs from 'fs' |
| 6 | +import * as util from 'util' |
| 7 | +import { getLogger } from '@stencila/logga' |
3 | 8 |
|
4 | | -export default function discover(): Manifest[] { |
| 9 | +const glob = util.promisify(require('glob')) |
| 10 | + |
| 11 | +const log = getLogger('executa:serve') |
| 12 | + |
| 13 | +const EXECUTORS_DIR_NAME = 'executors' |
| 14 | + |
| 15 | +export default async function discover(): Promise<Manifest[]> { |
5 | 16 | // TODO: implement discovery of manifest files from ~/.stencila/executors/ (or similar) |
6 | 17 | // Should be able to mostly copy and paste from |
7 | 18 | // https://github.com/stencila/node/blob/24c30d1c89b5f6b6719a0beeda7a55d19401c294/lib/host/Host.js#L654-L666 |
8 | 19 | // See https://github.com/stencila/executa/issues/2 |
9 | | - return [python, js] |
10 | | -} |
11 | 20 |
|
12 | | -// These are just stubs to be replaced by JSON read in from manifest.json files... |
| 21 | + let stencilaHome: string |
13 | 22 |
|
14 | | -const python: Manifest = { |
15 | | - capabilities: { |
16 | | - execute: { |
17 | | - type: 'object', |
18 | | - required: ['node'], |
19 | | - properties: { |
20 | | - node: { |
21 | | - type: 'object', |
22 | | - required: ['type', 'programmingLanguage'], |
23 | | - properties: { |
24 | | - type: { |
25 | | - enum: ['CodeChunk', 'CodeExpression'] |
26 | | - }, |
27 | | - programmingLanguage: { |
28 | | - enum: ['python'] |
29 | | - } |
30 | | - } |
31 | | - } |
32 | | - } |
| 23 | + switch (os.platform()) { |
| 24 | + case 'darwin': |
| 25 | + stencilaHome = path.join( |
| 26 | + process.env.HOME !== undefined ? process.env.HOME : '', |
| 27 | + 'Library', |
| 28 | + 'Application Support', |
| 29 | + 'Stencila' |
| 30 | + ) |
| 31 | + break |
| 32 | + case 'linux': |
| 33 | + stencilaHome = path.join( |
| 34 | + process.env.HOME !== undefined ? process.env.HOME : '', |
| 35 | + '.stencila' |
| 36 | + ) |
| 37 | + break |
| 38 | + case 'win32': // is 'win32' even on 64 bit windows systems |
| 39 | + stencilaHome = path.join( |
| 40 | + process.env.APPDATA !== undefined ? process.env.APPDATA : '', |
| 41 | + 'Stencila' |
| 42 | + ) |
| 43 | + break |
| 44 | + default: |
| 45 | + stencilaHome = path.join( |
| 46 | + process.env.HOME !== undefined ? process.env.HOME : '', |
| 47 | + 'stencila' |
| 48 | + ) |
| 49 | + } |
| 50 | + const executorsDir = path.join(stencilaHome, EXECUTORS_DIR_NAME) |
| 51 | + |
| 52 | + const manifests: Manifest[] = [] |
| 53 | + |
| 54 | + const discoverDir = async (dir: string): Promise<Manifest | undefined> => { |
| 55 | + // Check the folder exists (they may not e.g. if no packages have been registered) |
| 56 | + try { |
| 57 | + fs.accessSync(dir, fs.constants.R_OK) |
| 58 | + } catch (error) { |
| 59 | + return |
33 | 60 | } |
34 | | - }, |
35 | | - addresses: { |
36 | | - stdio: { |
37 | | - type: Transport.stdio, |
38 | | - command: 'python3', |
39 | | - args: ['-m', 'stencila.schema', 'listen'] |
| 61 | + // For each host in the directory |
| 62 | + for (const file of await glob(path.join(dir, '*.json'))) { |
| 63 | + let json |
| 64 | + try { |
| 65 | + json = fs.readFileSync(file, { encoding: 'utf8' }) |
| 66 | + } catch (error) { |
| 67 | + log.warn(`Warning: error reading file "${file}": ${error.message}`) |
| 68 | + continue |
| 69 | + } |
| 70 | + |
| 71 | + try { |
| 72 | + manifests.push(JSON.parse(json) as Manifest) |
| 73 | + } catch (error) { |
| 74 | + log.warn(`Warning: error parsing file "${file}": ${error.message}`) |
| 75 | + } |
40 | 76 | } |
41 | 77 | } |
| 78 | + |
| 79 | + await discoverDir(executorsDir) |
| 80 | + |
| 81 | + manifests.push(js) |
| 82 | + return manifests |
42 | 83 | } |
43 | 84 |
|
| 85 | +// These are just stubs to be replaced by JSON read in from manifest.json files... |
| 86 | + |
44 | 87 | const js: Manifest = { |
45 | 88 | capabilities: { |
46 | 89 | execute: { |
|
0 commit comments