@adobe/sizewatcher 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  - [Requirements](#requirements)
15
15
  - [Installation](#installation)
16
16
  - [Usage](#usage)
17
- - [CI Setup](#ci-setup)
17
+ - [**CI Setup**](#ci-setup)
18
18
  - [Configuration](#configuration)
19
19
  - [Comparators reference](#comparators-reference)
20
20
  - [Contribute](#contribute)
@@ -33,6 +33,8 @@ While any custom file or folder path can be measured via configuration, various
33
33
 
34
34
  `sizewatcher` runs as part of your CI and reports results as comment on the pull request or as github commit status (optional), allowing to block PRs if a certain threshold was exceeded.
35
35
 
36
+ :warning: **The git size comparison feature (the `git` comparator) is currently not calculating correct values (as of 1.2.1). See [issue #52](https://github.com/adobe/sizewatcher/issues/52) for more details.**
37
+
36
38
  This is an example of a `sizewatcher` Github PR comment with a failure (ignore the small numbers):
37
39
 
38
40
  ---
@@ -125,7 +127,7 @@ By default `sizewatcher` will
125
127
 
126
128
  ## Requirements
127
129
 
128
- - [Nodejs](https://nodejs.org) version 10+
130
+ - [Nodejs](https://nodejs.org) version 12+ (since 1.3.0)
129
131
  - recommended to use latest stable version "LTS"
130
132
  - Github or Github Enterprise
131
133
  - if you want to run it on pull requests
@@ -137,12 +139,14 @@ By default `sizewatcher` will
137
139
 
138
140
  ## Installation
139
141
 
140
- You can install the `sizewatcher` tool locally for testing or checking your changes before committing:
142
+ Local: You can install the `sizewatcher` tool locally for testing or checking your changes before committing:
141
143
 
142
144
  ```
143
145
  npm install -g @adobe/sizewatcher
144
146
  ```
145
147
 
148
+ For setup in Continuous Integration: see [CI Setup](#ci-setup) section.
149
+
146
150
  ## Usage
147
151
 
148
152
  When run locally, `sizewatcher` will output its results on the command line.
@@ -230,7 +234,7 @@ npx @adobe/sizewatcher
230
234
  This command will always use the latest published version. In some cases it might be (temporarily) desireable to stick to a certain version, which can be achieved using:
231
235
 
232
236
  ```
233
- npx @adobe/sizewatcher@1.0.0
237
+ npx @adobe/sizewatcher@1.2.1
234
238
  ```
235
239
 
236
240
  #### GITHUB_TOKEN
@@ -289,7 +293,7 @@ jobs:
289
293
 
290
294
  For [Travis CI](https://travis-ci.org) you need to
291
295
  - use `language: node_js`
292
- - if you already use a different language, find a way to ensure Nodejs 10+ is installed
296
+ - if you already use a different language, find a way to ensure Nodejs 12+ is installed
293
297
  - under `script` run `npx @adobe/sizewatcher`
294
298
  - set a secret environment variable `GITHUB_TOKEN` in the [Travis repository settings](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings) with a github token with permission to comment on PRs and reporting commit statuses for the repository
295
299
 
@@ -309,7 +313,7 @@ script:
309
313
  ### CircleCI
310
314
 
311
315
  For [CircleCI](https://circleci.com) you need to
312
- - use a docker image with Nodejs 10+ installed
316
+ - use a docker image with Nodejs 12+ installed
313
317
  - alternatively install [using nvm](https://www.google.com/search?q=circleci+use+nvm)
314
318
  - run `npx @adobe/sizewatcher`
315
319
  - set a secret environment variable `GITHUB_TOKEN` in the [CircleCI project settings](https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-project) (or in a [Context](https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-context)) with a github token with permission to comment on PRs and reporting commit statuses for the repository
@@ -334,7 +338,7 @@ jobs:
334
338
 
335
339
  This is not tested well but might work.
336
340
 
337
- Ensure Nodejs 10+ is installed.
341
+ Ensure Nodejs 12+ is installed.
338
342
 
339
343
  Set these environment variables in the CI job:
340
344
 
package/index.js CHANGED
@@ -14,60 +14,5 @@
14
14
 
15
15
  'use strict';
16
16
 
17
- // index.js - main cli entry point
18
-
19
- const debug = require("debug")("sizewatcher");
20
- const gitCheckoutBeforeAndAfter = require("./lib/checkout");
21
- const compare = require("./lib/compare");
22
- const report = require("./lib/report");
23
- const Git = require("simple-git/promise");
24
-
25
- function printUsage() {
26
- console.log(`Usage: sizewatcher [<options>] [<before> [<after>]]`);
27
- console.log();
28
- console.log("Arguments:");
29
- console.log(" <before> Before branch/commit for comparison. Defaults to default branch or main/master.");
30
- console.log(" <after> After branch/commit for comparison. Defaults to current branch.");
31
- console.log();
32
- console.log("Options:");
33
- console.log(" -h Show help");
34
- }
35
-
36
- async function main(argv) {
37
- try {
38
- // yes, this should probably use a framework like oclif or yargs.
39
- // but cli arguments are very limited now and so manual handling is enough
40
- if (argv[0] === "-h") {
41
- printUsage();
42
- process.exit(1);
43
- }
44
-
45
- if (!await Git().checkIsRepo()) {
46
- throw new Error(`Not inside a git checkout: ${process.cwd()}`);
47
- }
48
-
49
- console.log(`Checking out git branches...`);
50
- const { before, after } = await gitCheckoutBeforeAndAfter(process.cwd(), argv[0], argv[1]);
51
-
52
- if (before.branch === after.branch) {
53
- console.log(`Branches are identical, nothing to compare (${before.branch}=${after.branch}). Compare selected branches using 'sizewatcher <before> <after>'.`);
54
- process.exit();
55
- }
56
-
57
- console.log(`Comparing changes from '${before.branch}' to '${after.branch}'\n`);
58
-
59
- const deltas = await compare(before, after);
60
-
61
- await report(deltas);
62
-
63
- } catch (e) {
64
- debug(e);
65
- console.error("Error:", e.message || e);
66
- process.exit(1);
67
- } finally {
68
- console.log("Done. Cleaning up...");
69
- }
70
- }
71
-
72
-
73
- main(process.argv.slice(2));
17
+ const sizewatcher = require("./lib/sizewatcher");
18
+ sizewatcher(process.argv.slice(2));
package/lib/checkout.js CHANGED
@@ -18,7 +18,7 @@ const debug = require("debug")("sizewatcher");
18
18
  const tmp = require("tmp");
19
19
  tmp.setGracefulCleanup();
20
20
  const path = require("path");
21
- const Git = require("simple-git/promise");
21
+ const Git = require("simple-git");
22
22
 
23
23
  async function getGitRoot(dir) {
24
24
  // --git-dir will return relative directories
@@ -43,10 +43,14 @@ async function currentSha(dir) {
43
43
  }
44
44
 
45
45
  async function getDefaultBranch(dir) {
46
- const originInfo = await Git(dir).remote(["show", "origin"]);
47
- const m = originInfo.match(/HEAD branch: (.*)$/m);
48
- if (m) {
49
- return m[1];
46
+ try {
47
+ const originInfo = await Git(dir).remote(["show", "origin"]);
48
+ const m = originInfo.match(/HEAD branch: (.*)$/m);
49
+ if (m) {
50
+ return m[1];
51
+ }
52
+ } catch (e) {
53
+ debug(`ignoring error in getDefaultBranch(): ${e.message}`);
50
54
  }
51
55
  }
52
56
 
@@ -54,22 +58,12 @@ async function hasBranch(dir, branch) {
54
58
  try {
55
59
  await Git(dir).revparse(["--verify", branch]);
56
60
  return branch;
57
- } catch (e) { // eslint-disable-line no-unused-vars
61
+ } catch (e) {
62
+ debug(`ignoring error in hasBranch(): ${e.message}`);
58
63
  return undefined;
59
64
  }
60
65
  }
61
66
 
62
- async function hasRemoteTrackingBranch(dir) {
63
- const git = Git(dir);
64
- try {
65
- const remoteBranch = await git.revparse(["--abbrev-ref", "--symbolic-full-name", "@{u}"]);
66
- console.log(remoteBranch);
67
- return true;
68
- } catch (e) { // eslint-disable-line no-unused-vars
69
- return false;
70
- }
71
- }
72
-
73
67
  async function getPullRequestBaseBranch() {
74
68
  return process.env.GITHUB_BASE_REF
75
69
  || (process.env.TRAVIS_PULL_REQUEST !== "false" && process.env.TRAVIS_BRANCH);
@@ -110,14 +104,18 @@ async function cloneLocalAndCheckout(from, to, branch) {
110
104
  const gitRoot = await getGitRoot(from);
111
105
 
112
106
  debug(`local cloning ${gitRoot} into ${to}`);
113
- await Git().clone(gitRoot, to, { local: true });
107
+ await Git().clone(gitRoot, to, { '--local' : true });
114
108
 
115
- debug(`checking out '${branch}'...`);
116
- await Git(to).checkout(branch);
109
+ if (branch) {
110
+ debug(`checking out '${branch}'...`);
111
+ await Git(to).checkout(branch);
112
+ }
117
113
  }
118
114
 
119
115
  async function gitCheckoutBeforeAndAfter(gitDir, beforeBranch, afterBranch) {
116
+ debug(`retrieving before branch: ${beforeBranch}`);
120
117
  beforeBranch = await getBeforeBranch(gitDir, beforeBranch);
118
+ debug(`retrieving after branch: ${afterBranch}`);
121
119
  afterBranch = await getAfterBranch(gitDir, afterBranch);
122
120
  if (beforeBranch === afterBranch) {
123
121
  return {
@@ -135,24 +133,33 @@ async function gitCheckoutBeforeAndAfter(gitDir, beforeBranch, afterBranch) {
135
133
  const tempDir = tmp.dirSync({unsafeCleanup: true}).name;
136
134
  debug(`temporary directory: ${tempDir}`);
137
135
 
136
+ // before checkout --------------------------------------------------------------
137
+
138
138
  const beforeDir = path.join(tempDir, "before");
139
- await cloneRemoteAndCheckout(gitDir, beforeDir, beforeBranch);
139
+
140
+ // we can use the faster local clone only if the "branch" is present locally
141
+ if (await hasBranch(gitDir, beforeBranch)) {
142
+ debug(`[before] using local cloning because existing checkout already has before branch '${beforeBranch}'`);
143
+ await cloneLocalAndCheckout(gitDir, beforeDir, beforeBranch);
144
+ } else {
145
+ debug(`[before] using remote cloning, as existing checkout does not have before branch '${beforeBranch}'`);
146
+ // ...otherwise need to do a full remote clone
147
+ await cloneRemoteAndCheckout(gitDir, beforeDir, beforeBranch);
148
+ }
149
+
150
+ // after checkout --------------------------------------------------------------
140
151
 
141
152
  const afterDir = path.join(tempDir, "after");
142
- if (process.env.CI || await hasRemoteTrackingBranch(afterDir)) {
143
- await cloneRemoteAndCheckout(gitDir, afterDir, afterBranch);
144
153
 
145
- } else {
146
- // if sizewatcher is run locally inside checkout
147
- await cloneLocalAndCheckout(gitDir, afterDir, afterBranch);
148
-
149
- try {
150
- // need to ensure before branch is available under local name
151
- await Git(afterDir).branch([beforeBranch, "-t", `origin/${beforeBranch}`]);
152
- } catch (e) {
153
- debug("ignoring error", e);
154
- }
154
+ debug(`[after] using local cloning`);
155
+ await cloneLocalAndCheckout(gitDir, afterDir);
156
+ try {
157
+ // need to ensure before branch is available under local name
158
+ await Git(afterDir).branch([beforeBranch, "-t", `origin/${beforeBranch}`]);
159
+ } catch (e) {
160
+ debug("ignoring error", e);
155
161
  }
162
+ debug(`[after] current branch: ${await currentBranch(afterDir)}`);
156
163
 
157
164
  debug("folders ready");
158
165
 
@@ -19,7 +19,7 @@ const du = require("du");
19
19
  const path = require("path");
20
20
  const fs = require("fs");
21
21
  const { execSync } = require("child_process");
22
- const Git = require("simple-git/promise");
22
+ const Git = require("simple-git");
23
23
 
24
24
  async function getSize(dir) {
25
25
  debug(`git garbage collection of '${dir}'...`);
@@ -50,7 +50,7 @@ module.exports = {
50
50
  compare: async function(before, after) {
51
51
  const beforeSize = await getSize(before.dir);
52
52
  const afterSize = await getSize(after.dir);
53
- const details = await largestCommits(after.dir, before.branch, after.branch);
53
+ const details = await largestCommits(after.dir, before.branch, after.sha);
54
54
 
55
55
  return {
56
56
  beforeSize,
@@ -24,19 +24,36 @@ function isEmptyObject(obj) {
24
24
  return Object.keys(obj).length === 0 && obj.constructor === Object;
25
25
  }
26
26
 
27
+ function hasPackageJson(dir) {
28
+ return fs.existsSync(path.join(dir, "package.json"));
29
+ }
30
+
27
31
  async function getSize(dir) {
32
+ if (!hasPackageJson(dir)) {
33
+ return 0;
34
+ }
28
35
  process.chdir(dir);
36
+
29
37
  debug(`installing node dependencies inside ${dir}...`);
30
38
  if (fs.existsSync("package-lock.json") || fs.existsSync("npm-shrinkwrap.json")) {
31
39
  execSync("npm ci");
32
40
  } else {
33
41
  execSync("npm install");
34
42
  }
35
- debug(`calculating folder size of ${dir}/node_modules...`);
36
- return du(path.join(dir, "node_modules"));
43
+
44
+ const nodeModules = path.join(dir, "node_modules");
45
+ if (fs.existsSync(nodeModules)) {
46
+ debug(`calculating folder size of ${nodeModules}...`);
47
+ return du(nodeModules);
48
+ } else {
49
+ return 0;
50
+ }
37
51
  }
38
52
 
39
53
  async function costOfModules(dir) {
54
+ if (!hasPackageJson(dir)) {
55
+ return "(no package.json)";
56
+ }
40
57
  process.chdir(dir);
41
58
 
42
59
  const pkgJsonPath = path.join(dir, "package.json");
@@ -62,7 +79,7 @@ async function costOfModules(dir) {
62
79
  module.exports = {
63
80
 
64
81
  shouldRun: async function(beforeDir, afterDir) {
65
- return fs.existsSync(path.join(afterDir, "package.json"));
82
+ return hasPackageJson(beforeDir) || hasPackageJson(afterDir);
66
83
  },
67
84
 
68
85
  compare: async function(before, after) {
package/lib/compare.js CHANGED
@@ -136,6 +136,7 @@ function process(deltas) {
136
136
  };
137
137
 
138
138
  for (const d of deltas) {
139
+ debug("processing delta:", d);
139
140
  if (d.error) {
140
141
  summary.error++;
141
142
  continue;
@@ -147,14 +148,14 @@ function process(deltas) {
147
148
  };
148
149
 
149
150
  let increase;
150
- if (d.beforeSize <= 0) {
151
- if (d.afterSize <= 0) {
152
- increase = 0;
153
- } else {
154
- increase = -100;
155
- }
151
+ if (d.beforeSize <= 0 && d.afterSize <= 0) {
152
+ increase = 0;
153
+ } else if (d.afterSize <= 0) {
154
+ increase = -100;
155
+ } else if (d.beforeSize <= 0) {
156
+ increase = 100;
156
157
  } else {
157
- increase = d.beforeSize <= 0 ? -100 : (d.afterSize - d.beforeSize) / d.beforeSize * 100;
158
+ increase = (d.afterSize - d.beforeSize) / d.beforeSize * 100;
158
159
  }
159
160
  d.increase = increase.toFixed(1);
160
161
 
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ // main cli entry point
16
+
17
+ const debug = require("debug")("sizewatcher");
18
+ const gitCheckoutBeforeAndAfter = require("./checkout");
19
+ const compare = require("./compare");
20
+ const report = require("./report");
21
+ const Git = require("simple-git");
22
+
23
+ function printUsage() {
24
+ console.error(`Usage: sizewatcher [<options>] [<before> [<after>]]`);
25
+ console.error();
26
+ console.error("Arguments:");
27
+ console.error(" <before> Before branch/commit for comparison. Defaults to default branch or main/master.");
28
+ console.error(" <after> After branch/commit for comparison. Defaults to current branch.");
29
+ console.error();
30
+ console.error("Options:");
31
+ console.error(" -h Show help");
32
+ }
33
+
34
+ async function sizewatcher(argv) {
35
+ try {
36
+ if (!argv) argv = [];
37
+
38
+ // yes, this should probably use a framework like oclif or yargs.
39
+ // but cli arguments are very limited now and so manual handling is enough
40
+ if (argv[0] === "-h") {
41
+ printUsage();
42
+ return process.exit(1);
43
+ }
44
+
45
+ debug(`Running inside ${process.cwd()}`);
46
+
47
+ if (!await Git().checkIsRepo()) {
48
+ throw new Error(`Not inside a git checkout: ${process.cwd()}`);
49
+ }
50
+
51
+ console.log(`Checking out git branches...`);
52
+ const { before, after } = await gitCheckoutBeforeAndAfter(process.cwd(), argv[0], argv[1]);
53
+
54
+ if (before.branch === after.branch) {
55
+ console.log(`Branches are identical, nothing to compare (${before.branch}=${after.branch}). Compare selected branches using 'sizewatcher <before> <after>'.`);
56
+ return process.exit();
57
+ }
58
+
59
+ console.log(`Comparing changes from '${before.branch}' to '${after.branch}'\n`);
60
+
61
+ const deltas = await compare(before, after);
62
+
63
+ await report(deltas);
64
+
65
+ console.log("Done. Cleaning up...");
66
+
67
+ } catch (e) {
68
+ debug(e);
69
+ console.error("Error:", e.message || e);
70
+ return process.exit(1);
71
+ }
72
+ }
73
+
74
+ module.exports = sizewatcher;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/sizewatcher",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Warns if your pull requests introduce large size increases.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -28,9 +28,10 @@
28
28
  "@adobe/eslint-config-asset-compute": "^1.3.0",
29
29
  "coveralls": "^3.1.0",
30
30
  "eslint": "^7.1.0",
31
- "mocha": "^7.2.0",
31
+ "mocha": "^10.2.0",
32
32
  "mock-fs": "^4.13.0",
33
- "nyc": "^15.1.0"
33
+ "nyc": "^15.1.0",
34
+ "supports-color": "^8.1.1"
34
35
  },
35
36
  "dependencies": {
36
37
  "@octokit/rest": "^18.0.3",
@@ -41,7 +42,7 @@
41
42
  "js-yaml": "^3.14.0",
42
43
  "pretty-bytes": "^5.3.0",
43
44
  "require-dir": "^1.2.0",
44
- "simple-git": "^2.5.0",
45
+ "simple-git": "^3.7.1",
45
46
  "tmp": "^0.2.1",
46
47
  "xbytes": "^1.6.2"
47
48
  }