make-self-extracting
A library for the creation of self extracting shell scripts supporting Node 12 and newer.
Github: https://github.com/danishcake/make-self-extracting
NPM: https://www.npmjs.com/package/make-self-extracting
The shell scripts are designed to run under bash.
Why?
It's easier for your users if you distribute a single file, and it's called install.sh
.
Installation
Run
npm install make-self-extracting --save
Usage
The script runs in two stages:
- A 'pre-extraction' section. This runs before the embedded files have been extracted to a temporary directory, and is a good place to display a banner, gather input etc.
- A 'post-extraction' section'. This runs after the embedded files have been extracted. The working directory will have been changed to a temporary directory containing the extracted files for the duration of this section
await makeSelfExtractingScript(
{
preExtraction: 'echo This section runs in `pwd`',
postExtraction: 'cat header.txt\necho This section runs in `pwd`\nls -l'
},
[
{
filename: 'header.txt',
content: Buffer.from('This text comes from an embedded file\n', 'utf-8')
},
{
filename: 'example_zip.mjs',
content: fs.createReadStream('examples/zip_format.mjs')
},
{
filename: 'example_docker.mjs',
content: fs.createReadStream('examples/docker.mjs')
}
],
fs.createWriteStream('simple.sh')
);
This generates output that looks like this:
#!/bin/bash
# Self extracting self script created with 'make-self-extracting
# https://www.npmjs.com/package/make-self-extracting
# https://github.com/danishcake/make-self-extracting
echo This section runs in `pwd`
readonly TMPDIR=`mktemp -d`
readonly PAYLOAD_START=16
tail -n+$PAYLOAD_START $0 | tar -xz -C $TMPDIR
pushd $TMPDIR > /dev/null
cat header.txt
echo This section runs in `pwd`
ls -l
popd > /dev/null
rm -rf $TMPDIR
exit 0
# ... Compressed data
You can control how the payload files are stored using the SelfExtractingScriptOptions
argument.
type SelfExtractingScriptOptions = {
// Script to execute pre-extraction. This does not need a shebang
preExtraction?: string;
// Script content to execute post-extraction. This will be executed in a temporary directory
// containing the extracted payload
postExtraction?: string;
// If the 'Generated using' header should be omitted. Defaults to false
omitLibraryHeader?: boolean;
// Archive format. Zip has lower memory consumption during generation, but is less likely be available
// Zip uses more disk space during extraction, as it must extract the zip to a temporary directory first
// Defaults to 'tar'
archiveFormat?: 'zip' | 'tar';
// If the archive should be compressed. If the input files are already compressed it's usually better
// to just store the files instead
// Defaults to 5
compressionLevel?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
}
Additional examples are in the examples folder. They expect to be run from the project root.
You can add files for Buffers
, Readables
and paths to local files. The file mode defaults to 644 but can be set on a per file basis is required.
// Embedded shell script must be executable
{
filename: 'setup.sh',
path: 'setup.sh',
mode: 0o755
}
How it works
The embedded files are stored in an archive and appended to the shell script. The script then uses tail
on itself to extract the
archive to a temporary directory when run.
Remarks
The code herein will probably work fine under older versions of Node. The limiting factor is the jest unit test library.