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

fix: save artifacts and assets in output path #1601

Merged
merged 8 commits into from
Feb 7, 2017
Merged

Conversation

patrickhulce
Copy link
Collaborator

@patrickhulce patrickhulce commented Feb 2, 2017

More changes needed I came across during WPT integration investigation.

  • Default report when output is pretty will use same name and location as output path.
  • Artifacts will use same name and location as output path or will use getFilenamePrefix and be suffixed with .artifacts.log
  • Assets will use same name and location as output path.

Examples

--output-path=~/mydir/foo.out --save-assets generates

  • ~/mydir/foo.out
  • ~/mydir/foo.report.html
  • ~/mydir/foo-0.trace.json
  • ~/mydir/foo-0.screenshots.html

--output-path=./report.json --output json --save-artifacts generates

  • ./report.json
  • ./report.artifacts.log

--save-artifacts generates

  • ./<HOST>_<DATE>.report.html
  • ./<HOST>_<DATE>.artifacts.log

@patrickhulce patrickhulce changed the title fix: save artifacts in output path fix: save artifacts and assets in output path Feb 2, 2017
const traceData = data.traceData;
fs.writeFileSync(`${filenamePrefix}-${index}.trace.json`, JSON.stringify(traceData, null, 2));
log.log('trace file saved to disk', filenamePrefix);
const traceFilename = `${options.path}-${index}.trace.json`;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not super thrilled about calling this mixture of path and prefix path but passing both options everywhere seemed like overkill. My arm could be twisted to revert though... :)

Copy link
Member

Choose a reason for hiding this comment

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

isn't it just passed to this one function? agreed that calling it path is kind of confusing

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's this and saveArtifacts and used back in bin.ts

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

initial comments. My brain may just be tired, but it does seem like some new variable names may add some clarity here...but it may also just be the nature of the save*() beast we've created.

@@ -260,25 +260,31 @@ function runLighthouse(url: string,
.then(chrome => chromeLauncher = chrome)
.then(() => lighthouse(url, flags, config))
.then((results: Results) => {
let promise = Promise.resolve(results);
Copy link
Member

Choose a reason for hiding this comment

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

maybe move all this printing stuff to its own function? Definitely getting hard to follow :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will do

// delete artifacts from result so reports won't include artifacts.
const artifacts = results.artifacts;
results.artifacts = undefined;

if (flags.saveArtifacts) {
assetSaver.saveArtifacts(artifacts);
promise = promise.then(_ => assetSaver.saveArtifacts(artifacts, resolvedPath));
Copy link
Member

Choose a reason for hiding this comment

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

saveArtifacts is sync, so does wrapping it matter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I suppose not, it just seemed like the odd duckling before

* @return {!Promise}
*/
function saveAssets(artifacts, results) {
return prepareAssets(artifacts, results).then(assets => {
function saveAssets(artifacts, options = {}) {
Copy link
Member

Choose a reason for hiding this comment

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

this doesn't appear to be treated as an optional parameter, so any need for the default?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah ended up not being optional anymore, I'll remove

const traceData = data.traceData;
fs.writeFileSync(`${filenamePrefix}-${index}.trace.json`, JSON.stringify(traceData, null, 2));
log.log('trace file saved to disk', filenamePrefix);
const traceFilename = `${options.path}-${index}.trace.json`;
Copy link
Member

Choose a reason for hiding this comment

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

isn't it just passed to this one function? agreed that calling it path is kind of confusing

}
if (flags.saveAssets) {
return assetSaver.saveAssets(artifacts, results).then(() => results);
promise = promise.then(_ => assetSaver.saveAssets(artifacts, {
title: path.basename(resolvedPath),
Copy link
Member

Choose a reason for hiding this comment

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

not really clear what title is here, or why it's called that. file prefix?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it becomes the title of the screenshots HTML page, which was essentially this basename before but it needed all the results passed in to compute it

@patrickhulce
Copy link
Collaborator Author

alright @brendankenny moved some things around and just got rid of title completely for now, hopefully it's clearer now

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

some more minor stuff. Definitely like the flow of this better. pwmetrics-events.js threw a wrench in things, though :)

resolvedPath.slice(cwd.length + 1) : resolvedPath;

// delete artifacts from result so reports won't include artifacts.
const artifacts = results.artifacts;
Copy link
Member

Choose a reason for hiding this comment

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

maybe keep this in the caller and pass in artifacts as a separate argument to saveResults? Feels weird to remove them from the results in a function that's just saving things

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it feels weird have this part of the saving logic outside saveResults haha, compromise? results = Object.assign({}, results, {artifacts: undefined});

@@ -90,24 +89,23 @@ img {
/**
* Save entire artifacts object to a single stringified file
Copy link
Member

Choose a reason for hiding this comment

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

maybe add a line to the jsdoc that artifacts will be saved at pathWithBasename + '.artifacts.log'

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

@@ -131,19 +129,20 @@ function prepareAssets(artifacts, results) {
/**
* Writes trace(s) and associated screenshot(s) to disk.
* @param {!Artifacts} artifacts
* @param {!Results} results
* @param {string} pathWithBasename
Copy link
Member

Choose a reason for hiding this comment

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

same thing with jsdoc, pathWithBasename + '.trace.json' and screenshots (though I guess the index thing makes that slightly harder to understand :)

flags: {output: any, outputPath: string, saveArtifacts: boolean, saveAssets: boolean}) {
let promise = Promise.resolve(results);
const cwd = process.cwd();
const configuredPath = !flags.outputPath || flags.outputPath === 'stdout' ?
Copy link
Member

Choose a reason for hiding this comment

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

it would be great to get some of the PR's first comment captured here or in a doc on the method (readme too?).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

flags.outputPath.replace(/\.\w{2,4}$/, '');
const resolvedPath = path.resolve(cwd, configuredPath);
const pathWithBasename = resolvedPath.includes(cwd) ?
resolvedPath.slice(cwd.length + 1) : resolvedPath;
Copy link
Member

Choose a reason for hiding this comment

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

maybe i'm missing something obvious, but what does slicing accomplish here?

Copy link
Member

Choose a reason for hiding this comment

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

my read is that resolvedPath always includes cwd because we just did path.resolve on it.
and so we'll always slice,
taking the absolute file path back to a relative one.

This is 1) the same as path.relative(cwd, resolvedPath) AFAICT. but 2) i don't know why we nuke it, since its a more absolute location.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just nuked because with the default options the logging seemed a bit verbose to restate your cwd, don't have particularly strong feelings about it though if we're fine with the absolute path being printed.

const cwd = process.cwd();
const configuredPath = !flags.outputPath || flags.outputPath === 'stdout' ?
assetSaver.getFilenamePrefix(results) :
flags.outputPath.replace(/\.\w{2,4}$/, '');
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 this edit we could do...

const parsed = path.parse(flags.outputPath); 
parsed.base = parsed.base.replace(parsed.ext, '');
path.format(parsed);

but i don't know if that's really worth it. :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like the explicitness of it but not a huge fan of the triple lines, haha :)

results.artifacts = undefined;

if (flags.saveArtifacts) {
assetSaver.saveArtifacts(artifacts, pathWithBasename);
Copy link
Member

Choose a reason for hiding this comment

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

as mentioned offline, we can remove --save-artifacts flag and the functions if we want. AFAIK, they havent been used for ages and are only for debugging.

flags.outputPath.replace(/\.\w{2,4}$/, '');
const resolvedPath = path.resolve(cwd, configuredPath);
const pathWithBasename = resolvedPath.includes(cwd) ?
resolvedPath.slice(cwd.length + 1) : resolvedPath;
Copy link
Member

Choose a reason for hiding this comment

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

my read is that resolvedPath always includes cwd because we just did path.resolve on it.
and so we'll always slice,
taking the absolute file path back to a relative one.

This is 1) the same as path.relative(cwd, resolvedPath) AFAICT. but 2) i don't know why we nuke it, since its a more absolute location.

@@ -164,6 +164,23 @@ Options:
installations are found [boolean]
```

### Output Path Examples

`--output-path=~/mydir/foo.out --save-assets` generates
Copy link
Member

Choose a reason for hiding this comment

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

I don't believe this actually works, though, because we don't do ~ expansion

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

depends on your shell I suppose, should I change to an absolute path?

Copy link
Member

Choose a reason for hiding this comment

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

depends on your shell I suppose

ah, yeah, I have to do --output-path ~/mydir/foo.out for it to expand

const audits = results.audits;
const artifacts = results.artifacts;
// remove artifacts from result so reports won't include artifacts.
results = Object.assign({}, results, {artifacts: undefined});
Copy link
Member

Choose a reason for hiding this comment

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

this is fine (it makes some sense, we are dropping artifacts since we don't want the them in the saved/printed results), but since this resolves on the changed results I don't see the difference functionally?

So it still seems weird, but now it's that perfX relies on that side effect of saveResults to not get 100MB reports :) Not sure of the optimal configuration here, though

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

only changes in the sense that the original results object is preserved, if we never intend artifacts on being consumed along with the rest of the results I'd advocate splitting them into distinct sets in the lighthouse return object. Since that'd probably break others at this point, I'll restructure a bit more instead.

flags.outputPath.replace(/\.\w{2,4}$/, '');
const resolvedPath = path.resolve(cwd, configuredPath);
const pathWithBasename = resolvedPath.includes(cwd) ?
path.relative(cwd, resolvedPath) : resolvedPath;
Copy link
Member

Choose a reason for hiding this comment

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

I'm personally +1 for dropping this line since it definitely adds mental overhead when reading. If it's annoying in the log we could fix it where it's logged instead

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will do

promise = promise.then(_ => Printer.write(results, 'html', `${pathWithBasename}.report.html`));
}

return promise.then(_ => Printer.write(results, flags.output, flags.outputPath));
Copy link
Member

Choose a reason for hiding this comment

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

you're inheriting this, but it seems kind of magical that we rely on Printer.write to return results. How do you feel about doing a resolve on results here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed the need for it to return anything at all

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

LGTM!

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.

None yet

3 participants