Documentation

CI Integration

Automate image optimization in your CI/CD pipeline with GetWebP CLI.

CI Integration

Automate image optimization in your CI/CD pipeline with GetWebP CLI. This guide provides copy-paste-ready workflows for GitHub Actions and GitLab CI, with best practices for license management, structured output, and error handling.

See also: Commands Reference | JSON Output | Exit Codes | Getting Started


Table of Contents#


Prerequisites#

  • A GetWebP Starter or Pro license key (purchase at getwebp.com/pricing). The Free plan works but is limited to 10 files per run with a 3-second delay per file.
  • Images in your repository or build artifacts to convert.
  • jq installed in your CI environment (pre-installed on GitHub Actions runners and most GitLab runners).

Installation in CI#

Download a pre-built binary from getwebp.com/download. This approach has no runtime dependencies and starts instantly.

# Linux x64 (most CI runners)
curl -fsSL -o getwebp https://getwebp.com/download/getwebp-linux-x64
chmod +x getwebp
sudo mv getwebp /usr/local/bin/

Option B: Install via npm#

Requires Node.js >= 18 in the runner image.

npm install -g getwebp

License Key in CI#

Store your license key as a CI secret -- never hardcode it in workflow files.

GitHub Actions#

  1. Go to Settings > Secrets and variables > Actions in your repository.
  2. Click New repository secret.
  3. Name: GETWEBP_LICENSE_KEY, Value: your license key (e.g. XXXX-XXXX-XXXX-XXXX).

GitLab CI#

  1. Go to Settings > CI/CD > Variables in your project.
  2. Click Add variable.
  3. Key: GETWEBP_LICENSE_KEY, Value: your license key.
  4. Enable Mask variable to hide it from job logs.

Activating the License#

Run getwebp auth at the start of each pipeline job. The activation binds to a device fingerprint -- CI runners generate a new fingerprint per job, so each run consumes and releases a device slot.

getwebp auth "$GETWEBP_LICENSE_KEY" --json

Tip: If activation fails with exit code 3, verify the key is correct and that you have not exceeded your device limit. Manage devices at getwebp.com/dashboard.


GitHub Actions#

Complete Workflow#

Save this as .github/workflows/optimize-images.yml:

name: Optimize Images
 
on:
  push:
    branches: [main]
    paths:
      - "src/images/**"
  pull_request:
    paths:
      - "src/images/**"
 
jobs:
  optimize:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
 
      - name: Install GetWebP CLI
        run: |
          curl -fsSL -o getwebp https://getwebp.com/download/getwebp-linux-x64
          chmod +x getwebp
          sudo mv getwebp /usr/local/bin/
          getwebp --version
 
      - name: Activate license
        if: ${{ secrets.GETWEBP_LICENSE_KEY != '' }}
        run: getwebp auth "$GETWEBP_LICENSE_KEY" --json
        env:
          GETWEBP_LICENSE_KEY: ${{ secrets.GETWEBP_LICENSE_KEY }}
 
      - name: Convert images to WebP
        id: convert
        run: |
          getwebp convert ./src/images \
            -o ./dist/images \
            -q 85 \
            -r \
            --skip-existing \
            --json > conversion.json
 
          # Print summary
          echo "### Image Optimization Results" >> "$GITHUB_STEP_SUMMARY"
          echo "" >> "$GITHUB_STEP_SUMMARY"
          total=$(jq -r '.total // 0' conversion.json)
          success=$(jq -r '.successCount // 0' conversion.json)
          failed=$(jq -r '.failedCount // 0' conversion.json)
          echo "- **Total:** $total" >> "$GITHUB_STEP_SUMMARY"
          echo "- **Succeeded:** $success" >> "$GITHUB_STEP_SUMMARY"
          echo "- **Failed:** $failed" >> "$GITHUB_STEP_SUMMARY"
 
      - name: Upload converted images
        uses: actions/upload-artifact@v4
        with:
          name: optimized-images
          path: dist/images/
 
      - name: Fail on conversion errors
        run: |
          success=$(jq -r '.success' conversion.json)
          if [ "$success" != "true" ]; then
            echo "::error::Some images failed to convert"
            jq -r '.results[] | select(.status == "error") | "::error file=\(.file)::\(.error)"' conversion.json
            exit 1
          fi

Minimal Workflow (Free Plan)#

No license key required. Limited to 10 files per run.

name: Optimize Images (Free)
 
on:
  push:
    branches: [main]
    paths:
      - "src/images/**"
 
jobs:
  optimize:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Install GetWebP CLI
        run: |
          curl -fsSL -o getwebp https://getwebp.com/download/getwebp-linux-x64
          chmod +x getwebp
          sudo mv getwebp /usr/local/bin/
 
      - name: Convert images
        run: getwebp convert ./src/images -o ./dist/images --skip-existing --json
 
      - uses: actions/upload-artifact@v4
        with:
          name: optimized-images
          path: dist/images/

Commit Converted Images Back to the Repository#

If you want the pipeline to commit WebP files back into the repository:

      - name: Convert and commit
        run: |
          getwebp convert ./src/images -r --skip-existing --json > /dev/null
 
          if git diff --quiet; then
            echo "No new images to commit."
          else
            git config user.name "github-actions[bot]"
            git config user.email "github-actions[bot]@users.noreply.github.com"
            git add "*.webp"
            git commit -m "chore: optimize images to WebP"
            git push
          fi

GitLab CI#

Complete Pipeline#

Save this as .gitlab-ci.yml (or add the job to your existing file):

optimize-images:
  stage: build
  image: ubuntu:22.04
  variables:
    GETWEBP_LICENSE_KEY: $GETWEBP_LICENSE_KEY
  before_script:
    - apt-get update -qq && apt-get install -y -qq curl jq > /dev/null
    - curl -fsSL -o /usr/local/bin/getwebp https://getwebp.com/download/getwebp-linux-x64
    - chmod +x /usr/local/bin/getwebp
    - getwebp --version
    - |
      if [ -n "$GETWEBP_LICENSE_KEY" ]; then
        getwebp auth "$GETWEBP_LICENSE_KEY" --json
      fi
  script:
    - |
      getwebp convert ./src/images \
        -o ./dist/images \
        -q 85 \
        -r \
        --skip-existing \
        --json > conversion.json
 
      code=$?
 
      total=$(jq -r '.total // 0' conversion.json)
      success_count=$(jq -r '.successCount // 0' conversion.json)
      failed_count=$(jq -r '.failedCount // 0' conversion.json)
      echo "Converted $success_count / $total images ($failed_count failed)"
 
      if [ "$code" -ne 0 ]; then
        jq -r '.results[] | select(.status == "error") | "\(.file): \(.error)"' conversion.json
        exit $code
      fi
  artifacts:
    paths:
      - dist/images/
    expire_in: 1 week
  rules:
    - changes:
        - "src/images/**"

Using npm Instead of Binary Download#

optimize-images:
  stage: build
  image: node:20-slim
  before_script:
    - npm install -g getwebp
    - |
      if [ -n "$GETWEBP_LICENSE_KEY" ]; then
        getwebp auth "$GETWEBP_LICENSE_KEY" --json
      fi
  script:
    - getwebp convert ./src/images -o ./dist/images -r --skip-existing --json
  artifacts:
    paths:
      - dist/images/
    expire_in: 1 week

Best Practices#

Use --skip-existing#

Always pass --skip-existing in CI. It skips files that already have a .webp counterpart at the output path, making incremental builds fast and avoiding redundant work.

getwebp convert ./images -o ./dist -r --skip-existing

Use --json for Machine-Readable Output#

The --json flag outputs structured JSON on stdout while keeping human-readable messages on stderr. This makes it safe to pipe into jq or save to a file for post-processing.

getwebp convert ./images --json > results.json

See JSON Output for the full schema.

Use --dry-run for Validation#

Preview which files would be converted without writing anything. Useful for PR checks that validate image formats without producing artifacts.

getwebp convert ./images -r --dry-run --json

Pin the CLI Version#

For reproducible builds, pin the CLI version by downloading a specific release rather than always fetching the latest:

GETWEBP_VERSION="1.0.1"
curl -fsSL -o getwebp "https://getwebp.com/download/getwebp-linux-x64?v=${GETWEBP_VERSION}"

Parallel Processing#

On Starter/Pro plans, GetWebP automatically uses all available CPU cores minus one. You can tune this with --concurrency:

# Use 4 workers (useful for memory-constrained CI runners)
getwebp convert ./images -r --concurrency 4 --json

Exit Codes in CI#

GetWebP CLI returns semantic exit codes that map directly to CI pipeline decisions:

Exit CodeMeaningCI Action
0All files converted successfullyContinue pipeline
1General error (bad arguments, activation failure)Fail the job
2Partial failure (some files succeeded, some failed)Warn or fail (your choice)
3License / auth errorFail the job; check secrets
4Network error (API unreachable)Retry the job

Handling Exit Codes in a Shell Script#

#!/usr/bin/env bash
set -uo pipefail
 
getwebp convert ./images -o ./dist --json > results.json
code=$?
 
case $code in
  0)
    echo "All images converted successfully."
    ;;
  2)
    echo "Warning: some files failed. Review results.json." >&2
    # Decide: exit 0 to continue or exit 1 to fail
    jq -r '.results[] | select(.status == "error") | "\(.file): \(.error)"' results.json >&2
    exit 1
    ;;
  3)
    echo "License error. Verify GETWEBP_LICENSE_KEY is set correctly." >&2
    exit 1
    ;;
  4)
    echo "Network error. The GetWebP API may be temporarily unavailable." >&2
    exit 1
    ;;
  *)
    echo "getwebp exited with code $code" >&2
    exit 1
    ;;
esac

Retry on Network Errors#

MAX_RETRIES=3
DELAY=10
 
for attempt in $(seq 1 $MAX_RETRIES); do
  getwebp convert ./images -o ./dist --skip-existing --json > results.json
  code=$?
 
  if [ $code -eq 0 ]; then
    echo "Success on attempt $attempt."
    break
  elif [ $code -eq 4 ] && [ $attempt -lt $MAX_RETRIES ]; then
    echo "Network error. Retrying in ${DELAY}s (attempt $attempt/$MAX_RETRIES)..." >&2
    sleep $DELAY
    DELAY=$((DELAY * 2))
  else
    echo "Failed with exit code $code on attempt $attempt." >&2
    exit $code
  fi
done

See Exit Codes for detailed descriptions of every exit code.


Parsing JSON Output#

Use jq to extract information from --json output for CI reporting and decisions.

Check Overall Success#

getwebp convert ./images --json | jq -e '.success' > /dev/null

The -e flag causes jq to exit with code 1 when the result is false, which pairs well with set -e in shell scripts.

Extract Failed Files#

jq -r '.results[] | select(.status == "error") | "\(.file): \(.error)"' results.json

Calculate Total Bytes Saved#

jq '[.results[] | select(.status == "success") | (.originalSize - .newSize)] | add' results.json

Export Metrics as CSV#

jq -r '.results[] | select(.status == "success") | [.file, .originalSize, .newSize, .saved] | @csv' results.json

Detect Free Plan Limit#

status=$(jq -r '.status' results.json)
if [ "$status" = "partial" ]; then
  echo "Free plan file limit reached. Upgrade at https://getwebp.com/pricing"
  exit 1
fi

See JSON Output for the complete schema and additional jq recipes.


Troubleshooting#

"Permission denied" when running the binary#

The downloaded binary needs execute permission:

chmod +x getwebp

License activation fails in CI#

  • Verify the GETWEBP_LICENSE_KEY secret is set and not empty.
  • Check that the key has available device slots at getwebp.com/dashboard.
  • Activation requires outbound HTTPS access. Ensure the runner can reach api.getwebp.com.

"Network error" (exit code 4)#

  • The GetWebP API may be temporarily unavailable. Add a retry loop (see Retry on Network Errors).
  • Check firewall rules on self-hosted runners: outbound HTTPS to api.getwebp.com must be allowed.

JSON output is empty or malformed#

  • Make sure you redirect stderr separately: getwebp convert ./images --json > results.json 2>errors.log.
  • Warnings and progress messages go to stderr and should not be mixed with the JSON stream.

Free plan is too slow for CI#

The Free plan adds a 3-second delay per file and limits each run to 10 files. For CI pipelines, a Starter or Pro license removes these restrictions and enables parallel processing. See getwebp.com/pricing.