Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: feat(config): Use more conventional paths for config and data #5336

Merged
merged 4 commits into from Feb 8, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -0,0 +1,131 @@
/* @flow */

import {getDataDir, getCacheDir, getConfigDir} from '../../src/util/user-dirs';
import userHome from '../../src/util/user-home-dir';

const path = require('path');

describe('getDataDir', () => {
describe('on windows', () => {
beforeEach(() => {
mockProcessPlatform('win32');
});

test('uses Yarn/data within LOCALAPPDATA if it exists', () => {
process.env.LOCALAPPDATA = 'foo';
expect(getDataDir()).toBe(path.join('foo', 'Yarn', 'Data'));
delete process.env.LOCALAPPDATA;
});

test('uses the config dir otherwise', () => {
expect(getDataDir()).toBe(path.join(userHome, '.config', 'yarn'));
});
});

describe('on linux/darwin', () => {
beforeEach(() => {
mockProcessPlatform('linux');
});

test('uses XDG_DATA_HOME if it is set', () => {
process.env.XDG_DATA_HOME = 'foo';
expect(getDataDir()).toBe(path.join('foo', 'yarn'));
delete process.env.XDG_DATA_HOME;
});

test('falls back to the config dir', () => {
expect(getDataDir()).toBe(path.join(userHome, '.config', 'yarn'));
});
});
});

describe('getCacheDir', () => {
describe('on windows', () => {
beforeEach(() => {
mockProcessPlatform('win32');
});

test('uses Yarn\\Cache within LOCALAPPDATA if it exists', () => {
process.env.LOCALAPPDATA = 'foo';
expect(getCacheDir()).toBe(path.join('foo', 'Yarn', 'Cache'));
delete process.env.LOCALAPPDATA;
});

test('uses AppData\\Local\\Cache otherwise', () => {
expect(getCacheDir()).toBe(path.join(userHome, 'AppData', 'Local', 'Yarn', 'Cache'));
});
});

describe('on darwin (macOS)', () => {
beforeEach(() => {
mockProcessPlatform('darwin');
});

test('uses XDG_CACHE_HOME if it is set', () => {
process.env.XDG_CACHE_HOME = 'foo';
expect(getCacheDir()).toBe(path.join('foo', 'yarn'));
delete process.env.XDG_CACHE_HOME;
});

test('falls back to Library/Caches/Yarn', () => {
expect(getCacheDir()).toBe(path.join(userHome, 'Library', 'Caches', 'Yarn'));
});
});

describe('on others (linux, etc)', () => {
beforeEach(() => {
mockProcessPlatform('linux');
});

test('uses XDG_CACHE_HOME if it is set', () => {
process.env.XDG_CACHE_HOME = 'foo';
expect(getCacheDir()).toBe(path.join('foo', 'yarn'));
delete process.env.XDG_CACHE_HOME;
});

test('falls back to .cache/yarn', () => {
expect(getCacheDir()).toBe(path.join(userHome, '.cache', 'yarn'));
});
});
});

describe('getConfigDir', () => {
describe('on windows', () => {
beforeEach(() => {
mockProcessPlatform('win32');
});

test('uses Yarn\\Config within LOCALAPPDATA if it exists', () => {
process.env.LOCALAPPDATA = 'foo';
expect(getConfigDir()).toBe(path.join('foo', 'Yarn', 'Config'));
delete process.env.LOCALAPPDATA;
});

test('uses the config dir otherwise', () => {
expect(getConfigDir()).toBe(path.join(userHome, '.config', 'yarn'));
});
});

describe('on linux/darwin', () => {
beforeEach(() => {
mockProcessPlatform('linux');
});

test('uses XDG_CONFIG_HOME if it is set', () => {
process.env.XDG_CONFIG_HOME = 'foo';
expect(getConfigDir()).toBe(path.join('foo', 'yarn'));
delete process.env.XDG_CONFIG_HOME;
});

test('falls back to .config/yarn', () => {
expect(getConfigDir()).toBe(path.join(userHome, '.config', 'yarn'));
});
});
});

function mockProcessPlatform(name: string) {
// $FlowFixMe this is valid
Object.defineProperty(process, 'platform', {
get: jest.fn(() => name),
});
}
@@ -3,6 +3,7 @@
const os = require('os');
const path = require('path');
const userHome = require('./util/user-home-dir').default;
const {getCacheDir, getConfigDir, getDataDir} = require('./util/user-dirs');
const isWebpackBundle = require('is-webpack-bundle');

type Env = {
@@ -42,24 +43,8 @@ export const CHILD_CONCURRENCY = 5;

export const REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid'];

function getDirectory(category: string): string {
// use %LOCALAPPDATA%/Yarn on Windows
if (process.platform === 'win32' && process.env.LOCALAPPDATA) {
return path.join(process.env.LOCALAPPDATA, 'Yarn', category);
}

// otherwise use ~/.{category}/yarn
return path.join(userHome, `.${category}`, 'yarn');
}

function getPreferredCacheDirectories(): Array<string> {
const preferredCacheDirectories = [];

if (process.platform === 'darwin') {
preferredCacheDirectories.push(path.join(userHome, 'Library', 'Caches', 'Yarn'));
} else {
preferredCacheDirectories.push(getDirectory('cache'));
}
const preferredCacheDirectories = [getCacheDir()];

if (process.getuid) {
// $FlowFixMe: process.getuid exists, dammit
@@ -72,9 +57,10 @@ function getPreferredCacheDirectories(): Array<string> {
}

export const PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories();
export const CONFIG_DIRECTORY = getDirectory('config');
export const LINK_REGISTRY_DIRECTORY = path.join(CONFIG_DIRECTORY, 'link');
export const GLOBAL_MODULE_DIRECTORY = path.join(CONFIG_DIRECTORY, 'global');
export const CONFIG_DIRECTORY = getConfigDir();
export const DATA_DIRECTORY = getDataDir();
export const LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link');
export const GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global');

export const NODE_BIN_PATH = process.execPath;
export const YARN_BIN_PATH = getYarnBinPath();
@@ -2,6 +2,7 @@

import {readFileSync} from 'fs';
import * as path from 'path';
import {CONFIG_DIRECTORY} from '../constants';

const etc = '/etc';
const isWin = process.platform === 'win32';
@@ -20,6 +21,7 @@ function getRcPaths(name: string, cwd: string): Array<string> {
}

if (home) {
addConfigPath(CONFIG_DIRECTORY);
addConfigPath(home, '.config', name, 'config');
addConfigPath(home, '.config', name);
addConfigPath(home, `.${name}`, 'config');
@@ -0,0 +1,54 @@
/* @flow */

const path = require('path');
const userHome = require('./user-home-dir').default;

const FALLBACK_CONFIG_DIR = path.join(userHome, '.config', 'yarn');
const FALLBACK_CACHE_DIR = path.join(userHome, '.cache', 'yarn');

export function getDataDir(): string {
if (process.platform === 'win32') {
const WIN32_APPDATA_DIR = getLocalAppDataDir();
return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Data');
} else if (process.env.XDG_DATA_HOME) {
return path.join(process.env.XDG_DATA_HOME, 'yarn');
} else {
// This could arguably be ~/Library/Application Support/Yarn on Macs,
// but that feels unintuitive for a cli tool

// Instead, use our prior fallback. Some day this could be
// path.join(userHome, '.local', 'share', 'yarn')
// or return path.join(WIN32_APPDATA_DIR, 'Data') on win32
return FALLBACK_CONFIG_DIR;
}
}

export function getCacheDir(): string {
if (process.platform === 'win32') {
// process.env.TEMP also exists, but most apps put caches here

This comment has been minimized.

Copy link
@wbinnssmith

wbinnssmith Feb 8, 2018

Author Contributor

@Daniel15 do you know about this?

This comment has been minimized.

Copy link
@Daniel15

Daniel15 Feb 14, 2018

Member

TEMP is for temp files (anything you don't care about any more after some period of time while it's being used), whereas I think we want to keep the Yarn cache around.

return path.join(getLocalAppDataDir() || path.join(userHome, 'AppData', 'Local', 'Yarn'), 'Cache');
} else if (process.env.XDG_CACHE_HOME) {
return path.join(process.env.XDG_CACHE_HOME, 'yarn');
} else if (process.platform === 'darwin') {
return path.join(userHome, 'Library', 'Caches', 'Yarn');
} else {
return FALLBACK_CACHE_DIR;
}
}

export function getConfigDir(): string {
if (process.platform === 'win32') {
// Use our prior fallback. Some day this could be
// return path.join(WIN32_APPDATA_DIR, 'Config')
const WIN32_APPDATA_DIR = getLocalAppDataDir();
return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Config');

This comment has been minimized.

Copy link
@Daniel15

Daniel15 Feb 14, 2018

Member

If we want to be the "most correct" here, I think the config should go in AppData, while the cache should go in LocalAppData. The difference is that LocalAppData is for anything that should not be saved to a roaming profile (ie. synchronised to a server), while regular (roaming) AppData is anything that could/should be synchronised.

Config would be something that makes sense to sync, while syncing a cache would just waste space as it could all just be redownloaded off the internet.

} else if (process.env.XDG_CONFIG_HOME) {
return path.join(process.env.XDG_CONFIG_HOME, 'yarn');
} else {
return FALLBACK_CONFIG_DIR;
}
}

function getLocalAppDataDir(): ?string {
return process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Yarn') : null;
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.