BASH script build AppImage

A script to automate the creation of AppImages and to upload the finished AppImage to a server

This AppImage can update itself. I use zsync to update. Which means that very little data has to be downloaded when updating.

The script detects whether it is 32- or 64-bit architecture.

You need to edit to match the versions of Qt you are using. You also need to adapt the script to the folder structure of the server to which you upload the finished AppImage.


The script also creates a text file with information about the AppImage's MD5 sum, the ApppImage's size and the date and time of the upload. The text file is uploaded at the same time as the AppImage is uploaded to the server.

Light  |  Dark

# EDIT
QT5=5.15.10
QT6=6.5.2
EXECUTABLE=streamcapture2
SUPORTEDGLIBC=2.31
# STOP EDIT
BIN_DIR="/bin.ceicer.com/public_html/${EXECUTABLE}/bin/linux/GLIBC${GLIBCVERSION}"
ZSYNC_ADDRESS="http://bin.ceicer.com/zsync/${EXECUTABLE}/GLIBC${GLIBCVERSION}/"
ZSYNC_DIR="/bin.ceicer.com/public_html/zsync/${EXECUTABLE}/GLIBC${GLIBCVERSION}"

My script uses

Qt

linuxdeployqt

appimagetool

zsync



Example (Double click for full screen)

Step 1
Step 1

Step 2
Step 2

Step 3
Step 3

Step 4
Step 4

Step 5
Step 5

Step 6
Step 6

Download script

build_appimage.sh.tar.gz

I use a small c++ program so that the script knows which GLIBC version is used when creating the AppImage. A script works just as well.

The source code for the c++ program   |   Light  |  Dark  |   Copy to clipboard


#include <stdio.h>
#ifdef __GLIBC__
#include <gnu/libc-version.h>
#endif

int main(void)
{
    #ifdef __GLIBC__
        printf("%u.%u\n", __GLIBC__, __GLIBC_MINOR__);
        return 0;
    #else
        return 1;
    #endif
}

build_appimage.sh   |   Light  |  Dark   |  Open in new tab  |   Copy to clipboard

          
#!/bin/bash

# build_appimage.sh Copyright (C) 2023 Ingemar Ceicer
# https://ceicer.eu
# programming@ceicer.com

# build_appimage.sh is free software:
# you can redistribute it and/or modify
# it under the terms of the
# GNU General Public License as published by
# the Free Software Foundation, version 3.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# EDIT
QT5=5.15.10
QT6=6.5.2
EXECUTABLE=streamcapture2
SUPORTEDGLIBC=2.31
# STOP EDIT

RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
BOLD=$(tput bold)
NORMAL=$(tput sgr0)
GLIBCVERSION=$(glibcversion)
READMORE=https://gitlab.com/posktomten/${EXECUTABLE}

if [ $(getconf LONG_BIT) = 64 ]; then
    GCC="gcc_64"
else
    GCC="gcc_32"
fi

function doZsynkMake() {

    PS3=$(echo -e "${BLUE}Pleas Select Stable or BETA ${NC}")
    stable_or_beta=("Stable" "BETA" "Quit")
    select opt in "${stable_or_beta[@]}"; do

        case "$opt" in
        "Stable")
            BIN_DIR="/bin.ceicer.com/public_html/${EXECUTABLE}/bin/linux/GLIBC${GLIBCVERSION}"
            ZSYNC_ADDRESS="http://bin.ceicer.com/zsync/${EXECUTABLE}/GLIBC${GLIBCVERSION}/"
            ZSYNC_DIR="/bin.ceicer.com/public_html/zsync/${EXECUTABLE}/GLIBC${GLIBCVERSION}"
            echo "${EXECUTABLE} AppImage will be uploaded to"
            echo $BIN_DIR
            break
            ;;
        "BETA")
            BIN_DIR="/bin.ceicer.com/public_html/${EXECUTABLE}/bin/linux/GLIBC${GLIBCVERSION}/BETA"
            ZSYNC_ADDRESS="http://bin.ceicer.com/zsync/${EXECUTABLE}/GLIBC${GLIBCVERSION}/BETA/"
            ZSYNC_DIR="/bin.ceicer.com/public_html/zsync/${EXECUTABLE}/GLIBC${GLIBCVERSION}/BETA"
            echo "${EXECUTABLE} AppImage will be uploaded to"
            echo $BIN_DIR
            break
            ;;
        "Quit")
            echo "Goodbye!"
            exit 0
            break
            ;;

        esac
    done

    whitespace="     "
    date_now=$(date "+%F %H:%M:%S")

    for i in *.AppImage; do
        filelenght=$(("${#i}" + 41))
        #if [[ $filelenght -le 57 ]] ;
        #	then
        #		filelenght=56
        #fi

        streck=""

        while [[ ${#streck} -le ${filelenght} ]]; do
            streck="${streck}-"

        done

        echo "${streck}" >"$i-MD5.txt"
        echo "MD5 HASH SUM" >>"$i-MD5.txt"
        md5sum "$i" >>"$i-MD5.txt"
        echo "${streck}" >>"$i-MD5.txt"
        filesize=$(stat -c%s "$i")
        mb=$(bc <<<"scale=6; $filesize / 1048576")
        mb2=$(echo $mb | awk '{printf "%f", $1 + $2}')
        echo "Uploaded: $date_now" >>"$i-MD5.txt"
        echo "Size:$whitespace$mb2 MB" >>"$i-MD5.txt"
        echo "$i" requires GLIBC higher than or equal to "${GLIBCVERSION}" >>"$i-MD5.txt"
        echo "${streck}" >>"$i-MD5.txt"
        echo "HOW TO CHECK THE HASH SUM" >>"$i-MD5.txt"
        echo "${streck}" >>"$i-MD5.txt"
        echo "LINUX" >>"$i-MD5.txt"
        echo "\"md5sum\" is included in most Linus distributions." >>"$i-MD5.txt"
        echo "md5sum $i" >>"$i-MD5.txt"
        echo "${streck}" >>"$i-MD5.txt"
        echo "WINDOWS" >>"$i-MD5.txt"
        echo "\"certutil\" is included in Windows." >>"$i-MD5.txt"
        echo "certutil -hashfile $i md5" >>"$i-MD5.txt"
        echo "${streck}" >>"$i-MD5.txt"
        echo "READ MORE" >>"$i-MD5.txt"
        echo "$READMORE" >>"$i-MD5.txt"
        echo "${streck}" >>"$i-MD5.txt"

        echo
        echo " _   _                      _____               _ " >>"$i-MD5.txt"
        echo "| | | |  __ _ __   __ ___  |  ___|_   _  _ __  | |" >>"$i-MD5.txt"
        echo "| |_| | / _  |\ \ / // _ \ | |_  | | | ||  _ \ | |" >>"$i-MD5.txt"
        echo "|  _  || (_| | \ V /|  __/ |  _| | |_| || | | ||_|" >>"$i-MD5.txt"
        echo "|_| |_| \__,_|  \_/  \___| |_|    \__,_||_| |_|(_)" >>"$i-MD5.txt"

    done

    echo "----------------------------------------------------------------"
    PS3=$(echo -e "${BLUE}Please enter your choice: ${NC}")
    options=("Upload AppImage" "Upload AppImage later" "Quit")
    select opt in "${options[@]}"; do

        case $opt in
        "Upload AppImage")
            echo "You chose $REPLY which is $opt"

            cp -f *.AppImage ${EXECUTABLE}-zsyncmake/
            cp -f *.AppImage-MD5.txt ${EXECUTABLE}-zsyncmake/
            cd ${EXECUTABLE}-zsyncmake

            for i in *.AppImage; do

                ./zsyncmake "$i" -u "${ZSYNC_ADDRESS}/${i}"

            done

            HOST="$(sed -n 1p ../../secret)"
            USER="$(sed -n 2p ../../secret)"
            PASSWORD="$(sed -n 3p ../../secret)"

            for fil in *.AppImage; do

                ftp -v -inv $HOST <<EOF
passive
user $USER $PASSWORD
cd "$BIN_DIR"
put $fil
cd "$ZSYNC_DIR"
put $fil
bye
EOF
            done

            for fil in *.zsync; do

                ftp -v -inv $HOST <<EOF
passive
user $USER $PASSWORD
cd "$ZSYNC_DIR"
put $fil
bye
EOF

            done

            for fil in *AppImage-MD5.txt; do

                ftp -v -inv $HOST <<EOF
passive    
user $USER $PASSWORD
cd "$BIN_DIR"
put $fil
bye
EOF

            done
            break
            ;;

        "Upload AppImage later")
            cp -f *.AppImage ${EXECUTABLE}-zsyncmake/
            cd ${EXECUTABLE}-zsyncmake

            for i in *.AppImage; do

                ./zsyncmake "$i" -u "${ZSYNC_ADDRESS}/$i"

            done
            break
            ;;

        "Quit")
            echo "Goodbye"
            exit 0
            ;;
        *)
            echo "invalid option $REPLY"
            ;;
        esac
    done

} #doZsynkMake

possibleappdirs=$($(echo ls -d AppDir*))

for i in ${possibleappdirs[*]}; do

    options+=(${i})

done

PS3=$(echo -e "${BLUE}Pleas Select AppDir to create AppImage from\nor create *.zsync file and MD5 sum: ${NC}")
select opt in "${options[@]}" "Create *.zsync fil and MD5 sum" "Quit"; do
    case "$REPLY" in
    $((${#options[@]} + 1)))
        echo "Create *.zsync file and MD5 sum"
        doZsynkMake
        break
        ;;
    $((${#options[@]} + 2)))
        echo "Goodbye!"
        exit 0
        break
        ;;

    esac
    [ $REPLY -gt $((${#options[@]} + 1)) -o $REPLY -lt 1 ] && echo "Invalid selection" || break
done

echo "You chose $opt which is $(pwd)/${options[(($REPLY - 1))]}"

appdirname="${options[(($REPLY - 1))]}"

if [ -d "$(pwd)/$appdirname" ]; then
    echo "You have chosen to create an AppImage from the folder $(pwd)/$appdirname"

else
    echo "Error: Directory $(pwd)/$appdirname does not exists."

fi

echo "----------------------------------------------------------------"
PS3=$(echo -e "${BLUE}Please enter Qt version: ${NC}")
options=("5.15.2" "${QT5}" "${QT6}" "Create *.zsync file and MD5 sum" "Quit")
select opt in "${options[@]}"; do
    case $opt in
    "5.15.2 64-bit")
        export LD_LIBRARY_PATH="/opt/Qt/5.15.2/${GCC}/lib:${LD_LIBRARY_PATH}"
        export PATH="/opt/Qt/5.15.2/${GCC}/bin:${PATH}"
        echo "You chose $REPLY which is $opt"
        qmakeexecutable="/opt/Qt/5.15.2/${GCC}/bin/qmake"
        break
        ;;
    "${QT5}")
        export LD_LIBRARY_PATH="/opt/Qt/${QT5}/${GCC}/lib:${LD_LIBRARY_PATH}"
        export PATH="/opt/Qt/${QT5}/${GCC}/bin:${PATH}"
        echo "You chose $REPLY which is $opt"
        qmakeexecutable="/opt/Qt/${QT5}/${GCC}/bin/qmake"
        break
        ;;
    "${QT6}")
        export LD_LIBRARY_PATH="/opt/Qt/${QT6}/${GCC}/lib:${LD_LIBRARY_PATH}"
        export PATH="/opt/Qt/${QT6}/${GCC}/bin:${PATH}"
        echo "You chose $REPLY which is $opt"
        qmakeexecutable="/opt/Qt/${QT6}/${GCC}/bin/qmake"
        break
        ;;
    "Create *.zsync file and MD5 sum")
        echo "You chose $REPLY which is $opt"
        doZsynkMake
        break
        ;;
    "Quit")
        echo "Goodbye"
        echo "You chose $REPLY which is $opt"
        exit 0
        ;;
    *) echo "invalid option $REPLY" ;;
    esac
done
echo -----------------------------------------------------------
PS3=$(echo -e "${BLUE}Please enter your choice: ${NC}")
options=("Sign AppImage" "Do not sign AppImage" "Quit")
select opt in "${options[@]}"; do

    case $opt in
    "Sign AppImage")
        echo "You chose $REPLY which is $opt"
        signera=1
        break
        ;;
    "Do not sign AppImage")
        echo "You chose $REPLY which is $opt"
        signera=0
        break
        ;;
    "Quit")
        echo "Goodbye"
        exit 0
        ;;
    *) echo "invalid option $REPLY" ;;
    esac
done

echo "----------------------------------------------------------------"
PS3=$(echo -e "${BLUE}Please enter your choice: ${NC}")
options=("Make an AppImage" "Quit")
select opt in "${options[@]}"; do

    case $opt in
    "Make an AppImage")
        echo "You chose $REPLY which is $opt"

        if [[ (${GLIBCVERSION} > ${SUPORTEDGLIBC}) ]]; then
            echo -e ${BLUE}${BOLD}UNSUPORTED!${NORMAL}${NC}
            ~/bin/linuxdeployqt ${appdirname}/usr/bin/${EXECUTABLE} -qmake=$qmakeexecutable -bundle-non-qt-libs -verbose=2 -no-translations -no-copy-copyright-files -unsupported-allow-new-glibc
        else
            echo -e ${BLUE}${BOLD}SUPORTED!${NORMAL}${NC}
            ~/bin/linuxdeployqt ${appdirname}/usr/bin/${EXECUTABLE} -qmake=$qmakeexecutable -bundle-non-qt-libs -verbose=2 -no-translations -no-copy-copyright-files
        fi

        MACHINE_TYPE=$(uname -m)
        if [ ${MACHINE_TYPE} == 'x86_64' ]; then

            if [ "$signera" -gt 0 ]; then
                ARCH=x86_64 ~/bin/appimagetool --sign ${appdirname}
            else
                ARCH=x86_64 ~/bin/appimagetool ${appdirname}
            fi
        else

            if [ "$signera" -gt 0 ]; then
                ARCH=i386 ~/bin/appimagetool --sign ${appdirname}
            else
                ARCH=i386 ~/bin/appimagetool ${appdirname}
            fi

        fi
        break
        ;;
    "Quit")
        echo "Goodbye"
        exit 0
        ;;
    *) echo "invalid option $REPLY" ;;
    esac
done