assetopt

Guide

Optimize images in GitHub Actions (CI)

Add image and asset optimization to your CI pipeline: fail a pull request when assets aren't optimized, cache results between runs, and keep your build lean — from the command line, MIT.

Unoptimized images are the single biggest source of accidental page weight, and they slip into a repo one commit at a time. The fix is to make CI check for you: run assetopt on every pull request and fail the build when assets aren’t optimized. This guide wires that into GitHub Actions — no upload, no third-party service, MIT.

Two patterns, pick one

  • Gate (recommended) — CI checks that assets are already optimized and fails the PR if not. Your committed files stay the source of truth. Uses --min-savings and/or assetopt audit.
  • Optimize-in-build — CI optimizes assets as part of producing the deployable output (dist/), and the optimized files are what get deployed, not committed.

Install

Every pattern starts by installing the CLI in the job. No custom action needed — it’s a plain npm package:

- uses: actions/setup-node@v4
  with:
    node-version: 22
- run: npm install -g @assetopt/cli

Pattern 1 — Fail the PR when assets aren’t optimized

The --min-savings flag turns assetopt into a quality gate: if the total savings it could achieve exceed your threshold, that means the committed assets are heavier than they should be, and the command exits with code 1 — failing the job.

Run it on analyze (a dry-run — it writes nothing, just measures):

name: assets
on: [pull_request]

jobs:
  check-assets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm install -g @assetopt/cli
      # Fails if assetopt could still shave >10% off the committed assets
      - run: assetopt analyze ./public --min-savings 10

Because analyze never writes files, this is a pure check — safe to run on every PR. The threshold is global (it gates total savings across the run, not per file), so pick a number that reflects “these assets are already in good shape” for your project.

Or flag oversized files with audit

assetopt audit scans for files above a size threshold and exits 1 if any are flagged — useful when you care about individual offenders rather than aggregate savings:

- run: assetopt audit ./public --savings --threshold 20

Default size thresholds are 500 KB (images), 100 KB (JS), 50 KB (CSS), 50 KB (SVG). --savings runs the pipeline as a dry-run to compute potential savings per file; --threshold 20 flags anything that could shrink by more than 20%.

Pattern 2 — Optimize as part of the build

If your deploy uploads a build directory, optimize it in place before the deploy step:

- run: npm run build
- run: assetopt optimize ./dist
- run: ./deploy.sh   # ships the now-optimized ./dist

Only convert formats in this pattern if your HTML references the optimized filenames. On a site whose built HTML hard-codes .jpg/.png, keep formats intact with { "images": { "outputFormat": "keep" } } and let assetopt recompress in place. See optimize images before deploying for the full reasoning.

Cache results between runs

assetopt keeps a manifest (.assetopt-cache.json) so unchanged files are skipped on re-runs. Persist it across CI runs with actions/cache and every build after the first only reprocesses what changed:

- uses: actions/cache@v4
  with:
    path: optimized/.assetopt-cache.json
    key: assetopt-${{ hashFiles('public/**') }}
    restore-keys: assetopt-

Point path at wherever your output.dir puts the manifest (default ./optimized/).

Make the check required

A failing job only blocks a merge if the check is required. In your repo: Settings → Branches → branch protection rule → Require status checks to pass → select the check-assets job. Now a PR that introduces a fat, unoptimized image can’t merge until it’s fixed.

Why not use optimize with --min-savings directly?

You can — --min-savings works on both optimize and analyze. Use analyze for a pure PR check (nothing written, nothing to clean up), and optimize when you actually want the optimized files produced in that job (Pattern 2). See the feature catalog for the full flag reference.

Next steps

npm install -g @assetopt/cli
assetopt analyze ./public --min-savings 10   # try the gate locally first

Run it locally before you commit the workflow — the exit code is the same on your machine as in CI, so you can tune the threshold without pushing.

Get the CLI

$ npm install -g @assetopt/cli

Open source, MIT. See the docs orstar it on GitHub.