-
-
Save meramsey/b2a01dcbe37a83e03eabe540266446b3 to your computer and use it in GitHub Desktop.
AppImage Auto Installer Builder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# | |
# Copyright 2020 Frank David Martinez M. (mnesarco at github) | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# | |
# +------------------------------------------------------------------------+ | |
# | AppImage Auto Installer Builder | | |
# +------------------------------------------------------------------------+ | |
# | | | |
# | Converts an AppImage into a POSIX shell script that will | | |
# | deploy the AppImage to $HOME/.appimage and create valid icon | | |
# | and desktop launcher in $HOME/.local/shared/... | | |
# | | | |
# | usage: build-installer.py [-h] --appimage IMAGE [--exe-name EXE] | | |
# | [--app-name NAME] [--app-category CATEGORY] | | |
# | [--icon ICON] [--appimage-version VERSION] | | |
# | | | |
# | AppImage Installer Builder | | |
# | | | |
# | arguments: | | |
# | -h, --help show this help message and exit | | |
# | --appimage IMAGE, -a IMAGE | | |
# | AppImage file | | |
# | --exe-name EXE, -e EXE | | |
# | Final name of the installed executable | | |
# | --app-name NAME, -n NAME | | |
# | Final name of the application | | |
# | --app-category CATEGORY, -c CATEGORY | | |
# | Application's category | | |
# | --icon ICON, -i ICON Icon file path | | |
# | --appimage-version VERSION, --iv VERSION | | |
# | AppImage version | | |
# | | | |
# | Result: | | |
# | The result is a shell script named ${EXE}-installer.sh ready to be | | |
# | distributed. The final user downloads the installer and execute it. | | |
# | after that, the AppImage is installed and have a launcher. | | |
# +------------------------------------------------------------------------+ | |
import sys, argparse, os, re, stat | |
# ------------------------- | |
# CLI Arguments | |
# ------------------------- | |
parser = argparse.ArgumentParser(description='AppImage Installer Builder') | |
parser.add_argument('--appimage', '-a', help='AppImage file', required=True, dest="image") | |
parser.add_argument('--exe-name', '-e', help='Final name of the installed executable', dest="exe") | |
parser.add_argument('--app-name', '-n', help='Final name of the application', dest="name") | |
parser.add_argument('--app-category', '-c', help='Application\'s category', dest="category") | |
parser.add_argument('--icon', '-i', help='Icon file path', dest="icon") | |
parser.add_argument('--appimage-version', '--iv', help='AppImage version', dest="version") | |
args = parser.parse_args() | |
if not os.path.exists(args.image): | |
print("File {0} does not exists".format(args.image)) | |
exit(1) | |
if not args.exe: | |
args.exe = re.compile(r'^[^\-]+').search(args.image).group() | |
if not args.name: | |
args.name = args.exe | |
if not args.category: | |
args.category = "Utility" | |
if not args.version: | |
ver = re.compile(r'^[^\-]+-([^\-]+)').search(args.image) | |
if ver: | |
args.version = ver.group(1) | |
else: | |
args.version = '1' | |
if not args.icon: | |
args.icon = 'icon.svg' | |
if not os.path.exists(args.icon): | |
args.icon = 'icon.png' | |
if not os.path.exists(args.icon): | |
print("File {0} does not exists".format(args.icon)) | |
exit(1) | |
if args.icon.lower().endswith('.svg'): | |
icon_type = 'scalable' | |
else: | |
icon_type = '128x128' | |
icon_name = args.exe.lower() | |
# ------------------------- | |
# XDG Desktop file | |
# ------------------------- | |
desktop=""" | |
[Desktop Entry] | |
Name={name} | |
Exec=$HOME/.appimage/{exe} %f | |
Icon={icon} | |
Type=Application | |
Categories={cat}; | |
X-AppImage-Integrate=true | |
Name[en_US]={exe}.desktop | |
X-AppImage-Version={ver} | |
TryExec=$HOME/.appimage/{exe} | |
Terminal=false | |
Comment= | |
""".format(name=args.name, exe=args.exe, icon=icon_name, cat=args.category, ver=args.version) | |
# ------------------------- | |
# File offsets | |
# ------------------------- | |
icon_size = os.stat(args.icon).st_size | |
image_size = os.stat(args.image).st_size | |
icon_start = 1024 | |
image_start = icon_start + icon_size | |
# ------------------------- | |
# File paths | |
# ------------------------- | |
icon_dir="$HOME/.local/share/icons/hicolor/{0}/apps".format(icon_type) | |
icon_file="{0}/{1}.{2}".format(icon_dir, icon_name, 'svg' if icon_type == 'scalable' else 'png') | |
desktop_dir="$HOME/.local/share/applications" | |
desktop_file="{0}/{1}.desktop".format(desktop_dir, args.exe) | |
image_dir="$HOME/.appimage" | |
image_file="{0}/{1}".format(image_dir, args.exe) | |
# ------------------------- | |
# Shell script | |
# ------------------------- | |
script = """#!/bin/bash | |
cat > _tmp_desktop <<EOM{desktop} | |
EOM | |
mkdir -p "{icon_dir}" | |
dd if="$0" of={icon_file} bs=1 skip={icon_start} count={icon_size} | |
mkdir -p "{desktop_dir}" | |
mv _tmp_desktop {desktop_file} | |
chmod +x {desktop_file} | |
gio set {desktop_file} "metadata::trusted" yes | |
mkdir -p "{image_dir}" | |
tail -c{image_size} "$0" > {image_file} | |
chmod +x {image_file} | |
ln -sf {desktop_file} | |
exit 0 | |
""".format( | |
desktop=desktop, | |
icon_dir=icon_dir, | |
icon_file=icon_file, | |
icon_start=icon_start, | |
icon_size=icon_size, | |
desktop_dir=desktop_dir, | |
desktop_file=desktop_file, | |
image_dir=image_dir, | |
image_file=image_file, | |
image_start=image_start, | |
image_size=image_size, | |
exe=args.exe) | |
# ------------------------- | |
# Pad alignment | |
# ------------------------- | |
pad = icon_start - len(script.encode('ascii')) | |
script += "#" * pad | |
# ------------------------- | |
# Write Installer | |
# ------------------------- | |
installer = "{exe}-installer.sh".format(exe=args.exe) | |
with open(installer, 'wb') as f: | |
f.write(script.encode('ascii')) | |
with open(args.icon, 'rb') as icon_f: | |
f.write(icon_f.read()) | |
with open(args.image, 'rb') as image_f: | |
while True: | |
dat = image_f.read(8192) | |
if not dat: | |
break | |
f.write(dat) | |
st = os.stat(installer) | |
os.chmod(installer, st.st_mode | stat.S_IEXEC) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment