Glimpse

Project Files

init, check, .glimpseignore, and the .glimpse-baseline.json baseline.

On this page 11

Three files configure glimpse for your project. .glimpseignore excludes paths, .glimpse-baseline.json records images already handled, and the optional .github/workflows/glimpse.yml runs checks or automatic optimization in GitHub Actions.

Init

Run it from the project root:

bash
cpx glimpse init

Init creates the ignore file and asks whether to accept your current images unchanged. This baseline seed defaults to No and makes no API calls unless selected. Seeding means checks and automatic optimization skip those images until their contents change. Review .glimpseignore before seeding.

When run interactively in a Git repository without an existing workflow, init then asks:

text
Which GitHub Actions workflow would you like?
  [0] Check and optimize
  [1] Check only
  [2] No workflow

Check and optimize is selected by default. It checks images after a PR merges into any branch and opens an optimization PR against that branch. GLIMPSE_TOKEN is optional. A personal token provides higher limits. See automatic optimization for setup.

Check only runs the existing check on PRs and pushes to main, and fails on flagged images. Its token is optional.

Use flags to select a workflow without the workflow prompt:

bash
cpx glimpse init --workflow                         # check and optimize
cpx glimpse init --workflow-mode=optimize            # check and optimize
cpx glimpse init --workflow-mode=check               # check only
cpx glimpse init --workflow-mode=check --no-interaction

An explicit mode overrides --workflow's default. Only check and optimize are accepted. Plain non-interactive runs and closed stdin never select a workflow implicitly. --update-baseline explicitly seeds the baseline; without it, non-interactive init creates an empty baseline.

Existing files are kept. --force recreates .glimpseignore; to also replace the workflow, select a workflow with --workflow or --workflow-mode. For example:

bash
cpx glimpse init --workflow-mode=check --force

--force alone never replaces an existing workflow or baseline. Use --update-baseline to refresh the baseline. A kept custom workflow gets neutral setup guidance because init does not assume what it does.

The default for --workflow has changed from check only to check and optimize. Existing workflow files are not migrated. Scripts that need the old behavior should use --workflow-mode=check.

Check

check is the CI gate. It scans a directory (or a single file), estimates the best-format saving for every image, and lists only the ones that would benefit from optimization. Without --fix, it sends image measurements without uploading image bytes.

bash
cpx glimpse check .                        # fail if any image could save 10% or more
cpx glimpse check assets/ --threshold=25   # only fail on big wins
cpx glimpse check . --json                 # machine-readable report
code
+---------------------+---------+-----------+--------+---------+
| File                | Current | Estimated | Format | Saved % |
+---------------------+---------+-----------+--------+---------+
| art/social-card.png | 1.2 MB  | ~180 KB   | AVIF   | 85.0%   |
| art/icon.png        | 96 KB   | ~34 KB    | WEBP   | 64.6%   |
+---------------------+---------+-----------+--------+---------+
2 of 14 images need optimization (threshold: 10%).

An image needs optimization when the format that saves the most (the same pick analyze makes) would save at least --threshold percent (default 10, decimals allowed). The exit code is 1 when any image is over the threshold, so a CI pipeline fails until the images are optimized.

Files that cannot be checked (unreadable or unrecognized) are listed separately and also fail the run: a passing check means every image was checked and passed, not just that none were flagged. A directory with no images passes.

Fix flagged images

Use --fix to optimize the flagged images in place:

bash
cpx glimpse check . --fix
cpx glimpse check assets/ --fix --quality=85  # optional re-encode quality

--quality is optional and requires --fix. When omitted, optimization uses the existing optimize default. An explicit quality changes only optimization, not the estimates used to select images.

Checking finishes before any file is changed. If a file cannot be checked, the command fails without optimizing anything. Otherwise, it uploads only the flagged images for optimization and preserves their formats and filenames. It respects the same threshold, ignore rules, and baseline as ordinary check.

The command returns 0 when optimization succeeds or nothing needs fixing. It returns 1 on failure. If optimization fails partway through, it stops and keeps the earlier successful changes. Successful writes update an existing baseline; the command does not create one. Run cpx glimpse init first if you want subsequent directory scans to skip optimized images.

With --fix --json, the report adds a fixed list of successfully optimized filenames. files and needs_optimization describe the original check results. failed contains checking or optimization errors. Without --fix, the existing check output and exit codes stay the same.

Skip an image

Restore one image from Git and accept it unchanged in the baseline:

bash
cpx glimpse skip assets/photo.png

Run this from the repository root. By default it restores the version in HEAD, so you can undo an optimization you have not committed yet. If you already committed the optimization, choose a commit containing the original:

bash
cpx glimpse skip assets/photo.png --from=abc1234

Only that image is restored. The command creates or updates .glimpse-baseline.json with via: "skip", without calling the API. Commit the image and baseline together. The Git index is left unchanged. check and directory analyze scans skip the image while its bytes match. Editing the image makes it eligible again.

There are no backups. If the original is not in the selected Git commit, the command fails without changing the image or baseline. Symlinks and paths outside the repository are refused.

On automatic optimization PRs, you can also add an inline glimpse skip comment on the image.

.glimpseignore

A .glimpseignore file at the project root excludes paths from scans, using gitignore syntax:

gitignore
# generated assets, not worth optimizing
vendor/
public/build/
*.gif
!logo.gif

The file is read from the directory you run glimpse from, and patterns are relative to it: with the rules above, cpx glimpse check . and cpx glimpse check public/ both skip public/build/. Negated patterns (!logo.gif) re-include files, with the same caveat as git: a file inside an ignored directory cannot be re-included.

check <dir> and analyze <dir> honor the file, and the transform commands honor it too by never recording an ignored file into the baseline.

.glimpse-baseline.json

Adopting glimpse in an existing project should not require converting every image on day one. The baseline records files you have already dealt with, so check <dir> and analyze <dir> skip them and only new or changed images get flagged. It is the same idea as phpstan's baseline: accept the current state, gate everything from here on.

Generate it by scanning the directory:

bash
cpx glimpse analyze assets/ --update-baseline

Or let cpx glimpse init set it up together with the other files.

The file lists each image with its size, content hash, and the operation that put it there, under a _readme header that explains the format to anyone reading it in review:

json
{
    "_readme": [
        "Images already handled by glimpse: check and analyze skip a file while its size and xxh128 content hash still match its entry.",
        "..."
    ],
    "files": {
        "photos/hero.png": {
            "size": 482133,
            "xxh128": "5f3d0a9c1b2e4d6f8a7c9e0b1d2f3a4c",
            "via": "analyze"
        },
        "photos/hero.webp": {
            "size": 98342,
            "xxh128": "9a1c3e5f7b2d4a6c8e0f1a3b5c7d9e0f",
            "via": "optimize"
        }
    }
}

How entries match

An image is skipped only while its content still matches the recorded entry. Replace a baselined file with a new version and it re-enters the scan automatically. Where .glimpseignore says "never look at these paths", the baseline says "these exact file contents are already handled".

The via field

via separates files you accepted as-is from files glimpse actually processed. analyze means the file was taken as-is by --update-baseline. A command name (convert, resize, thumbnail) means glimpse produced or processed the file. optimize means the optimizer chain ran, through the optimize command or a transform's --optimize flag. When you work through a bulk-accepted baseline later, the analyze entries are the ones still waiting for real treatment.

Where the baseline applies

Every command reads the baseline (and .glimpseignore) from the current working directory, the way composer and phpstan resolve their config. Run glimpse from the project root and the baseline there governs the run, with entries keyed relative to it (scanning assets/ matches keys like assets/hero.png). There is no upward search: run glimpse from anywhere else and the baseline simply does not apply. --update-baseline writes to the current directory and requires the scanned directory to be inside it.

The baseline only applies to directory scans. Naming a file explicitly (cpx glimpse analyze photo.png) always analyzes it. Commit the file so CI and your teammates share the same starting point.

The transforms keep it current

convert and optimize record the written output and its source (an in-place conversion that replaced the source also cleans up its old entry), while resize and thumbnail record only the file they produce. Writing to stdout or to a path outside the working directory records nothing. A baseline problem never fails a transform that already succeeded; it degrades to a warning on STDERR. The transforms never create the baseline file; that is --update-baseline's job. Re-running analyze <dir> --update-baseline refreshes changed entries and prunes deleted files.

Concurrent writes are safe

A run that will write takes an exclusive lock on the file first, and the content lands in a temporary file swapped atomically into place. A second concurrent writer fails fast with a clear error (analyze --update-baseline) or degrades to a STDERR warning (the transforms) instead of silently overwriting entries. Readers never see a half-written file, and a failed write leaves the previous baseline untouched.