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 1 commit
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,132 @@
/* @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');
});

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

it('uses AppData\\Local\\Data otherwise', () => {
expect(getDataDir()).toBe(path.join(userHome, 'AppData', 'Local', 'Yarn', 'Data'));
});
});

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

it('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;
});

it('falls back to .local/share/yarn', () => {
expect(getDataDir()).toBe(path.join(userHome, '.local', 'share', 'yarn'));
});
});
});

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

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

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

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

it('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;
});

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

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

it('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;
});

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

});

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

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

it('uses AppData\\Local\\Config otherwise', () => {
expect(getConfigDir()).toBe(path.join(userHome, 'AppData', 'Local', 'Yarn', 'Config'));
});
});

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

it('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;
});

it('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,45 @@
/* @flow */

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

export function getDataDir(): string {
if (process.platform === 'win32') {
return path.join(getLocalAppDataDir(), '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, always use the typical location of XDG_DATA_HOME
return path.join(userHome, '.local', 'share', 'yarn');

This comment has been minimized.

Copy link
@bestander

bestander Feb 8, 2018

Member

I have a concern that this changes current setup people have, may cause confusion.

Before it was ~/.config/yarn/global, now it will be ~/.local/share/yarn/global.
Do we really need this change?

This comment has been minimized.

Copy link
@wbinnssmith

wbinnssmith Feb 8, 2018

Author Contributor

Honestly this is my main reason for the PR 😝

I thought it was really, really weird to be storing a ton of binaries in the user's configuration directory. The kind of stuff we put here is a perfect fit with XDG_DATA_HOME, which on many machines defaults to this location.

}
}

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(), '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 path.join(userHome, '.cache', 'yarn');
}
}

export function getConfigDir(): string {
if (process.platform === 'win32') {
return path.join(getLocalAppDataDir(), 'Config');
} else if (process.env.XDG_CONFIG_HOME) {
return path.join(process.env.XDG_CONFIG_HOME, 'yarn');
} else {
return path.join(userHome, '.config', 'yarn');
}
}

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