Properly Creating ZIP Archives on MacOS
By: Ryan Knightly
Tags: Archive, Python, Programming, Mac, Software Development

How The Problem Came Up

I recently wrote a Python program for an organization I am involved with to track attendance at meetings. When I was done with the project, I needed to package it to send it to them, so I created a ZIP archive the only way I knew how:

Right-click on the folder, then click on compress.

I sent off the file and all was well. Except that when I viewed the project on the machine they were running it on (which uses Windows), I noticed that the project was a little off.

The file structure looked like the following (renamed to my-project to keep things simple).

my-project/

│

├── __MACOSX

│ └── <irrelevant files>

│

└── my-project/

│

├── .git/

│ └── <irrelevant files>

│

├── .gitignore

├── my-script.py

├── my-script.pyc

├── LICENSE

└── README.md

As opposed to what I would really like, which would have been:

my-project/

│

├── my-script.py

├── LICENSE

└── README.md

Here are the problems

There is a superfluous directory named __MACOSX/ . The actual project is an extra directory deep. The project files are at the path of my-project/my-project , rather than simply my-project/ . The .git/ directory and .gitignore file are still included, even though they are not helpful to the end user. The .pyc file is still in the project.

I initially wondered why the __MACOSX/ directory issue wasn’t clear to me before I sent off the ZIP archive. It turns out that the first two problems, including the __MACOSX/ directory and the actual project being an extra directory deep, don’t even occur if you if you are only working with a Mac. If you try uncompressing the .zip archive by simply left-clicking on it, the project will look like the following:

my-project/

│

├── .git/

│ └── <irrelevant files>

│

├── .gitignore

├── my-script.py

├── my-script.pyc

├── LICENSE

└── README.md

It turns out that those first two problems only occur when you open the project on a Windows (or other non-MacOS) machine. If you’re curious, it’s because the __MACOSX/ directory contains metadata about the files specific to MacOS, such as the icon image and other information. When the archive is opened on a machine not running MacOS, it doesn’t know what to do with the data in __MACOSX/ so it just leaves it as it’s own directory.

If you want to see this issue even on a Mac, you can unzip it through the terminal with the following:

unzip my-project.zip -d my-project

Now all of the listed problems are visible.

Of course, the project still worked even with all of those issues. I still wanted to remove them, however, because they made the project unnecessarily complicated, and the goal is always to make the user experience as simple and intuitive as possible.

So let’s fix them

Instead of using the GUI and clicking on Compress “my-project,” we can also use the Terminal to make ZIP archives.

Creating ZIP Archives With the Terminal

Navigate to the project directory, and run the following:

zip -r my-project.zip .

Creating archives this way eliminates the first two problems of the __MACOSX folder and the actual project folder being an extra directory deep in the project.

However, we still have the .git directory and the .pyc file. So let’s get rid of those too.

Excluding files from the ZIP archive

You can exclude files from the archive with the -x <ExcludedFileNames> option.

We can use -x “.*” to exclude all hidden files, which will eliminate the .git/ directory and .gitignore file, as well as any .DS_Store files that also end up in the archive.

We can also use -x “*.pyc” to exclude the .pyc files (or any other files you may want to exclude). Put those together, and we have the following:

zip -r my-project.zip . -x ".*" -x "*.pyc"

With this command, we now have a ZIP archive that, when expanded, yields a directory with the desired structure:

my-project/

│

├── my-script.py

├── LICENSE

└── README.md

Of course, you may not want to exclude all hidden files, so you don’t need to use the -x “.*” option. Instead you use a more specific statement such as -x “.git/” or just leave it out entirely.

Also, the -x “*.pyc” option is specific for Python projects, so you should replace it or get rid of it if you are using another language.

At the end, we are left with two shell commands to add to the toolbox: