Automating Image Compression Using TinyPng & Git Hooks

Varun Sharma
Apr 29 · 5 min read
Image for post
Image for post
An artwork by roserodionova

What does TinyPng do?

As per the description given on TinyPNG website:

TinyPNG uses smart lossy compression techniques, which reduces the file size of your PNG files by decreasing the number of colors in the image. Thus requiring fewer bytes to store the data. The effect is nearly invisible but it makes a very large difference in file size!

How is it helpful to maintain optimal Apk size?

TinyPNG compression reduces image size significantly without much loss in quality. So it is a win-win for developers, designers, and product owners.

Image for post
Image for post

Ways to use TinyPNG for Android project:

  1. At TinyPNG website one can upload images with original size. Compress them and then download the optimized images. It’s a manual process that is tedious and time-consuming. Also, you have to do it for many pixel densities i.e. drawable-xxhdpi, drawable-xxxhdpi etc.
  2. Using Android Studio plug-in
  3. TinyPNG Gradle plug-in
  4. TinPNG cli

As you can see there are very different ways to integrate or use TinyPNG with your android project.

Our Approach

We have more than 180 modules for our Android project. So running TinyPNG even once over the entire project was very time-consuming.

Earlier we used to manually review images added to the Customer App apk after a fixed period of time. This regular check would tell us whether those images are already compressed or not. To save this manual effort we built an automation to compress only the newly added images in our code base.

We wanted the individual module owner to take responsibility for the image compression. Earlier it was the responsibility of the release manager. So we chose 4th approach from above i.e TinyPNG cli integration.

Now we wanted to run image compression only for the images that are newly added. For this, we used git pre-commit hook, which executes commands to determine newly added files and run compression on them.

Image for post
Image for post

What is git pre-commit hook?

It’s invoked by git commit command. This hook is called before obtaining the proposed commit message. Exiting with anything other than zero will abort the commit. It is used to check the commit itself rather than the message.

Why git hooks?

  1. They allow uniform actions to be performed over the code changes. These actions are coherent across all the code branches.
  2. Git Hooks save time otherwise used in manual configuration by each developer.

How to add changes to the git pre-commit hook?

Go to your-project-path/.git/hooks folder. Then change the pre-commit file. Its contents are generally a bash script. That is it, you have added a per-commit hook 😎🖖.

One problem here is we can not commit the .git folder to github as this folder is not versioned.

How to propagate pre-commit hook changes uniformly to all the developers?

  1. Create a git_hooks directory in the code base i.e. your-project-path/git_hooks
  2. Add a pre-commit file to the git_hooks directory.
  3. Add a gradle task which copies your-project-path/git_hooks/pre-commit to your-project-path/.git/hooks/pre-commit. Configure it to be triggered on clean project.
//Installing Git Hooks
task installGitHooks(type: Copy) {
("cp " + rootProject.rootDir+ "/git_hooks/pre-commit " + rootProject.rootDir+ "/.git/hooks/pre-commit").execute()
Runtime.getRuntime().exec("chmod -R +x .git/hooks/")
}
clean.dependsOn installGitHooks

Now you can make changes in your-project-path/git_hooks/pre-commit. Commit them to github. Other developers clone your repository. Executing clean project command updates their developer-project-path/.git/hooks/pre-commit file with latest git pre-commit hook changes.

Contents of project-path/git_hooks/pre-commit:

#!/usr/bin/env bash
# To use copy this file into your .git/hooks folder in your git repo
# make sure to add execute permissions using: chmod +x .git/hooks/pre-commit

command -v tinypng >/dev/null 2>&1 || {
echo "Installing tinypng-cli"
npm install -g tinypng-cli
echo "Please install manually if any error occurs, you can refer to below link"
echo "\t Site: \033[1m\033[4mhttps://www.npmjs.com/package/tinypng-cli\033[0m"
}

key="your-tinyPNG-api-key"

ExecuteImageCompression (){
output=$(tinypng $file -k $key)
}

IterateCommitedImages (){
for file in $(git diff --diff-filter=d --staged --name-only | grep ".png\|.jpg\|.jpeg")
do
echo "Crushing $file"
ExecuteImageCompression
git add $file;
done
}

Challenges in case of failed merge commit

  1. All works well until we encounter a failed git merge.
  2. In case of the failed git merge a new commit is created after conflict resolution.
  3. This new commit runs pre-commit hook with merged files. This creates greater than expected change with base branch.
Image for post
Image for post

How to overcome the challenge in case of failed git merge?

  1. Challenge to detect failed git merge can be solved by a bit of observation of the project-path/.git folder. In case of failed git merge a MERGE_MSG file is created which is removed once the merge is committed.
  2. We can check for existence the MERGE_MSG file in the project-path/.git folder. Based on presence or absence of the file we can decide whether to run the pre-commit hook or not.

To do this add -

if [ -e .git/MERGE_MSG ]; then
echo "Merge commit skipping image compression"
else
IterateCommitedImages
fi

to the bottom of the project-path/git_hooks/pre-commit file.

Now the setup is complete 👌🙂.

What’s next?

Git hooks can be used to perform various operations such as verifying and altering commit messages, preventing invalid pushes from taking place and running tasks after work tree is updated etc. .

One can adapt specific git hook according to his requirements to simplify or standardize development process pipelines. So identify the use case and hook it up!

Tokopedia Engineering

Story from people who build Tokopedia

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store