Skip to content

Commit

Permalink
fix(where)!: Thread OS info
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jan 3, 2023
1 parent 3c94de5 commit 111cf83
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 115 deletions.
17 changes: 13 additions & 4 deletions packages/cli/src/endo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import path from 'path';
import url from 'url';
import crypto from 'crypto';
import { spawn } from 'child_process';
import os from 'os';

import { Command } from 'commander';
import { makePromiseKit } from '@endo/promise-kit';
Expand Down Expand Up @@ -42,10 +43,18 @@ const packageDescriptorPath = url.fileURLToPath(
new URL('../package.json', import.meta.url),
);

const statePath = whereEndoState(process.platform, process.env);
const sockPath = whereEndoSock(process.platform, process.env);
const cachePath = whereEndoCache(process.platform, process.env);
const logPath = path.join(cachePath, 'endo.log');
const { username, homedir } = os.userInfo();
const temp = os.tmpdir();
const info = {
user: username,
home: homedir,
temp,
};

const statePath = whereEndoState(process.platform, process.env, info);
const sockPath = whereEndoSock(process.platform, process.env, info);
const cachePath = whereEndoCache(process.platform, process.env, info);
const logPath = path.join(statePath, 'endo.log');

export const main = async rawArgs => {
const { promise: cancelled, reject: cancel } = makePromiseKit();
Expand Down
15 changes: 12 additions & 3 deletions packages/daemon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import url from 'url';
import popen from 'child_process';
import fs from 'fs';
import path from 'path';
import os from 'os';

import { E } from '@endo/eventual-send';
import { makePromiseKit } from '@endo/promise-kit';
Expand All @@ -14,10 +15,18 @@ import { makeEndoClient } from './src/client.js';
// Reexports:
export { makeEndoClient } from './src/client.js';

const { username, homedir } = os.userInfo();
const temp = os.tmpdir();
const info = {
user: username,
home: homedir,
temp,
};

const defaultLocator = {
statePath: whereEndoState(process.platform, process.env),
sockPath: whereEndoSock(process.platform, process.env),
cachePath: whereEndoCache(process.platform, process.env),
statePath: whereEndoState(process.platform, process.env, info),
sockPath: whereEndoSock(process.platform, process.env, info),
cachePath: whereEndoCache(process.platform, process.env, info),
};

const endoDaemonPath = url.fileURLToPath(
Expand Down
93 changes: 41 additions & 52 deletions packages/where/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@
/**
* Returns a path for local Endo application user data on Windows.
*
* @param {{[name: string]: string}} env
* @param {{[name: string]: string | undefined}} env
* @param {import('./types.js').Info} info
*/
const whereEndoHomeWindows = env => {
const whereHomeWindows = (env, info) => {
// Favoring local app data over roaming app data since I don't expect to be
// able to listen on one host and connect on another.
// TODO support roaming data for shared content addressable state and
// find a suitable mechanism for merging state that may change independently
// on separate roaming hosts.
if (env.LOCALAPPDATA !== undefined) {
return `${env.LOCALAPPDATA}\\Endo`;
return `${env.LOCALAPPDATA}`;
}
if (env.APPDATA !== undefined) {
return `${env.APPDATA}\\Local\\Endo`;
return `${env.APPDATA}\\Local`;
}
if (env.USERPROFILE !== undefined) {
return `${env.USERPROFILE}\\AppData\\Local\\Endo`;
return `${env.USERPROFILE}\\AppData\\Local`;
}
if (env.HOMEDRIVE !== undefined && env.HOMEPATH !== undefined) {
return `${env.HOMEDRIVE}${env.HOMEPATH}\\AppData\\Local\\Endo`;
return `${env.HOMEDRIVE}${env.HOMEPATH}\\AppData\\Local`;
}
return 'Endo';
return `${info.home}\\AppData\\Local`;
};

/**
Expand All @@ -36,47 +37,36 @@ const whereEndoHomeWindows = env => {
*
* @type {typeof import('./types.js').whereEndoState}
*/
export const whereEndoState = (platform, env) => {
export const whereEndoState = (platform, env, info) => {
if (env.XDG_STATE_HOME !== undefined) {
return `${env.XDG_STATE_HOME}/endo`;
} else if (platform === 'win32') {
return whereEndoHomeWindows(env);
} else if (platform === 'darwin') {
if (env.HOME !== undefined) {
return `${env.HOME}/Library/Application Support/Endo`;
return `${whereHomeWindows(env, info)}\\Endo`;
}
const home = env.HOME !== undefined ? env.HOME : info.home;
if (platform === 'darwin') {
if (home !== undefined) {
return `${home}/Library/Application Support/Endo`;
}
} else if (env.HOME !== undefined) {
return `${env.HOME}/.local/state/endo`;
}
return 'endo/state';
return `${home}/.local/state/endo`;
};

/**
* Returns the most suitable location for storing state that ideally does not
* persist between restarts or reboots, specifically PID files.
* This should be coincident with the directory containing UNIX domain sockets,
* but is not suitable for Windows named pipes.
*
* @type {typeof import('./types.js').whereEndoEphemeralState}
*/
export const whereEndoEphemeralState = (platform, env) => {
export const whereEndoEphemeralState = (platform, env, info) => {
if (env.XDG_RUNTIME_DIR !== undefined) {
return `${env.XDG_RUNTIME_DIR}/endo`;
} else if (platform === 'win32') {
return whereEndoHomeWindows(env);
} else if (platform === 'darwin') {
if (env.HOME !== undefined) {
return `${env.HOME}/Library/Application Support/Endo`;
}
}
if (env.USER !== undefined) {
// The XDG specification says that in this case, we should fall through to
// system specific behavior, but we are not in a position to assume which
// system we're using, other than it is probably a variety of Linux,
// in which case /tmp and /run might be viable base paths.
return `/tmp/endo-${env.USER}`;
return `${whereHomeWindows(env, info)}\\Temp\\Endo`;
}
return '/tmp/endo';
const temp = env.TMPDIR !== undefined ? env.TMPDIR : info.temp;
const user = env.USER !== undefined ? env.USER : info.user;
return `${temp}/endo-${user}`;
};

/**
Expand All @@ -85,42 +75,41 @@ export const whereEndoEphemeralState = (platform, env) => {
*
* @type {typeof import('./types.js').whereEndoSock}
*/
export const whereEndoSock = (platform, env, protocol = 'captp0') => {
if (platform === 'win32') {
export const whereEndoSock = (platform, env, info, protocol = 'captp0') => {
// It must be possible to override the socket or named pipe location, but we
// cannot use XDG_RUNTIME_DIR for Windows named pipes, so for this case, we
// invent our own environment variable.
if (env.ENDO_SOCK !== undefined) {
return env.ENDO_SOCK;
} else if (platform === 'win32') {
// Named pipes have a special place in Windows (and in our ashen hearts).
if (env.USERNAME !== undefined) {
return `\\\\?\\pipe\\${env.USERNAME}-Endo\\${protocol}.pipe`;
} else {
return `\\\\?\\pipe\\Endo\\${protocol}.pipe`;
}
const user = env.USERNAME !== undefined ? env.USERNAME : info.user;
return `\\\\?\\pipe\\${user}-Endo\\${protocol}.pipe`;
} else if (env.XDG_RUNTIME_DIR !== undefined) {
return `${env.XDG_RUNTIME_DIR}/endo/${protocol}.sock`;
} else if (platform === 'darwin') {
if (env.HOME !== undefined) {
return `${env.HOME}/Library/Application Support/Endo/${protocol}.sock`;
}
} else if (env.USER !== undefined) {
return `/tmp/endo-${env.USER}/${protocol}.sock`;
const home = env.HOME !== undefined ? env.HOME : info.home;
return `${home}/Library/Application Support/Endo/${protocol}.sock`;
}
return `endo/${protocol}.sock`;
const user = env.USER !== undefined ? env.USER : info.user;
const temp = env.TMPDIR !== undefined ? env.TMPDIR : info.temp;
return `${temp}/endo-${user}/${protocol}.sock`;
};

/**
* Returns the most suitable path for Endo caches.
*
* @type {typeof import('./types.js').whereEndoCache}
*/
export const whereEndoCache = (platform, env) => {
export const whereEndoCache = (platform, env, info) => {
if (env.XDG_CACHE_HOME !== undefined) {
return `${env.XDG_CACHE_HOME}/endo`;
} else if (platform === 'win32') {
return `${whereEndoHomeWindows(env)}`;
return `${whereHomeWindows(env, info)}\\Endo`;
} else if (platform === 'darwin') {
if (env.HOME !== undefined) {
return `${env.HOME}/Library/Caches/Endo`;
}
} else if (env.HOME !== undefined) {
return `${env.HOME}/.cache/endo`;
const home = env.HOME !== undefined ? env.HOME : info.home;
return `${home}/Library/Caches/Endo`;
}
return 'endo/cache';
const home = env.HOME !== undefined ? env.HOME : info.home;
return `${home}/.cache/endo`;
};
38 changes: 28 additions & 10 deletions packages/where/test/test-where-endo-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,15 @@ test('windows', t => {
'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH',
);
t.is(
whereEndoCache('win32', {}),
'Endo',
'Under duress, fall back to a relative path, just Endo',
whereEndoCache(
'win32',
{},
{
home: 'C:\\Users\\Alice',
},
),
'C:\\Users\\Alice\\AppData\\Local\\Endo',
'Fall through to system-provided home',
);
});

Expand All @@ -64,9 +70,15 @@ test('darwin', t => {
'In absence of XDG environment, use conventional Mac cache location',
);
t.is(
whereEndoCache('darwin', {}),
'endo/cache',
'Under duress, fall back to a relative path, just endo/cache',
whereEndoCache(
'darwin',
{},
{
home: '/Users/homer',
},
),
'/Users/homer/Library/Caches/Endo',
'Fall back to system provided home',
);
});

Expand All @@ -89,10 +101,16 @@ test('linux', t => {
'Infer the conventional XDG environment from the user HOME',
);
t.is(
whereEndoCache('linux', {
USER: 'IGNOREME',
}),
'endo/cache',
whereEndoCache(
'linux',
{
USER: 'IGNOREME',
},
{
home: '/home/homer',
},
),
'/home/homer/.cache/endo',
'Under duress, do not attempt to infer whether /users/ or /home/ is a correct USER prefix for HOME, fall through to a relative path',
);
});
67 changes: 44 additions & 23 deletions packages/where/test/test-where-endo-ephemeral-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test('windows', t => {
HOMEDRIVE: 'IGNOREME',
HOMEPATH: 'IGNOREME',
}),
'C:\\Users\\Alice\\AppData\\Local\\Endo',
'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Use LOCALAPPDATA for Endo state if available',
);
t.is(
Expand All @@ -20,7 +20,7 @@ test('windows', t => {
HOMEDRIVE: 'IGNOREME',
HOMEPATH: 'IGNOREME',
}),
'C:\\Users\\Alice\\AppData\\Local\\Endo',
'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Infer LOCALAPPDATA from APPDATA if necessary and possible',
);
t.is(
Expand All @@ -29,20 +29,26 @@ test('windows', t => {
HOMEDRIVE: 'IGNOREME',
HOMEPATH: 'IGNOREME',
}),
'C:\\Users\\Alice\\AppData\\Local\\Endo',
'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Infer LOCALAPPDATA from USERPROFILE if necessary and possible',
);
t.is(
whereEndoEphemeralState('win32', {
HOMEDRIVE: 'C:\\',
HOMEPATH: 'Users\\Alice',
}),
'C:\\Users\\Alice\\AppData\\Local\\Endo',
'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Infer LOCALAPPDATA from HOMEDRIVE and HOMEPATH if necessary and possible',
);
t.is(
whereEndoEphemeralState('win32', {}),
'Endo',
whereEndoEphemeralState(
'win32',
{},
{
home: 'C:\\Users\\Alice',
},
),
'C:\\Users\\Alice\\AppData\\Local\\Temp\\Endo',
'Under duress, just use a relative path',
);
});
Expand All @@ -59,16 +65,18 @@ test('darwin', t => {
'Favor XDG state home over Darwin conventions if provided by the user',
);
t.is(
whereEndoEphemeralState('darwin', {
HOME: '/Users/alice',
}),
'/Users/alice/Library/Application Support/Endo',
'Use the Mac/Darwin conventional location for Application user data',
);
t.is(
whereEndoEphemeralState('darwin', {}),
'/tmp/endo',
'Under duress, fall back to an Endo tmp directory',
whereEndoEphemeralState(
'darwin',
{
HOME: 'IGNOREME',
},
{
user: 'alice',
temp: '/tmp/volumes/0',
},
),
'/tmp/volumes/0/endo-alice',
'Use the system temporary location and user name',
);
});

Expand All @@ -84,16 +92,29 @@ test('linux', t => {
'Use XDG state home if provided by the user',
);
t.is(
whereEndoEphemeralState('linux', {
USER: 'alice',
HOME: 'IGNOREME',
}),
'/tmp/endo-alice',
whereEndoEphemeralState(
'linux',
{
USER: 'alice',
HOME: 'IGNOREME',
},
{
temp: '/tmp/volume/0',
},
),
'/tmp/volume/0/endo-alice',
'In the absence of XDG information, infer a temporary location from the USER if available',
);
t.is(
whereEndoEphemeralState('linux', {}),
'/tmp/endo',
whereEndoEphemeralState(
'linux',
{},
{
user: 'homer',
temp: '/tmp/volume/0',
},
),
'/tmp/volume/0/endo-homer',
'For lack of any useful environment information, fall back to a shared temporary directory',
);
});
Loading

0 comments on commit 111cf83

Please sign in to comment.