Newsletter
TechAnV Blog
Get updates on security engineering, Rust, eBPF, and DevSecOps. No spam, unsubscribe anytime.
Check your inbox and click the confirmation link to complete your subscription.
Optimizing PNGs in GitHub Actions using Oxipng#
My datasette-screenshots repository generates screenshots of Datasette using my shot-scraper tool, for people who need them for articles or similar.
Jacob Weisz suggested optimizing these images as they were quite big. I want them to be as high quality as possible (I even take them using --retina mode), but that didn’t mean I couldn’t use lossless compression on them.
I often use squoosh.app to run a version of Oxipng compiled to WebAssembly in my browser. I decided to figure out how to run that same program in GitHub Actions.
Installing Rust apps using Cargo#
Surprisingly there isn’t yet a packaged version of Oxipng for Ubuntu - so I needed another way of installing it.
The project README suggests installing it using cargo install oxipng.
I used the tmate trick to try that out in a GitHub Actions worker - the cargo command is available by default but it took over a minute to fetch and compile all of the dependencies.
I didn’t want to do this on every run, so I looked into ways to cache the built program. Thankfully the actions/cache action documents how to use it with Rust.
The full recipe for installing Oxipng in GitHub Actions looks like this:
1 - name: Cache Oxipng2 uses: actions/cache@v33 with:4 path: ~/.cargo/5 key: ${{ runner.os }}-cargo6 - name: Install Oxipng7 run: |8 which oxipng || cargo install oxipngThe first time the action runs it does a full compile of Oxipng - but on subsequent runs the which oxipng command succeeds and skips the cargo install step entirely.
Running Oxipng in an Action#
All of the PNGs that I wanted to optimize were in the root of my checkout, so I added this step:
1 - name: Optimize PNGs2 run: |-3 oxipng -o 4 -i 0 --strip safe *.pngThe -o 4 is the highest recommended level of optimization.
-i 0 causes it to remove interlacing - “Interlacing can add 25-50% to the size of an optimized image” according to the README.
--strip safe strips out any image metadata that is guaranteed not to affect how the image is rendered.
Oxipng updates the specified images in place, hence the *.png at the end.
Testing this in a branch first#
I tested this all in a branch first so that I could see if it was working correctly.
Since my workflow usually pushes any changed files back to the same GitHub repository, I added a check to that step which caused it to only run on pushes to the main branch:
1 - name: Commit and push2 if: github.ref == 'refs/heads/main'3 run: |-4 git config user.name "Automated"5 ...But I wanted to preview the generated images - so I added this step in the branch to save them to an artifact zip file that I could then inspect:
1 - name: Artifacts2 uses: actions/upload-artifact@v33 with:4 name: screenshots5 path: "*.png"Once I got it working, I squash-merged this pull request back into main.
The result#
Oxipng worked really well!
It reduced the size of all three of my screenshots:
- faceting.png from 305KB to 211KB
- global-power-plants.png from 1.14MB to 770KB
- sql-query.png from 235KB to 169KB
This commit shows the difference and lets you compare both images.