Skip to content

Commit

Permalink
Merge pull request #1 from decoyjoe/build-sanoid-portable
Browse files Browse the repository at this point in the history
Create sanoid-portable
  • Loading branch information
decoyjoe authored Nov 27, 2024
2 parents b996253 + e5ed555 commit 637dafd
Show file tree
Hide file tree
Showing 8 changed files with 965 additions and 202 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build/
875 changes: 674 additions & 201 deletions LICENSE

Large diffs are not rendered by default.

74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,74 @@

# sanoid-portable
A portable self-contained binary build of Sanoid, a ZFS snapshot management tool.

Run [Sanoid](https://github.com/jimsalterjrs/sanoid) without installing any dependencies.

## Summary

*sanoid-portable* is a self-contained, portable build of the [Sanoid](https://github.com/jimsalterjrs/sanoid) ZFS
snapshot management tool. Built using [APPerl (Actually Portable Perl)](https://computoid.com/APPerl/), this
self-contained, portable binary encompasses the Perl runtime, all required Perl dependencies, and the Sanoid script
itself. This enables you to run Sanoid on any Linux or FreeBSD system without needing to install additional Perl
dependencies or configure the system's Perl environment.

This is useful if you'd like to use Sanoid on an appliance-like storage system, such as TrueNAS, where standard package
installations are restricted or non-ideal.

## Installation

Download the latest version of sanoid-portable from the GitHub releases and make it executable:

```console
wget https://github.com/decoyjoe/sanoid-portable/releases/latest/download/sanoid-portable
chmod +x sanoid-portable
```

Create symbolic links for each tool you plan to use (sanoid-portable uses the invoking command name (`argv[0]`) to
determine its behavior):

```console
ln -s sanoid-portable sanoid
ln -s sanoid-portable syncoid
ln -s sanoid-portable findoid
```

## Usage

Invoke the symbolic link:

```console
./sanoid --help
./syncoid --help
./findoid --help
```

Refer to the [Sanoid documentation](https://github.com/jimsalterjrs/sanoid) for configuration instructions.

### Important Compatibility Note

sanoid-portable ***must*** be run from a Thompson Shell-compatible shell such as `bash` ([due to a limitation of
APPerl](https://computoid.com/APPerl/)). It is *not* compatible with shells like `zsh` or `fish`, which will fail to run
sanoid-portable with an error such as: `zsh: exec format error: sanoid-portable`.

## Developing

Run the initialization script to prepare your environment to build the sanoid-portable executable on a Debian-based
system:

```console
./init.sh
```

Build the executable:

```console
./build.sh
```

This script will download and configure APPerl, download necessary Perl modules, and build the portable Sanoid binary.

The executable gets built to `build/sanoid-portable`.

## License

This project is licensed under the GPL v3.0 license - see the [LICENSE](LICENSE) file for details.
25 changes: 25 additions & 0 deletions apperl-project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"apperl_configs": {
"sanoid-portable": {
"desc": "Portable sanoid binary",
"base": "full",
"dest": "sanoid-portable",
"default_script": "/zip/bin/sanoid-portable.pl",
"install_modules": [
"Module-Build-0.4234.tar.gz",
"Config-IniFiles-3.000003.tar.gz",
"Capture-Tiny-0.48.tar.gz"
],
"zip_extra_files": {
"bin": [
"sanoid-portable.pl",
"sanoid_source/sanoid",
"sanoid_source/syncoid",
"sanoid_source/findoid",
"versions.json"
]
}
}
},
"defaultconfig": "sanoid-portable"
}
108 changes: 108 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/bin/bash

set -euo pipefail

SANOID_VERSION=$(jq -r '.Sanoid' versions.json)
PACKAGING_REVISION=$(jq -r '.PackagingRevision' versions.json)
APPERL_VERSION=$(jq -r '.APPerl' versions.json)
SANOID_PORTABLE_VERSION="${SANOID_VERSION}-${PACKAGING_REVISION}"

echo "Building sanoid-portable version ${SANOID_PORTABLE_VERSION}, based on Sanoid version ${SANOID_VERSION} and APPerl version ${APPERL_VERSION}"

repo_root="$(realpath "$(dirname "$0")")"

# Cleanup previous artifacts if they exist
if [ -d build ]; then
echo 'Cleaning up previous build...'
rm -rf build
fi

mkdir build
pushd build > /dev/null

echo 'Downloading necessary modules...'

# Perl build dependency
# https://metacpan.org/dist/Module-Build
wget https://cpan.metacpan.org/authors/id/L/LE/LEONT/Module-Build-0.4234.tar.gz

# Sanoid dependency
# https://metacpan.org/dist/Config-IniFiles
wget https://cpan.metacpan.org/authors/id/S/SH/SHLOMIF/Config-IniFiles-3.000003.tar.gz

# Sanoid dependency
## https://metacpan.org/dist/Capture-Tiny
wget https://cpan.metacpan.org/authors/id/D/DA/DAGOLDEN/Capture-Tiny-0.48.tar.gz

echo 'Cloning sanoid repository...'
rm -rf sanoid_source
git clone https://github.com/jimsalterjrs/sanoid.git sanoid_source
echo ''

echo "Checking out Sanoid version \"${SANOID_VERSION}\""
pushd sanoid_source > /dev/null
git -c advice.detachedHead=false checkout "v${SANOID_VERSION}"
git log -1
popd > /dev/null
echo ''

echo 'Downloading APPerl (Actually Portable Perl)...'
wget -O perl.com "https://github.com/G4Vi/Perl-Dist-APPerl/releases/download/v${APPERL_VERSION}/perl.com"
chmod u+x perl.com

echo 'APPerl (perl.com) SHA-256 checksum:'
sha256sum perl.com
echo ''

# Bootstrap; use APPerl to build a custom APPerl.
ln -s perl.com apperlm

cp "${repo_root}/apperl-project.json" .
cp "${repo_root}/sanoid-portable.pl" .
cp "${repo_root}/versions.json" .

echo 'Installing build dependencies...'
./apperlm install-build-deps

echo 'Checking out the APPerl sanoid-portable build...'
./apperlm checkout sanoid-portable

echo 'Configuring build environment...'
./apperlm configure

echo 'Building sanoid-portable...'
./apperlm build

echo ''
echo 'Build complete.'
echo ''

stat sanoid-portable
echo ''

./sanoid-portable

# APPerl uses the invoking command name (argv[0]) to determine which internal script to run
ln -s sanoid-portable sanoid
ln -s sanoid-portable syncoid
ln -s sanoid-portable findoid

./sanoid --version
echo ''

echo 'Testing execution of sanoid...'
./sanoid --help
echo ''

echo 'Testing execution of syncoid...'
./syncoid --help
echo ''

echo 'Testing execution of findoid...'
./findoid --help
echo ''

echo 'SHA-256 checksum:'
sha256sum sanoid-portable

popd > /dev/null
28 changes: 28 additions & 0 deletions init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

set -euo pipefail

# Identify the operating system from /etc/os-release
OS=$(grep ^ID= /etc/os-release | cut -d '=' -f 2 | tr -d '"')

# Check if the OS is Debian or a Debian derivative (like Ubuntu)
if [[ "$OS" != 'debian' && "$OS" != 'ubuntu' ]]; then
echo 'Error: This script is intended only for Debian-based systems.'
exit 1
fi

# Determine whether to use 'sudo' or run directly (if root)
if [[ "$EUID" -ne 0 ]]; then
SUDO='sudo'
else
SUDO=''
fi

# Update package lists to ensure packages are up to date
$SUDO apt-get update

$SUDO apt-get install -y build-essential wget git unzip zip jq

echo ''
echo 'All necessary dependencies have been installed.'
echo ''
51 changes: 51 additions & 0 deletions sanoid-portable.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/perl
use strict;
use warnings;
use FindBin;
use Getopt::Long;
use JSON::PP;

open my $versions_json_file, '<', "$FindBin::Bin/versions.json" or die "Can't open versions.json: $!";
my $versions_json = do { local $/; <$versions_json_file> };
close $versions_json_file;

my $versions = decode_json($versions_json);
my $sanoid_version = $versions->{'Sanoid'};
my $packaging_revision = $versions->{'PackagingRevision'};
my $apperl_version = $versions->{'APPerl'};
my $sanoid_portable_version = "$sanoid_version-$packaging_revision";

# Only output the version number when using -V|--version
my $print_version_only = 0;
GetOptions('V|version' => \$print_version_only);

if ($print_version_only) {
print "$sanoid_portable_version\n";
exit 0;
}

# Print versions
print "sanoid-portable: $sanoid_portable_version\n";
print "Sanoid: $sanoid_version\n";
print "Perl: $^V\n"; # Built-in variable for Perl version
print "APPerl: $apperl_version\n";

my $usage = <<'USAGE';
This binary executes sanoid, syncoid, or findoid based on the name of the symbolic link invoked.
Create symbolic links to use the different tools:
ln -s sanoid-portable sanoid
ln -s sanoid-portable syncoid
ln -s sanoid-portable findoid
Make sure to make sanoid-portable executable:
chmod +x sanoid-portable
Then invoke the symlink:
./sanoid --help
./syncoid --help
./findoid --help
USAGE

print "\n$usage";
5 changes: 5 additions & 0 deletions versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"Sanoid": "2.2.0",
"PackagingRevision": "1",
"APPerl": "0.6.1"
}

0 comments on commit 637dafd

Please sign in to comment.