Skip to content
Merged
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A GitHub action that reports changes in compressed file sizes on your PRs.

- Automatically uses `yarn`, `pnpm`, `bun`, or `npm ci` when lockfiles are present
- Bring your own toolchain by using clean, install, and build scripts
Comment thread
unrevised6419 marked this conversation as resolved.
Outdated
- Builds your PR, then builds the target and compares between the two
- Doesn't upload anything or rely on centralized storage
- Supports [custom build scripts](#customizing-the-build) and [file patterns](#customizing-the-list-of-files)
Expand All @@ -26,7 +26,11 @@ jobs:

steps:
- uses: actions/checkout@v2
- uses: preactjs/compressed-size-action@v2
- uses: preactjs/compressed-size-action@v3
with:
install-script: npm ci
build-script: npm run build
clean-script: npm run clean
```

### Customizing the Installation
Expand Down
54 changes: 27 additions & 27 deletions action.yml
Comment thread
unrevised6419 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
name: 'compressed-size-action'
description: 'Get compressed size differences for every PR'
author: 'Jason Miller'
name: "compressed-size-action"
description: "Get compressed size differences for every PR"
author: "Jason Miller"
branding:
icon: 'archive'
color: 'purple'
icon: "archive"
color: "purple"
inputs:
repo-token:
description: 'The GITHUB_TOKEN secret'
description: "The GITHUB_TOKEN secret"
required: false
default: ${{ github.token }}
clean-script:
description: 'An npm-script that cleans/resets state between branch builds'
description: "A script that cleans/resets state between branch builds"
install-script:
required: false
Comment thread
rschristian marked this conversation as resolved.
description: 'Custom installation script to run to set up the dependencies in your project'
required: true
description: "Custom installation script to run to set up the dependencies in your project"
build-script:
description: 'The npm-script to run that builds your project'
default: 'build'
required: true
description: "The script to run that builds your project"
compression:
description: 'The compression algorithm to use: "gzip" or "brotli"'
show-total:
description: 'Show total size and difference.'
default: 'true'
description: "Show total size and difference."
default: "true"
collapse-unchanged:
description: 'Move unchanged files into a separate collapsed table'
default: 'true'
description: "Move unchanged files into a separate collapsed table"
default: "true"
omit-unchanged:
description: 'Exclude unchanged files from the sizes table entirely'
description: "Exclude unchanged files from the sizes table entirely"
strip-hash:
description: 'A regular expression to remove hashes from filenames. Submatches are turned into asterisks if present, otherwise the whole match is removed.'
description: "A regular expression to remove hashes from filenames. Submatches are turned into asterisks if present, otherwise the whole match is removed."
use-check:
description: 'Report status as a CI Check instead of using a comment [experimental]'
description: "Report status as a CI Check instead of using a comment [experimental]"
minimum-change-threshold:
description: 'Consider files with changes below this threshold as unchanged. Specified in bytes.'
default: 1
description: "Consider files with changes below this threshold as unchanged. Specified in bytes."
default: "1"
pattern:
description: 'minimatch pattern of files to track'
description: "minimatch pattern of files to track"
exclude:
description: 'minimatch pattern of files NOT to track'
description: "minimatch pattern of files NOT to track"
cwd:
description: 'A custom working directory to execute the action in relative to repo root (defaults to .)'
description: "A custom working directory to execute the action in relative to repo root (defaults to .)"
comment-key:
description: 'Optional key to include in the bot comment to allow for multiple bundle calculations to be posted in separate comments.'
description: "Optional key to include in the bot comment to allow for multiple bundle calculations to be posted in separate comments."
sort-by:
description: 'The column and direction to sort the results by. The format is "column:direction", where column is one of "Filename", "Size", or "Change" and direction is "asc" or "desc". For example, "Size:desc" sorts the table by file size in descending order.'
default: 'Filename:asc'
default: "Filename:asc"

runs:
using: 'node20'
main: 'index.js'
using: "node20"
main: "index.js"
6 changes: 4 additions & 2 deletions jsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"module": "nodenext",
Comment thread
unrevised6419 marked this conversation as resolved.
Outdated
"moduleResolution": "NodeNext",
"allowJs": true,
"checkJs": true,
"resolveJsonModule": true,
"noEmit": true
"noEmit": true,
"strict": true,
"useUnknownInCatchVariables": false
Comment thread
unrevised6419 marked this conversation as resolved.
Outdated
}
}
46 changes: 19 additions & 27 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// @ts-check

import { getInput, setFailed, startGroup, endGroup, debug } from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { exec } from '@actions/exec';
import SizePlugin from 'size-plugin-core';
import { getPackageManagerAndInstallScript, diffTable, toBool, stripHash, getSortOrder } from './utils.js';
import { diffTable, toBool, stripHash, getSortOrder } from './utils.js';

/**
* @typedef {ReturnType<typeof import("@actions/github").getOctokit>} Octokit
Expand All @@ -17,7 +19,7 @@ async function run(octokit, context, token) {
// const pr = (await octokit.pulls.get({ owner, repo, pull_number })).data;
try {
debug('pr' + JSON.stringify(context.payload, null, 2));
} catch (e) {}
} catch (e) { }

let baseSha, baseRef;
if (context.eventName == 'push') {
Expand All @@ -27,8 +29,8 @@ async function run(octokit, context, token) {
console.log(`Pushed new commit on top of ${baseRef} (${baseSha})`);
} else if (context.eventName == 'pull_request' || context.eventName == 'pull_request_target') {
const pr = context.payload.pull_request;
baseSha = pr.base.sha;
baseRef = pr.base.ref;
baseSha = pr?.base.sha;
baseRef = pr?.base.ref;

console.log(`PR #${pull_number} is targeted at ${baseRef} (${baseRef})`);
} else {
Expand All @@ -46,22 +48,17 @@ async function run(octokit, context, token) {
stripHash: stripHash(getInput('strip-hash'))
});

const buildScript = getInput('build-script') || 'build';
const buildScript = getInput('build-script', { required: true });
Comment thread
rschristian marked this conversation as resolved.
const cwd = process.cwd();

let { packageManager, installScript } = await getPackageManagerAndInstallScript(cwd);
if (getInput('install-script')) {
installScript = getInput('install-script');
}
const installScript = getInput('install-script', { required: true });

startGroup(`[current] Install Dependencies`);
console.log(`Installing using ${installScript}`);
Comment thread
rschristian marked this conversation as resolved.
await exec(installScript);
endGroup();

startGroup(`[current] Build using ${packageManager}`);
console.log(`Building using ${packageManager} run ${buildScript}`);
Comment thread
rschristian marked this conversation as resolved.
await exec(`${packageManager} run ${buildScript}`);
startGroup(`[current] Building`);
await exec(buildScript);
endGroup();

// In case the build step alters a JSON-file, ....
Expand Down Expand Up @@ -98,26 +95,20 @@ async function run(octokit, context, token) {
}
endGroup();

/** @type {string|undefined} */
const cleanScript = getInput('clean-script');
if (cleanScript) {
startGroup(`[base] Cleanup via ${packageManager} run ${cleanScript}`);
await exec(`${packageManager} run ${cleanScript}`);
startGroup(`[base] Cleanup`);
await exec(cleanScript);
endGroup();
}

startGroup(`[base] Install Dependencies`);

({ packageManager, installScript } = await getPackageManagerAndInstallScript(cwd));
if (getInput('install-script')) {
installScript = getInput('install-script');
}

console.log(`Installing using ${installScript}`);
await exec(installScript);
endGroup();

startGroup(`[base] Build using ${packageManager}`);
await exec(`${packageManager} run ${buildScript}`);
startGroup(`[base] Building`);
await exec(buildScript);
endGroup();

// In case the build step alters a JSON-file, ....
Expand Down Expand Up @@ -178,9 +169,9 @@ async function run(octokit, context, token) {
try {
const comments = (await octokit.issues.listComments(commentInfo)).data;
const commentRegExp = new RegExp(`<sub>[\s\n]*(compressed|gzip)-size-action${commentKey ? `::${commentKey}` : ''}</sub>`)
for (let i = comments.length; i--; ) {
for (let i = comments.length; i--;) {
Comment thread
unrevised6419 marked this conversation as resolved.
Outdated
const c = comments[i];
if (commentRegExp.test(c.body)) {
if (c.body && commentRegExp.test(c.body)) {
commentId = c.id;
break;
}
Expand Down Expand Up @@ -252,10 +243,11 @@ async function createCheck(octokit, context) {
const check = await octokit.checks.create({
...context.repo,
name: 'Compressed Size',
head_sha: context.payload.pull_request.head.sha,
head_sha: context.payload.pull_request?.head.sha,
status: 'in_progress'
});

/** @param {object} details */
return async (details) => {
await octokit.checks.update({
...context.repo,
Expand Down
34 changes: 1 addition & 33 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,6 @@
import fs from 'fs';
import path from 'path';
import prettyBytes from 'pretty-bytes';

/**
* @param {string} cwd
* @returns {Promise<{ packageManager: string, installScript: string }>}
*/
export async function getPackageManagerAndInstallScript(cwd) {
const [yarnLockExists, pnpmLockExists, bunLockBinaryExists, bunLockExists, packageLockExists] = await Promise.all([
fileExists(path.resolve(cwd, 'yarn.lock')),
fileExists(path.resolve(cwd, 'pnpm-lock.yaml')),
fileExists(path.resolve(cwd, 'bun.lockb')),
fileExists(path.resolve(cwd, 'bun.lock')),
fileExists(path.resolve(cwd, 'package-lock.json')),
]);

let packageManager = 'npm';
let installScript = 'npm install';
if (yarnLockExists) {
installScript = 'yarn --frozen-lockfile';
packageManager = 'yarn';
} else if (pnpmLockExists) {
installScript = 'pnpm install --frozen-lockfile';
packageManager = 'pnpm';
} else if (bunLockBinaryExists || bunLockExists) {
installScript = 'bun install --frozen-lockfile';
packageManager = 'bun';
} else if (packageLockExists) {
installScript = 'npm ci';
}

return { packageManager, installScript };
}

/**
* Check if a given file exists and can be accessed.
* @param {string} filename
Expand All @@ -41,7 +9,7 @@ export async function fileExists(filename) {
try {
await fs.promises.access(filename, fs.constants.F_OK);
return true;
} catch (e) {}
} catch (e) { }
return false;
}

Expand Down
17 changes: 2 additions & 15 deletions tests/utils.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import path from 'path';
import { toBool, getDeltaText, iconForDifference, diffTable, getPackageManagerAndInstallScript, fileExists, stripHash } from '../src/utils.js';
import { toBool, getDeltaText, iconForDifference, diffTable, fileExists, stripHash } from '../src/utils.js';

test('toBool', () => {
expect(toBool('1')).toBe(true);
Expand Down Expand Up @@ -67,7 +66,7 @@ test('diffTable', () => {
expect(diffTable(files, { ...defaultOptions, collapseUnchanged: false })).toMatchSnapshot();
expect(diffTable(files, { ...defaultOptions, omitUnchanged: true })).toMatchSnapshot();
expect(diffTable(files, { ...defaultOptions, minimumChangeThreshold: 10 })).toMatchSnapshot();
expect(diffTable(files.map(file => ({...file, delta: 0})), { ...defaultOptions })).toMatchSnapshot();
expect(diffTable(files.map(file => ({ ...file, delta: 0 })), { ...defaultOptions })).toMatchSnapshot();

expect(diffTable([files[2]], { ...defaultOptions })).toMatchSnapshot();

Expand All @@ -76,18 +75,6 @@ test('diffTable', () => {
expect(diffTable(files, { ...defaultOptions, sortBy: 'Change:desc' })).toMatchSnapshot();
});

test('getPackageManagerAndInstallScript', async () => {
let cwd = process.cwd();
let { packageManager, installScript } = await getPackageManagerAndInstallScript(cwd);
expect(packageManager).toBe('npm');
expect(installScript).toBe('npm ci');

cwd = path.join(cwd, 'tests');
({ packageManager, installScript } = await getPackageManagerAndInstallScript(cwd));
expect(packageManager).toBe('npm');
expect(installScript).toBe('npm install');
});

test('fileExists', async () => {
expect(await fileExists('package.json')).toBe(true);
expect(await fileExists('file-that-does-not-exist')).toBe(false);
Expand Down