Skip to content

Commit

Permalink
build: rewrite appimage build config
Browse files Browse the repository at this point in the history
resolves streamlink#798

- build inside ghcr.io/streamlink/appimage-buildenv-{x86_64,i686}
  containers and copy missing libraries from the centos7 base image,
  except those being a dependency of gtk3 or being part of the appimage
  exclusion list
- remove custom appimagekit grunt task
- remove custom squashfs-tools build script
- refactor appimage build task configs and clean up related configs
- rewrite build script and split into two
  • Loading branch information
bastimeyer committed May 11, 2021
1 parent 9771757 commit e90f843
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 323 deletions.
30 changes: 0 additions & 30 deletions .github/workflows/build-squashfs-tools.sh

This file was deleted.

1 change: 0 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ jobs:
yarn install --pure-lockfile --no-progress --non-interactive
sudo apt install pigz nsis appstream{,-util}
./.github/workflows/install-wine.sh
./.github/workflows/build-squashfs-tools.sh
- name: Build
run: yarn run grunt clean:tmp_prod webpack:prod
- name: Compile & package
Expand Down
1 change: 0 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ module.exports = function( grunt ) {
tmp_test: r( "build", "tmp", "test" ),
tmp_coverage: r( "build", "tmp", "coverage" ),
tmp_installer: r( "build", "tmp", "installer" ),
tmp_appimage: r( "build", "tmp", "appimage" ),
dist: r( "dist" )
}
},
Expand Down
93 changes: 93 additions & 0 deletions build/resources/appimage/build-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -e

APPDIR="${1}"
INSTALLDIR="${2}"
VERSION="${3}"
ARCH="${4}"
LIBDIR="${APPDIR}/${INSTALLDIR}/lib"

declare -A excludepackages=(
[gtk3]=true
)


# ----


SELF=$(basename "$(readlink -f "${0}")")
log() {
echo "[${SELF}] $@"
}
err() {
log >&2 "$@"
exit 1
}

[[ $# == 4 ]] || err "Invalid arguments"
[[ -f /.dockerenv ]] || err "This script is supposed to be run from build.sh inside a docker container"

for pkg in $(repoquery --queryformat="%{name}" --requires --recursive --resolve "${!excludepackages[@]}" | sort -u); do
excludepackages["${pkg}"]=true
done

declare -A excludelibraries
for lib in $(sed -e '/#.*/d; /^[[:space:]]*|[[:space:]]*$/d; /^$/d' /usr/local/share/appimage/excludelist); do
excludelibraries["${lib}"]=true
done


find_dependencies() {
log "Finding missing dependencies for: $@"
declare -A libs
for file in "$@"; do
[[ -f "${file}" && -x "${file}" ]] || err "File does not exist or is not executable: ${file}"
for lib in $(LD_LIBRARY_PATH=$(dirname "${file}") ldd "${file}" | awk '/ => not found/ {print $1}'); do
# don't check a lib more than once
[[ -n "${excludelibraries["${lib}"]}" ]] && continue
excludelibraries["${lib}"]=true
# find the lib's package
local package=$(repoquery --queryformat="%{name}" --file "${lib}" | head -n1)
[[ -z "${package}" ]] && err "Missing package for: ${lib}"
[[ -n "${excludepackages["${package}"]}" ]] && continue
libs["${lib}"]="${package}"
done
done

[[ ${#libs[@]} == 0 ]] && return

log "Installing packages: ${libs[@]}"
yum install -y --setopt=tsflags= "${libs[@]}"
log "Copying libraries"
for lib in "${!libs[@]}"; do
for path in $(repoquery --list --archlist "${ARCH}" "${libs["${lib}"]}"); do
if [[ "$(basename -- "${path}")" == "${lib}" ]]; then
( set -x; install -m755 -t "${LIBDIR}" "${path}" )
fi
if echo "${path}" | grep -Ei '^/usr/share/(doc|licenses)/.*(copying|licen[cs]e|readme|terms).*'; then
( set -x; install -Dm644 -t "${APPDIR}${path}" "${path}" )
fi
done
done

find_dependencies $(echo "${!libs[@]}" | tr ' ' '\n' | while read -r lib; do echo "${LIBDIR}/${lib}"; done)
}
build_appimage() {
log "Building appimage"
[ "${SOURCE_DATE_EPOCH}" ] && mtime="@${SOURCE_DATE_EPOCH}" || mtime=now
find "${APPDIR}" -exec touch --no-dereference "--date=${mtime}" '{}' '+'
VERSION="${VERSION}" ARCH="${ARCH}" /usr/local/bin/appimagetool \
--verbose \
--comp gzip \
--no-appstream \
"${APPDIR}" \
output
}
build() {
find_dependencies $(find "${APPDIR}/${INSTALLDIR}" -type f -exec sh -c 'readelf -h {} >/dev/null 2>&1' \; -print)
build_appimage
}
build
172 changes: 172 additions & 0 deletions build/resources/appimage/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#!/usr/bin/env bash
set -e

# this build script is meant to be run by the dist:appimage_linux{32,64} grunt tasks
# don't run it directly unless you know what you're doing

NAME="${1}"
VERSION="${2}"
ARCH="${3}"
DOCKER_IMAGE="${4}"
DOCKER_DIGEST="${5}"
INPUT="${6}"
OUTPUT="${7}"

declare -A DEPS=(
[docker]=docker
[appstreamcli]=appstreamcli
[appstream-util]=appstreamutil
)


# ----


SELF=$(basename "$(readlink -f "${0}")")
DOCKER_SCRIPT="$(dirname "$(readlink -f "${0}")")/build-docker.sh"

log() {
echo "[${SELF}] $@"
}
err() {
log >&2 "$@"
exit 1
}

[[ $# == 7 ]] || err "Invalid arguments"

for dep in "${!DEPS[@]}"; do
command -v "${dep}" 2>&1 >/dev/null || err "Missing dependency: ${DEPS["${dep}"]}"
done


# ----


tempdir=$(mktemp -d) && trap "rm -rf ${tempdir}" EXIT || exit 255
cd "${tempdir}"

appdir="${NAME}.AppDir"
installdir="opt/${NAME}"


# ----


get_docker_image() {
log "Getting docker image"
docker image ls --digests "${DOCKER_IMAGE}" | grep "${DOCKER_DIGEST}" 2>&1 >/dev/null \
|| docker image pull "${DOCKER_IMAGE}@${DOCKER_DIGEST}"
}


prepare_appimage() {
log "Installing AppDir files"

# create AppImage AppDir
mkdir -p "${appdir}/${installdir}"

# copy app contents
cp -a "${INPUT}/." "${appdir}/${installdir}/"

# copy licenses
install -Dm644 \
-t "${appdir}/usr/share/licenses/${NAME}/" \
"${appdir}/${installdir}/LICENSE.txt" \
"${appdir}/${installdir}/credits.html"

# copy appstream metainfo
install -Dm644 \
-t "${appdir}/usr/share/metainfo/" \
"${appdir}/${installdir}/${NAME}.appdata.xml"

# copy icons
for res in 16 32 48 64 128 256; do
install -Dm644 \
"${appdir}/${installdir}/icons/icon-${res}.png" \
"${appdir}/usr/share/icons/hicolor/${res}x${res}/apps/${NAME}.png"
done

# symlink root AppImage icons
for link in "${appdir}/.DirIcon" "${appdir}/${NAME}.png"; do
ln -sr "${appdir}/usr/share/icons/hicolor/256x256/apps/${NAME}.png" "${link}"
done

# create desktop file
mkdir -p "${appdir}/usr/share/applications/"
cat > "${appdir}/usr/share/applications/${NAME}.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=Streamlink Twitch GUI
GenericName=Twitch.tv browser for Streamlink
Comment=Browse Twitch.tv and watch streams in your videoplayer of choice
Keywords=streamlink;twitch;
Categories=AudioVideo;Network;
Exec=${NAME}
Icon=${NAME}
EOF

# symlink root AppImage desktop file
ln -sr "${appdir}/usr/share/applications/${NAME}.desktop" "${appdir}/${NAME}.desktop"

# symlink executable
mkdir -p "${appdir}/usr/bin"
ln -sr "${appdir}/${installdir}/${NAME}" "${appdir}/usr/bin/${NAME}"

# AppRun
cat > "${appdir}/AppRun" <<EOF
#!/usr/bin/env bash
HERE=\$(dirname "\$(readlink -f "\${0}")")
export LD_LIBRARY_PATH="\${HERE}/${installdir}/lib\${LD_LIBRARY_PATH:+":\${LD_LIBRARY_PATH}"}"
"\${HERE}/usr/bin/${NAME}" "\${@}"
EOF
chmod +x "${appdir}/AppRun"

# remove unneeded stuff
rm -rf \
"${appdir}/${installdir}/"{add,remove}-menuitem.sh \
"${appdir}/${installdir}/"{LICENSE.txt,credits.html} \
"${appdir}/${installdir}/${NAME}.appdata.xml" \
"${appdir}/${installdir}/icons/"
}


verify_appstream() {
log "Verifying appstream data"
appstreamcli validate-tree "${appdir}/usr/share/metainfo/${NAME}.appdata.xml"
appstream-util validate-relax "${appdir}/usr/share/metainfo/${NAME}.appdata.xml"
}


build_appimage() {
log "Building appimage inside container"
local target=/build
install -m755 -t "${tempdir}" "${DOCKER_SCRIPT}"
docker run \
--interactive \
--rm \
--env SOURCE_DATE_EPOCH \
--mount "type=bind,source=${tempdir},target=${target}" \
--workdir "${target}" \
"${DOCKER_IMAGE}@${DOCKER_DIGEST}" \
/usr/bin/bash <<EOF
set -e
trap "chown -R $(id -u):$(id -g) '${target}'" EXIT
"./$(basename -- "${DOCKER_SCRIPT}")" \
"${appdir}" \
"${installdir}" \
"${VERSION}" \
"${ARCH}"
EOF
install -Dm755 "${tempdir}/output" "${OUTPUT}"
}


build() {
prepare_appimage
verify_appstream
get_docker_image
build_appimage
}

build
Loading

0 comments on commit e90f843

Please sign in to comment.