Automating Sketch with GitHub Actions
Tags: sketch, github, githubactions, automation
Published: 2022-06-05 15:57:18

Sometimes I use Sketch to create graphics for my content; however, I have always found it challenging to keep track of my work. Saving files here and there, versioning them with what I thought were sensible name schemes, only to realise that following such schemes requires a lot of mental effort.

My dream was always to be able to store my Sketch files in a *Git* repo; for some reason, I always thought this would be impossible, that Sketch's files were binaries impossible to properly version.

In the following post, I'll explain why I was wrong and how is it that you can version your *Sketch* files as plain text documents.

So I start with a base document, nothing too complex as I don't want to overcomplicate things:

![Simple image](https://ik.imagekit.io/thatcsharpguy/posts/sketch-in-git/Screenshot_2022-06-04_at_19.59.35.png?ik-sdk-version=javascript-1.4.3&updatedAt=1654369932542)

I have named this document *tcsg.sketch*; then, the next thing to understand is how Sketch actually saves these documents as a single file. The most important thing is that a *.sketch* file [is nothing more than a *.zip*](https://developer.sketch.com/file-format/?_ga=2.160187325.1466637750.1654335985-1710079208.1653454852) file with a bunch of *.json* files inside.

## De-_sketchify_

We know that *Git* does not play nice with binary files but plays very nicely with plain text files – and *JSON* is just that. Why not decompress the *.sketch* file and keep track of the *.json* files alone; after all, when we want to open our file in Sketch again, we can compress those files again.

### Decompress

With this in mind, we can use:

```shell
unzip -d tcsg tcsg.sketch
```

To unzip the files into the `tcsg` repository. A quick glance into the newly unzipped repository gives us the following repo structure:

```text
tcsg
├── document.json
├── meta.json
├── pages
│   └── 4FB4BFA1-4E01-4EE8-9962-F07A85622B2F.json
├── previews
│   └── preview.png
└── user.json
```

I will not discuss the details of the files, as they are well explained in Sketch's documentation.

We should note that for optimisation purposes, the *JSON* files are saved with no indentation, and all the contents are stored in a single line. As I want to embrace the full power of Git, I need to format these files to be able to view the diffs.

### Indent files

There is a useful tool to work with *JSON* fines from the *CLI*, it is named *jq*, I will use it to format the files with indentation:

```shell
find ./tcsg -type f -name "*.json" \
    -exec sh -c "jq . {} | sponge {}"  \;
```

An explanation of the above command:

 - `find ./tcsg`: Searches for objects in the `./tcsg` folder
 - `-type f`: Specifies, with `f` that we are looking for a regular file
 - `-name "*.json"`: Filters the files we will find to all those ending in `.json`
 - `-exec [command]`: Executes a command for each file; within this command we can use `{}` to refer to the file name. The command to execute should be followed by `\;`
 - `sh -c "jq . {} | sponge {}"`: In this case, the command that will be executed for each file is `jq . [filename] | sponge [filename]`.

### Delete previews (optional)

There is a `previews` folder where the last page edited by the user is preserved to be used as a thumbnail (and a preview) for the document. Again, this is an image, and for the time being, I will delete it since it is not needed for the file format.

```shell
rm -rf ./tcsg/previews/preview.png
```

And that is it! we now have a Sketch document as a series of plain text files.

## *Sketchify*

Of course, I want this process to be reversible – I want to be able to open my documents in Sketch again.

### Create a temporary folder

I rather not modify the original directory, so I will create a copy of the working directory:

```shell
cp -r ./tcsg ./tcsg_temp
```

### *Un*-indent files

When I decompressed the files, I realised that the *JSON* files contained all the information in a single line; to respect that format, let's apply the `jq -c . {} | sponge {}` command to all those files. It is pretty similar to the format command above, with the difference of the `-c` flat of *jq*, which "compresses" the output.

```shell
find ./tcsg_temp -type f -name "*.json" \
    -exec sh -c "jq -c . {} | sponge {}"  \;
```

### Remove previews (optional)

Again, let's delete any preview image, for consistency with the process above:

```shell
rm -rf ./tcsg_temp/previews/preview.png
```

### Putting everything together

I placed all the above code into a single file called `desketchify.sh`:

```shell
#!/usr/bin/env bash

unzip -o -d tcsg tcsg.sketch

find ./tcsg -type f -name "*.json" \
    -exec  sh -c "jq . {} | sponge {}" \;

rm -rf ./tcsg/previews/preview.png
```

### Compress

Finally, in the compression step, we need to change directory to the temporary folder I have been working on. Then apply the compression step using the `zip` utility:

```shell
cd ./tcsg_temp; zip -r -X ../tcsg.sketch *
```

The flag `-r` specifies that `zip` should recursively compress the files; the `-X` flag specifies that the compression should not save any extra file attributes.

At the end of this command I should have a `.sketch` file that can be opened in the app.

### Cleanup

Lastly, let's clean up what I just did:

```shell
cd ..; rm -rf ./tcsg_temp
```

### Putting everything together

I placed all the above code into a single file called `sketchify.sh`:

```shell
#!/usr/bin/env bash

cp -r ./tcsg ./tcsg_temp

find ./tcsg_temp -type f -name "*.json" \
    -exec  sh -c "jq -c . {} | sponge {}" \;

rm -rf ./tcsg_temp/previews/preview.png

cd ./tcsg_temp; zip -r -X ../tcsg.sketch *

cd ..; rm -rf ./tcsg_temp
```

## Exporting artboards

But why stop there? what if I want to export the contents of the file as images? This will make it easy to share the assets with people who do not have Sketch installed at all!

This is surprisingly easy using GitHub Actions; all I need to do is use a kind of hidden gem in the Sketch ecosystem: their *sketchtool* utility, [read more about it here](https://developer.sketch.com/cli/). It allows you to interact with Sketch documents without human interaction.

In particular, the command that I am interested in the most is the one that exports artboards: `sketchtool export artboards [file]`.

The tool itself is free to use for my purposes, but we need to download Sketch, I wrote the following code to achieve that:

```shell
wget -O sketch.zip \
    https://download.sketch.com/sketch-88.1-145978.zip
unzip -qq sketch.zip
Sketch.app/Contents/MacOS/sketchtool -v
```

Leaving the *sketchtool* accesible via the `Sketch.app/Contents/MacOS/sketchtool` command. Obviously, be mindful of the version you are working with.

## Full automation

It is finally time to put everything together using GitHub actions, I want to run all these steps only when the source files of the Sketch document change: 

```yaml
name: Sketchify

on:
  workflow_dispatch:
  push:
    branches: [ "main" ]
    paths:
      - 'tcsg/**'
```

The jobs are organised in steps, where each step performs one and only one action:

```yaml
jobs:
  generate_assets:
    name: Generate assets
    runs-on: macos-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Install dependencies
        run: |
          brew install jq
          brew install moreutils
      - name: Create Sketchfile
        run: ./sketchify.sh
      - name: Install Sketch
        run: |
          wget -O sketch.zip https://download.sketch.com/sketch-88.1-145978.zip
          unzip -qq sketch.zip
          Sketch.app/Contents/MacOS/sketchtool -v
      - name: Export artboards
        run: Sketch.app/Contents/MacOS/sketchtool export artboards tcsg.sketch --output=export --formats=jpg --scales=1,2
      - name: Save exported images
        uses: actions/upload-artifact@v3
        with:
          name: images
          path: export/
      - name: Save generated Sketch file
        uses: actions/upload-artifact@v3
        with:
          name: sketch-file
          path: tcsg.sketch
```


This will rebuild my Sketch file every time new changes are made to the repository and will export all the artboards in it. The best part? the artefacts will be available for download in the GitHub UI:

![Artifact available to download](https://ik.imagekit.io/thatcsharpguy/posts/sketch-in-git/Screenshot_2022-06-05_at_14.17.46.png?ik-sdk-version=javascript-1.4.3&updatedAt=1654435369288)

## Conclusion

I consider this to be a pretty decent way to store Sketch files as assets in a Git repository; of course, depending on the changes you make, the diffs may still be monstrous; but at least they are more trackable than as a single *zip* file.

To use the code described in this post you will need to make some adjustments to it to refer to your own files.

So, tell me, do you use Sketch? I hope this post was useful for you as it was helpful for me, I discovered so many things about Sketch. If you have any doubts, let me know on [Twitter at @feregri_no](https://twitter.com/feregri_no). As always, find the code for this post [in GitHub](https://github.com/fferegrino/tcsg-logos/tree/automating-sketch-with-github-actions). Happy *Sketch*-ing.