Guide to using MakeSelf tool to create self-extracting shell scripts for easy distribution
How to package your shell scripts project as a release which can be easily downloaded, extracted, and run.
This is intended to work with one or more executable scripts that use a shell shebang. This could work with other shebangs I guss, but you're probably better off using the packaging approach for that language.
This guide is written for macOS / Linux.
The tool we use here is MakeSelf.
See the makeself.io docs for more info.
You can run MakeSelf locally or as part of a Github Action triggered on a release to package your project directory as a single downloadable file - usually with .run
extension. This can be included on a release and then downloaded and unpacked by users.
The MakeSelf approach is similar to using a Github download/archive/zip file but does not require use of unzip
or tar
.
The .run
file can be run as script with project-specific help in the output. The .run
file can be read easily directly as a script in a text editor - a portion of the script will be compressed binary data which is not human-readable but which the script itself can extract.
When your bundled release is unpacked, as setup script can be run automatically. This can be useful for installing dependencies oror installing the project into a project directory e.g. ~/.my-project
or ~/.local/my-project
.
The MakeSelf flow is suited to shell projects which do not have a packaging pattern to flow like that of Node packages, Ruby gems or Python packages.
The MakeSelf approach is cleaner than providing a Github archive download link (for master or a tag), since the Github approach always includes the entire repo - including tests and docs.
NB. For a small project, the standard Github flow might be fine and you don't have to use MakeSelf and any of the shell command and Github Actions overhead needed for creating a release. Just created a tag and view the tag/release on Github. Also remember to include tar
/ unzip
and make
instructions in your docs so users can setup your project by hand.
I wrote this guide to make it accessible for myself to follow the MakeSelf approach.
This guide covers how to:
- Install Makeself
- Run Makeself
- Bundle a release for your own project
- Download and run your release
If you use a Debian-based system, you can install from APT
packages. This should be useful locally and for adding as a step in a Github Action workflow.
$ sudo apt install makeself
See the Makeself Debian Wiki page.
Go to the releases page.
Look at the latest release at the top. For this tutorial, we use 2.4.2.
Click on the first item on the assets section.
- e.g.
makeself-2.4.2.run
which points to https://github.com/megastep/makeself/releases/download/release-2.4.2/makeself-2.4.2.run
Save it to your Downloads directory.
$ cd Downloads/
$ ls
makeself-2.4.2.run
The downloaded file should be there. A portion of the script is binary content - this will be unzipped into multiple files.
Check options on the run file. These will similar to what you actually create as your own later on.
$ ./makeself-2.4.2.run --help
Run it.
$ ./makeself-2.4.2.run
Check the contents of the makeself-2.4.2/
directory created. The type annotation of *
below means executable.
$ ls -F makeself-2.4.2/
COPYING makeself.1 makeself-header.sh* makeself.lsm makeself.sh* README.md test/ VERSION
Note the makeself.sh
script which is the entry point we need. And makeself-header.sh
which is a dependency.
Rather than keeping MakeSelf in your Downloads directory, move it somewhere more permanent. Ideally keep all the license files and docs with it.
Move the unpacked directory. Rename it so it is easy to upgrade later.
$ mv makeself-2.4.2/ ~/.local/makeself
The section assumes $HOME/.local/bin
is in your PATH
.
$ mkdir -p ~/.local/bin
Symlink the main script and its header file in a bin directory. Note the header file must be in the same directory as the main script otherwise it can't be found when we run it later.
$ ln -s ~/.local/makeself/makeself.sh ~/.local/bin
$ ln -s ~/.local/makeself/makeself-header.sh ~/.local/bin
Now you can run the script from anywhere.
$ cd ~
$ makeself.sh -h
...
Check the help at any time.
$ ./makeself.sh -h
...
Usage: ./makeself.sh [args] archive_dir file_name label startup_script [script_args]
Note the order is important - the args
(options) all use --
prefix and are optional. Since MakeSelf cannot differentiate between arguments given as options or as script args except by their position around the main args.
Option | Description |
---|---|
--notemp |
The generated archive will not extract the files to a temporary directory, but in a new directory created in the current directory. This is better to distribute software packages that may extract and compile by themselves (i.e. launch the compilation through the embedded script). |
--license FILE |
Include a license. |
See the docs or CLI help for all available options.
These are copied from the docs:
Argument | Description |
---|---|
archive_dir |
is the name of the directory that contains the files to be archived |
file_name |
is the name of the archive to be created |
label |
is an arbitrary text string describing the package. It will be displayed while extracting the files. |
startup_script |
is the command to be executed from within the directory of extracted files. Thus, if you wish to execute a program contain in this directory, you must prefix your command with ./ . For example, ./program will be fine. The script_args are additional arguments for this command. |
My notes:
- The
archive_dir
value could be your repo (or.
within your repo) or it could be a subdirectory likeapp
if your want to exclude tests and docs at the top-level. Based on the flow in the MakeSelf repo'smake-release.sh
script, you could create a temporary directory, copy distributable files to it and then pass that directories path to MakeSelf to bundle that. - A multi-word
label
value should be in quotes as per the examples. - The
startup_script
- It could be
echo
followed by the arguments as what you want to echo. Example from the docs:echo "Makeself has extracted itself
- It could be the install steps. e.g.
./configure && make && make install
- It could be
An example copied from the docs:
# CMD archive_dir file_name label startup_script
./makeself.sh /home/joe/mysoft mysoft.sh "Joe's Nice Software Package" ./setup
How to use MakeSelf to create a bundled release file for your your project
Ensure you have the makeself.sh
downloaded and that you can run it, as per the instructions in the previous sections.
Navigate to your target repo. Here I used my auto-tag
repo.
$ cd ~/repos/auto-tag
Prepare:
$ mkdir /tmp/autotag-dist
$ cp autotag LICENSE /tmp/autotag-dist/
In this case, autotag
is an executable shell script that just has extension in the name. You could replace with your own value like my_script.sh
.
Bundle and output a file in the release
directory of your project.
$ makeself.sh
--license LICENSE
--no-temp \
/tmp/autotag-dist \
release/autotag.run \
'Increment git tags the smart/lazy way' \
echo 'Installed autotag'
Header is 668 lines long
About to compress 12 KB of data...
Adding files to archive named "autotag.run"...
./
./LICENSE
./autotag
./LICENSE
./autotag
CRC: 365064486
MD5: 94bd3701c6013ec091cf7158c2c68767
Self-extractable archive "autotag.run" successfully created.
I don't know why the content names appear duplicated above.
Note that by specifying --license file
as an option, when you unpackage the file you'll see the license in the terminal and then be prompted to enter y
to accept.
One could also include a VERSION
file with a release number in it.
Include the .run
file in a release by dragging it to the Github Page manually. Or using Github Actions - see release.yml or the example below.
.github/worksflows/release.yml
steps:
- name: Checkout project
uses: actions/checkout@v2
- name: Build release
run: make
- name: Upload to release
uses: fnkr/github-action-ghr@v1
env:
GHR_PATH: release/
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Move the local run file (e.g. autotag.run
) to Downloads or download one from your releases.
$ cd ~/Downloads
Check the help.
$ autotag.run -h
View the contents if you want.
$ view autotag.run
Unpack it.
$ autotag-run
Creating directory autotag-dist
Verifying archive integrity... 100% MD5 checksums are OK. All good.
Uncompressing AutoTag - Increment git tags the smart/lazy way 100%
Installed autotag
$ ls autotag-dist/
autotag LICENSE
Note that output directory name here (e.g. autotag-dist
) will be the based as name of the directory that was originally packaged (/tmp/autotag-dist
).
If there were any install commands with make
those could have be executed at the point.
If there was a setup
or install
script (triggered by make install
) that could even copy the files to a new directory outside of Downloads. Such as ~/.local/autotag
.
Files in the source repo:
- make-release.sh script
- workflows to build or release