Developer guidesAutomate with WP-CLI

Automate with WP-CLI

Validate icons in CI with real exit codes, move packs between sites and check deploys from the shell with the wp easy-svg commands of Easy SVG Pro.

Easy SVG Pro ships seven WP-CLI commands. Together they let you check icons before they go live, copy icon sets from one site to many and confirm after a deploy that every collection is there. This tutorial shows how to set a site up for that, which exit codes you can rely on, and three pipelines you can copy.

The commands at a glance

CommandWhat it doesWrites something?
wp easy-svg icons listEvery collection with count, origin and problem countNo
wp easy-svg icons problemsEvery file a source folder could not use, and whyNo
wp easy-svg icons validateDry run of a folder against this site's sanitizerNo
wp easy-svg icons importCopies a folder into a drop-in under wp-content/easy-svg-iconsFiles
wp easy-svg pack listEvery exportable collection, also as JSONNo
wp easy-svg pack exportOne collection as a pack ZIPA ZIP file
wp easy-svg pack importA pack ZIP as a new icon setDatabase

Prepare the site

Every command checks two things before it does anything: the licence is active, and the WP-CLI feature is switched on under Icons → Features. If either is missing you get Turn on WP-CLI under Icons → Features (a license is required). and exit code 1. No WordPress user is needed, the commands run as nobody.

Activate both plugins

Easy SVG Support has the sanitizer. Without it validate and both imports refuse to run.

wp plugin activate easy-svg easy-svg-pro

Bind the licence once

Put the key into wp-config.php, ideally from an environment variable:

define( 'EASY_SVG_PRO_LICENSE_KEY', getenv( 'EASY_SVG_PRO_LICENSE_KEY' ) );

The site activates itself from this constant on admin_init, the first time an administrator (a network administrator on multisite) opens wp-admin. WP-CLI does not fire admin_init, so a site that only ever sees WP-CLI never binds. Open wp-admin once after setting the constant, or enter the key under Icons → License instead. After that the site keeps a site token and the key is not needed again.

Switch on the WP-CLI feature

Turn it on under Icons → Features, or from the shell. Merge into the stored flags instead of overwriting them, otherwise every other feature switches off:

wp eval '
$flags        = (array) get_option( "easy_svg_pro_features", array() );
$flags["cli"] = true;
update_option( "easy_svg_pro_features", $flags );
'

Switch on what your collections need in the same way: sources for theme and drop-in folders, editor for icon sets.

Check

wp easy-svg icons list

A table means the site is ready.

Use a long lived site for automation, such as your staging server. Every activation takes a seat on your licence, and a throwaway CI container that is rebuilt on every run would need a fresh activation each time, which it cannot get without a visit to wp-admin. Run the commands against staging over SSH instead, for example with a @staging alias in wp-cli.yml.
Never put the real licence key into a repository, a CI log or a Docker image. Read it from a secret into an environment variable and keep wp-config.php out of version control.

Exit codes you can rely on

CommandExit 0Exit 1
anyLicence inactive or WP-CLI feature off
icons validateRan, any resultWith --strict: at least one file skipped. Also: sanitizer missing or too old
icons problemsClean and with problems
icons importCopied, even with skipped files or zero filesBad --collection, folder not creatable, sanitizer missing
pack exportZIP writtenUnknown slug, no ZipArchive, write failed
pack importSet stored, even with refused icons or a slug collisionUnreadable file, nothing acceptable, no free slug

Three gaps to know about:

  • validate --strict passes on a folder that does not exist or holds no .svg files. Count the files yourself first.
  • icons problems never fails. Look for the Success: line.
  • pack import moves aside to slug-2 on a collision and still exits 0. Look for was taken in the output.

Pipeline 1: validate icons on every pull request

The check has to run against a real WordPress with the site's sanitizer settings, so the job copies the icons to staging and validates them there.

name: Icons
on:
  pull_request:
    paths:
      - "theme/assets/icons/**"

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install SSH key
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.STAGING_SSH_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan staging.example.com >> ~/.ssh/known_hosts
      - name: Copy icons to staging
        run: |
          rsync -a --delete theme/assets/icons/ deploy@staging.example.com:/tmp/icons-pr-${{ github.event.number }}/
      - name: Validate
        run: |
          ssh deploy@staging.example.com bash -s <<'SH'
          set -euo pipefail
          DIR=/tmp/icons-pr-${{ github.event.number }}
          n=$(find "$DIR" -maxdepth 1 -name '*.svg' | wc -l)
          if [ "$n" -eq 0 ]; then echo "No SVG files to validate"; exit 1; fi
          wp --path=/var/www/staging easy-svg icons validate "$DIR" --strict
          SH

The job fails with the table in the log as soon as one icon would be skipped, and it names the reason: not-svg, no-drawing, duplicate, bad-name or unreadable.

Pipeline 2: one icon set, many client sites

Build the set once on staging, export it, import it everywhere.

#!/usr/bin/env bash
set -euo pipefail

SLUG="agency-ui"
PACK="/tmp/$SLUG.zip"

wp @staging easy-svg pack export "$SLUG" --file="$PACK"
scp staging:"$PACK" "$PACK"

for site in client-a client-b client-c; do
	scp "$PACK" "$site":/tmp/
	out="$(wp @"$site" easy-svg pack import "/tmp/$SLUG.zip" --as="$SLUG" 2>&1)" || {
		echo "[$site] import failed: $out" >&2
		continue
	}
	if grep -q 'was taken' <<<"$out"; then
		echo "[$site] $SLUG exists already, a copy was stored under another slug. Remove it." >&2
	fi
	echo "[$site] $out"
done

Things to know:

  • pack import always creates a new set. Running it twice gives agency-ui and agency-ui-2. To update a set, change it in the set editor or remove the old set first.
  • The import fires easy_svg_pro_set_icons_written, so icon history keeps a revision and your own listeners run. See React to set changes.
  • Keywords from the manifest do not survive the trip into an icon set.

Pipeline 3: check a deploy

After a theme or drop-in deploy, confirm the collections are there and complete:

#!/usr/bin/env bash
set -euo pipefail

expected=("theme" "acme")
list="$(wp easy-svg pack list --format=json)"

for slug in "${expected[@]}"; do
	jq -e --arg s "$slug" 'any(.[]; .slug == $s and .icons > 0)' <<<"$list" >/dev/null \
		|| { echo "Collection $slug missing or empty" >&2; exit 1; }
done

problems="$(wp easy-svg icons problems)"
grep -q '^Success:' <<<"$problems" || { echo "$problems"; exit 1; }

Pitfalls

  • Features hide collections. Icon sets only exist as collections while Icon set editor is on, folders only while Theme & folder sources is on. A missing collection after a deploy is often a switched off feature.
  • icons import creates the target folder first. A typo in the source path leaves an empty folder and exits 0.
  • Paths are on the server. With an alias like @staging, validate, import and export read and write paths on the staging server, not on your machine.
  • Deactivating does not stick while the constant is set. Release the seat under Icons → License and the next admin visit binds it again from EASY_SVG_PRO_LICENSE_KEY. Remove the constant first.