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

Save cached config to standard directories #38

Merged
merged 17 commits into from
Jul 14, 2017
Merged

Save cached config to standard directories #38

merged 17 commits into from
Jul 14, 2017

Conversation

Siilwyn
Copy link
Contributor

@Siilwyn Siilwyn commented May 8, 2017

Instead of placing the configuration file in the home directory it should be placed in the appropriate directory per OS.

Breaking change: resolves issue #37

@Siilwyn Siilwyn changed the title WIP: Save cached config to standard directories Save cached config to standard directories May 8, 2017
@Siilwyn
Copy link
Contributor Author

Siilwyn commented May 8, 2017

@phated & @tkellen ready for review!

Copy link
Member

@phated phated left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This can be reduced by a lot though. There also needs to be tests over the new .js file.

env-paths.js Outdated
const path = require('path');
const os = require('os');

const homedir = require('user-home');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@Siilwyn Siilwyn May 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm simply using user-home because it's already a dependency.

Edit: I looked into using the homedir-polyfill but would rather stick with user-home because it has no dependencies and is used a bit more.

env-paths.js Outdated
const appData = env.LOCALAPPDATA || path.join(homedir, 'AppData', 'Local');

return {
// data/config/cache/log are invented by me as Windows isn't opinionated about this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a point in ripping this module verbatim. The usage just plucks the .cache property so the API should be reduced to that use case.

index.js Outdated

const configfile = '.v8flags.'+process.versions.v8+'.'+crypto.createHash('md5').update(user).digest('hex')+'.json';
const configPath = envPaths('js-v8flags').cache;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API should be reduced to just return the .cache property (and not generate objects with a bunch of properties). This also removes the need for attribution since it isn't even the same module anymore.

if (err) return tryOpenConfig(path.join(os.tmpdir(), configfile), cb);
return cb(null, fd);
});
fs.mkdir(configPath, function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this need to be mkdirp? By that I mean, is there a chance the multiple directory paths will need to be created?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I'm not sure what the proper solution is here. It should only create one directory unless the user deleted a default OS directory which would probably also break other apps...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can definitely count on some environments not being able to make this directory. It's a crazy world out there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tkellen I understand but regardless of an error or not tryOpenConfig will be called so if the directory can not be created os.tmpdir will be used. Is that enough or would using mkdirp be better in some case?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, by "multiple" I think @phated meant nested. It doesn't seem unlikely that the user would define a nested directory in config, in which case you would either need to recreate the logic in mkdirp and loop over the directory segments to create each one, or just use mkdirp so you don't need to think about edge cases.

also, is there a reason the error isn't being handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I understand @phated meant nested directories. With my reply I meant that it's nested inside a default OS directory so I'm wondering if the case needs to be handled.

Regarding error handling, if for some reason we can't create the directory the next function tryOpenConfig will handle the logic of falling back to a temporary directory so that's why there is no need to handle it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you handle the error (and it's not EEXIST - which you wouldn't need to worry about with mkdirp) you could avoid an extra tryOpenConfig on the configPath before attempting tmpdir, right?

Copy link
Contributor Author

@Siilwyn Siilwyn May 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's true. Do you think using mkdirp has enough benefit: avoiding an extra tryOpenConfig in edge cases to justify including this new dependency?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if it isn't worth the added dependency (not sure if so), checking the error might still be worth it.

cache-paths.js Outdated

const env = process.env;

const macos = function (name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer named functions over function assignments

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of taking a name, can just have 'js-v8flags' assigned as a variable at the top of file.

cache-paths.js Outdated
return path.join(library, 'Caches', name);
};

const windows = function (name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer named functions over function assignments

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of taking a name, can just have 'js-v8flags' assigned as a variable at the top of file.

cache-paths.js Outdated
};

// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
const linux = function (name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer named functions over function assignments

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of taking a name, can just have 'js-v8flags' assigned as a variable at the top of file.

cache-paths.js Outdated
return path.join(env.XDG_CACHE_HOME || path.join(homedir, '.cache'), name);
};

module.exports = function (name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calling this as a function, it should probably just export the proper path based on platform

if (err) return tryOpenConfig(path.join(os.tmpdir(), configfile), cb);
return cb(null, fd);
});
fs.mkdir(configPath, function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you handle the error (and it's not EEXIST - which you wouldn't need to worry about with mkdirp) you could avoid an extra tryOpenConfig on the configPath before attempting tmpdir, right?

config-path.js Outdated
module.exports = windows();
}

module.exports = linux();
Copy link
Member

@phated phated May 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always overwrite the value with the result of linux(). I believe this outlines why this needs extra unit tests added.

Copy link
Contributor Author

@Siilwyn Siilwyn May 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh whoops my bad, it's because I quickly removed it from the function late in the evening ^^.
What kind of unit tests would you add, hardcode the expected path in the test?

@Siilwyn
Copy link
Contributor Author

Siilwyn commented May 10, 2017

For some reason the CI test on Node 0.10 fails at one unit test and I don't understand why. Any help would be appreciated! :)

config-path.js Outdated
if (process.platform === 'darwin') {
module.exports = macos();
} else if (process.platform === 'win32') {
module.exports = windows();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space after =

config-path.js Outdated
function macos () {
const library = path.join(userHome, 'Library');
return path.join(library, 'Caches', name);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no semi-colon after named functions

config-path.js Outdated
function windows () {
const appData = env.LOCALAPPDATA || path.join(userHome, 'AppData', 'Local');
return path.join(appData, name, 'Cache');
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no semi-colon after named functions

config-path.js Outdated
function linux () {
const username = path.basename(userHome);
return path.join(env.XDG_CACHE_HOME || path.join(userHome, '.cache'), name);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no semi-colon after named functions

test.js Outdated
afterEach(cleanup);

it('should return default linux path in other environments', function(done) {
Object.defineProperty(process, 'platform', {value: 'other'});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be reset/cleaned-up

test.js Outdated
});

it('should return default macos path in darwin environment', function(done) {
Object.defineProperty(process, 'platform', {value: 'darwin'});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be reset/cleaned-up

test.js Outdated
});

it('should return default windows path in win32 environment', function(done) {
Object.defineProperty(process, 'platform', {value: 'win32'});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be reset/cleaned-up

config-path.js Outdated
return path.join(env.XDG_CACHE_HOME || path.join(userHome, '.cache'), name);
}

if (process.platform === 'darwin') {
Copy link

@tkellen tkellen May 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I doubt it matters for this module, this approach for dynamic exporting makes it impossible to statically analyze this. Can you update this to export a single function that takes process.platform as an argument and returns the correct result accordingly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You realize that it was initially exporting a function but phated told me to change it?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, I did not--I have not been watching as closely as he has. Mind sharing what you think the most appropriate approach is here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, I'm not sure as both ways of their pros and cons. Think as it is now it's very specific to this package so I would leave it as it is. When this package drops support for < Node 4 ideally we would swap it with the env-paths package, by then the 'new ES import' will be in place too so it has more significance to be able to statically analyze it.
🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speaking of Node 0.10, do you have any idea why it's failing in CI? Really need some help to get that fixed. :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phated yes I already tried debugging locally but can't figure it out. Node 0.10 is acting weird, test doesn't find the user-home module when process.platform is win32. Just changing process.platform is enough to make it work. :/
All newer Node versions work fine.

Copy link
Contributor Author

@Siilwyn Siilwyn May 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright after giving this more debugging time I'm still stuck. What do you think of reversing the earlier change and returning a function instead passing it the platform instead of using a global?
Or dropping 0.10 support but you probably have a reason not to ...?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aren't dropping 0.10 support because it is still the latest node version shipped in official channels on some linux.

I don't like returning the function; we should really just figure out what is going on here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not returning the function; it’s exporting it.

module.exports = function (process) {
    if (process.platform === ) {
        
    }
};

would make it possible to pass an object in in tests without changing process itself, which is what causes the breakage on 0.10, and additionally clean up the require cache hack.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, bad wording from my end. That's what I meant. @phated thoughts?

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Jun 7, 2017

It's working with all versions now.

config-path.js Outdated
}

return linux();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a semicolon.

config-path.js Outdated
const name = 'js-v8flags';

function macos () {
const library = path.join(userHome, 'Library');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems as though this project uses two-space indentation, not tabs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this project really needs an editorconfig.

test.js Outdated
@@ -16,6 +16,9 @@ function eraseHome() {
delete env.USER;
delete env.LNAME;
delete env.USERNAME;
delete env.XDG_CACHE_HOME;
delete env.LOCALAPPDATA;
delete process.platform;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn’t seem very nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly needed though.

afterEach(cleanup);

it('should return default linux path in other environments', function(done) {
delete require.cache[require.resolve('./config-path.js')];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’d be best to make it so it isn’t, then. Is this somehow clearing the cache for user-home?

Copy link
Contributor Author

@Siilwyn Siilwyn Jun 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while back but I think it does yes. I consider it bad but this pattern is used in other existing tests too. The best way to test this would be in sandboxes but that falls outside the scope of this PR.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Jun 19, 2017

@tkellen & @phated thoughts?

@phated
Copy link
Member

phated commented Jun 20, 2017

@Siilwyn I needed to put this on the backburner to push through the last couple of gulp4 blockers. I'll do a more in-depth review of everything once that is shipped.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Jun 21, 2017

Okay nice! I already love using Gulp 4 in the alpha.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Jun 27, 2017

@tkellen do you have some time to review in advance?

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Jul 13, 2017

@tkellen could you review the PR? :)

@tkellen
Copy link

tkellen commented Jul 14, 2017

Apologies for the delay @Siilwyn. I've just added you as a collaborator on this repo / author on npm. Thank you for this work!

I'm actually leaving for a much needed vacation this morning and will be offline for the next week. If you'd like to land and publish this, please feel free to do so as long as you keep a close eye on the issue tracker and fix any issues that crop up as a result.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Jul 14, 2017

Thank you for your reply and the given trust @tkellen! I'll let this sit for a bit more today so there's some room for further replies.

Enjoy your vacation, going offline for a while is always refreshing! ⛱️

@Siilwyn Siilwyn merged commit a1845cc into gulpjs:master Jul 14, 2017
@Siilwyn Siilwyn deleted the cache-to-standard-directory branch July 14, 2017 19:03
elhigu pushed a commit to knex/knex that referenced this pull request Oct 25, 2017
The v8flags dependency got updated so that it follows the OS cache location conventions. See [the PR](gulpjs/v8flags#38) for more information.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants