Skip to content

feat(package-metrics): adds package-metrics action - #38

Draft
christopherferreira9 wants to merge 15 commits into
mainfrom
cferreira/add-package-metrics-action
Draft

feat(package-metrics): adds package-metrics action#38
christopherferreira9 wants to merge 15 commits into
mainfrom
cferreira/add-package-metrics-action

Conversation

@christopherferreira9

@christopherferreira9 christopherferreira9 commented Jul 8, 2026

Copy link
Copy Markdown

Purpose


This PR adds a new reusable action meant to be used on repositories that consist of developer oriented packages. All runs will upload its own JSON result so we should be able to at later stage scrape results and consume this information in Grafana. This will allow tracking of the metrics outlined below to the specific commit.

It measures:

  • packed size
  • unpacked size
  • minified size minified and gzipped)
  • gzipped size
  • install size

Workflow Inputs


  • paths: a list of paths where packages are located
  • is-monorepo: when true, paths is ignored and packages will be automatically discovered under packages/
  • enable-comment: when true, a comment is made to the PR with the output data

Notes


  • ⚠️ When calling this workflow, the checkout should be made for a specific ref otherwise the merge ephemeral commit will be used. A warning is added to the workflow if this doesn't happen. Even though this will cause an inconsistent result, the JSON output will contain the run link and the exact commit that triggered the job so tracing back is still possible.
  • The results are attached as Job Summary so even if enable-comment is false, a visual queue is still displayed.
  • supports pnpm and yarn with the use of corepack
  • the calling workflow must run its own install step before invoking this action. without it pnpm pack or yarn pack won't be able to resolve workspace:* references
  • fork prs are skipped entirely
  • This workflow should be used to get informal metrics and should be a non-blocking step for the repos that call it as drifts can be expected. In order for this to be a blocking workflow, base metrics + thresholds need to be set (at a later stage and after monitoring is in place).

Samples


JSON Output:

{
  "schemaVersion": 1,
  "commit": "a1b2c3d4e5f6...",
  "timestamp": "2026-07-06T14:32:01.000Z",
  "runId": 123456789,
  "runUrl": "https://github.com/some-org/some-repo/actions/runs/123456789",
  "packages": [
    {
      "name": "@offchainlabs/some-pkg",
      "version": "1.2.3",
      "path": "/absolute/path/to/packages/some-pkg",
      "metrics": {
        "packedSize": 12345,
        "unpackedSize": 34567,
        "bundleSize": 45678,
        "bundleSizeGzip": 15234,
        "installSize": 5123456
      },
      "errors": {}
    }
  ]
}

PR Comment:

📦 Package Metrics Report

Commit 160f6e9 · 2026-07-08T16:10:30.934Z

Package Packed Unpacked Bundle Bundle (gzip) Install
fixture-single-pkg 219 B 119 B 19 B 39 B 338 B

Full JSON report →

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

📦 Package Metrics Report

Commit d3e48fa · 2026-07-09T17:17:53.042Z

Package Packed Unpacked Bundle Bundle (gzip) Install
fixture-single-pkg 219 B 119 B 19 B 39 B 338 B

Full JSON report →

Comment thread .github/workflows/package-metrics.yml Fixed
Comment thread .github/workflows/package-metrics.yml Fixed
Comment thread .github/workflows/package-metrics.yml Fixed
Comment on lines +26 to +27
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || '' }}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

we should specify a ref when using this action in order to avoid getting metrics for an ephemeral merge commit

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

All files under package-metrics/__fixtures__ are only used for testing the action on this repo itself. They're used as dummy packages repositories.

export async function bundleSize(pkgPath) {
try {
const entry = resolveEntry(pkgPath);
const result = await esbuild.build({

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not entirely sure if this would follow exactly the same way we build on consumers but it should allow us to get a baseline that would correlate to real values.

@christopherferreira9 christopherferreira9 changed the title Cferreira/add package metrics action feat(package-metrics): adds package-metrics action Jul 9, 2026
const pkgJsonPath = path.join(packageDir, 'package.json');
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));

// peerDependencies aren't rewritten: a plain npm install doesn't

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think it depends on npm version. In npm 7 and up peer deps are installed by default.
https://docs.npmjs.com/cli/v10/configuring-npm/package-json#peerdependencies

Also --omit=dev still resolves dependencies (it just doesn't install them on the disc). Which means it would most likely fail during peer deps resolution part of workspace:* for monorepos. Worth looking into this flag instead:
https://docs.npmjs.com/cli/v10/using-npm/config#legacy-peer-deps

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ah this is a very good point! I think the current approach for considering peerDeps packages for the installSize metric is not consistent to be honest, let me rethink this, will leave the comment open.

@christopherferreira9 christopherferreira9 Jul 9, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This was broken in two ways: a peeDep resolving to a real registry package was already silently counted despite the comment claiming otherwise, and a workspace-sibling peerDep (to a real but unpublished semver) would 404 and fail the whole install. Fixed by adding peerDependencies to the same sibling-rewrite loop already used for dependencies/optionalDependencies — it now resolves locally instead of hitting the registry, and its size counts toward installSize, matching what a real npm≥7 install would actually do. Fix in: 8204568


// peerDependencies aren't rewritten: a plain npm install doesn't
// auto-install them, so it wouldn't affect the measured size.
for (const depsField of ['dependencies', 'optionalDependencies']) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: it may not be applicable today, but consider the following package structure.

my-monorepo/                              NOTE: none of a, b, c exist on npmjs.com
├─ pnpm-workspace.yaml
└─ packages/
   ├─ a/package.json    { "name": "a", "version": "1.0.0" }
                         (needs nothing)
   
   ├─ b/package.json    { "name": "b", "version": "1.0.0",
                         "dependencies": { "a": "workspace:*" } } 
   
   └─ c/package.json    { "name": "c", "version": "1.0.0",
                          "dependencies": { "b": "workspace:*" } } 
chain:    c ──needs──> b ──needs──> a

Just something worth flagging and keeping in mind. Because we only rewrite 1 level down, we'd get an error: "no package named 'a' exists". But imo we can accept it as an edge case and resolve it once we hit it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch! This implies changing the algorithm but I think its worth doing now since there are no repositories consuming this action just yet.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in d3e48fa. Also made the pnpm monorepo fixture use this exact structure to be tested.

Comment thread package-metrics/README.md
```

- `schemaVersion` — increments if this shape ever changes in a breaking way. Consumers
(e.g. a future artifact-scraping/Grafana ingestion job) should check this field.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Worth noting that there's a retention period on storing artefacts in github. Not sure what it's for Offchain/ZD but worth taking into account if we don't want to lose some data before we migrate it to Grafana.

Nothing actionable, just something to keep in mind.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is a good point. I'd say that for now we need to take it with a grain of salt and work towards storing this data on our end. We can also hold from using this action on the consumer side until the entire stack is in place.

try {
entries = readdirSync(packagesRoot, { withFileTypes: true });
} catch (err) {
console.warn(`package-metrics: could not read "${packagesRoot}": ${err.message}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this way we'd actually get to see the warning in the console, instead just output logs.

Suggested change
console.warn(`package-metrics: could not read "${packagesRoot}": ${err.message}`);
core.warning(`package-metrics: could not read "${packagesRoot}": ${err.message}`);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agree, this will increase visibility. Fixed here: 40ccbfa


let packages;
try {
packages = discoverPackages({ workspaceRoot, isMonorepo, paths });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shouldn't we throw if packages is empty and let the action fail?

@christopherferreira9 christopherferreira9 Jul 9, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agree, fixed! f1b0aa4

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.

3 participants