Merge pull request #108 from librekeys/ci/auto-update-appimages

Support for Auto-Updating AppImages.
This commit is contained in:
Suyog Tandel
2026-07-21 11:04:38 +05:30
committed by GitHub
3 changed files with 228 additions and 95 deletions
+169 -78
View File
@@ -1,98 +1,189 @@
#!/bin/bash
set -eo pipefail
VARIANT=${1:-"glibc-x86_64"}
# ──────────────────────────────────────────────
# Utility Functions
# ──────────────────────────────────────────────
echo "=== Building PicoForge AppImage for variant: ${VARIANT} ==="
detect_src_dir() {
if [ -f "/workspace/Cargo.toml" ]; then
echo "/workspace"
elif [ -f "/workspace/picoforge/Cargo.toml" ]; then
echo "/workspace/picoforge"
else
echo "Error: Cargo.toml not found in /workspace or /workspace/picoforge"
exit 1
fi
}
# Locate PicoForge source directory
if [ -f "/workspace/Cargo.toml" ]; then
SRC_DIR="/workspace"
elif [ -f "/workspace/picoforge/Cargo.toml" ]; then
SRC_DIR="/workspace/picoforge"
else
echo "Error: Cargo.toml not found in /workspace or /workspace/picoforge"
exit 1
fi
get_version() {
local version
version="$(grep -m 1 '^version' Cargo.toml | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/')"
echo "${version:-0.0.0}"
}
cd "$SRC_DIR"
get_arch_name() {
local arch
arch="$(uname -m)"
case "${arch}" in
x86_64) echo "x86-64" ;;
aarch64) echo "aarch64" ;;
*) echo "${arch}" ;;
esac
}
# Extract version from Cargo.toml
VERSION=$(grep -m 1 '^version' Cargo.toml | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/')
if [ -z "$VERSION" ]; then
VERSION="0.0.0"
fi
echo "PicoForge Version: ${VERSION}"
compute_output_name() {
local version="$1"
local variant="$2"
local arch_name="$3"
# Determine architecture and target naming
ARCH=$(uname -m)
if [ "$ARCH" == "x86_64" ]; then
ARCH_NAME="x86-64"
elif [ "$ARCH" == "aarch64" ]; then
ARCH_NAME="aarch64"
else
ARCH_NAME="$ARCH"
fi
case "${variant}" in
glibc-2.28-x86_64|glibc-2.28-aarch64)
echo "picoforge_${version}_glibc-2.28_${arch_name}.AppImage"
;;
musl-x86_64|musl-aarch64)
echo "picoforge_${version}_musl_${arch_name}.AppImage"
;;
*)
echo "picoforge_${version}_${variant}_${arch_name}.AppImage"
;;
esac
}
# Define output filename based on variant
case "$VARIANT" in
glibc-2.28-x86_64|glibc-2.28-aarch64)
OUTPUT_NAME="picoforge_${VERSION}_glibc-2.28_${ARCH_NAME}.AppImage"
;;
musl-x86_64|musl-aarch64)
OUTPUT_NAME="picoforge_${VERSION}_musl_${ARCH_NAME}.AppImage"
;;
*)
OUTPUT_NAME="picoforge_${VERSION}_${VARIANT}_${ARCH_NAME}.AppImage"
;;
esac
# ──────────────────────────────────────────────
# Build Steps
# ──────────────────────────────────────────────
echo "Building release binary with Cargo..."
cargo build --release
setup_update_info() {
local zsync_glob="$1"
BINARY_PATH="target/release/picoforge"
DESKTOP_FILE="data/in.suyogtandel.picoforge.desktop"
ICON_FILE="static/appIcons/in.suyogtandel.picoforge.svg"
if [ -z "${GITHUB_REPOSITORY_OWNER}" ] || [ -z "${GITHUB_REPOSITORY}" ]; then
return 0
fi
if [ ! -f "$BINARY_PATH" ]; then
echo "Error: Binary not found at $BINARY_PATH"
exit 1
fi
local repo_name="${GITHUB_REPOSITORY#*/}"
export UPDATE_INFORMATION="gh-releases-zsync|${GITHUB_REPOSITORY_OWNER}|${repo_name}|latest|${zsync_glob}"
echo "Set UPDATE_INFORMATION: ${UPDATE_INFORMATION}"
}
if [ ! -f "$DESKTOP_FILE" ]; then
echo "Error: Desktop file not found at $DESKTOP_FILE"
exit 1
fi
build_release() {
echo "Building release binary with Cargo..."
cargo build --release
}
if [ ! -f "$ICON_FILE" ]; then
echo "Error: Icon file not found at $ICON_FILE"
exit 1
fi
verify_assets() {
local binary="$1"
local desktop="$2"
local icon="$3"
echo "Packaging AppImage using linuxdeploy..."
export APPIMAGE_EXTRACT_AND_RUN=1
if [ ! -f "${binary}" ]; then
echo "Error: Binary not found at ${binary}"
exit 1
fi
if [ ! -f "${desktop}" ]; then
echo "Error: Desktop file not found at ${desktop}"
exit 1
fi
if [ ! -f "${icon}" ]; then
echo "Error: Icon file not found at ${icon}"
exit 1
fi
}
# Run linuxdeploy to bundle executable, desktop entry, icon, excluding host daemon libpcsclite
rm -rf AppDir
linuxdeploy \
--appdir AppDir \
--executable "$BINARY_PATH" \
--desktop-file "$DESKTOP_FILE" \
--icon-file "$ICON_FILE" \
--exclude-library libpcsclite.so.1 \
--output appimage
package_appimage() {
local output_name="$1"
local binary="$2"
local desktop="$3"
local icon="$4"
# Find generated AppImage file and rename to standard naming
GENERATED_APPIMAGE=$(ls -t *.AppImage 2>/dev/null | head -n 1)
echo "Packaging AppImage using linuxdeploy..."
export APPIMAGE_EXTRACT_AND_RUN=1
if [ -z "$GENERATED_APPIMAGE" ]; then
echo "Error: AppImage generation failed"
exit 1
fi
if [ -n "${UPDATE_INFORMATION}" ]; then
echo "Using UPDATE_INFORMATION: ${UPDATE_INFORMATION}"
else
echo "UPDATE_INFORMATION not set — AppImage will not support auto-update"
fi
mkdir -p /workspace/target/release
mv "$GENERATED_APPIMAGE" "/workspace/target/release/${OUTPUT_NAME}"
rm -rf AppDir
export LDAI_OUTPUT="${output_name}"
echo "=== Build Complete! Output: /workspace/target/release/${OUTPUT_NAME} ==="
ls -lh "/workspace/target/release/${OUTPUT_NAME}"
rm -rf AppDir
linuxdeploy \
--appdir AppDir \
--executable "${binary}" \
--desktop-file "${desktop}" \
--icon-file "${icon}" \
--exclude-library libpcsclite.so.1 \
--output appimage
}
move_artifacts() {
local output_name="$1"
local generated_appimage
generated_appimage="$(ls -t *.AppImage 2>/dev/null | head -n 1)"
if [ -z "${generated_appimage}" ]; then
echo "Error: AppImage generation failed"
exit 1
fi
mkdir -p /workspace/target/release
mv "${generated_appimage}" "/workspace/target/release/${output_name}"
local generated_zsync
generated_zsync="$(ls -t *.zsync 2>/dev/null | head -n 1)"
if [ -n "${generated_zsync}" ]; then
mv "${generated_zsync}" "/workspace/target/release/${output_name}.zsync"
sed -i "s|^URL:.*|URL: ${output_name}|" "/workspace/target/release/${output_name}.zsync"
sed -i "s|^Filename:.*|Filename: ${output_name}|" "/workspace/target/release/${output_name}.zsync"
echo "Moved and patched zsync to ${output_name}.zsync"
fi
}
cleanup() {
rm -rf AppDir
}
print_summary() {
local output_name="$1"
echo "=== Build Complete! Output: /workspace/target/release/${output_name} ==="
ls -lh "/workspace/target/release/${output_name}"
}
# ──────────────────────────────────────────────
# Main
# ──────────────────────────────────────────────
main() {
local variant="${1:-glibc-x86_64}"
echo "=== Building PicoForge AppImage for variant: ${variant} ==="
local src_dir
src_dir="$(detect_src_dir)"
cd "${src_dir}"
local version
version="$(get_version)"
echo "PicoForge Version: ${version}"
local arch_name
arch_name="$(get_arch_name)"
local output_name
output_name="$(compute_output_name "${version}" "${variant}" "${arch_name}")"
local zsync_glob="${output_name/${version}/*}.zsync"
setup_update_info "${zsync_glob}"
build_release
local binary="target/release/picoforge"
local desktop="data/in.suyogtandel.picoforge.desktop"
local icon="static/appIcons/in.suyogtandel.picoforge.svg"
verify_assets "${binary}" "${desktop}" "${icon}"
package_appimage "${output_name}" "${binary}" "${desktop}" "${icon}"
move_artifacts "${output_name}"
cleanup
print_summary "${output_name}"
}
main "$@"
+56 -17
View File
@@ -1,26 +1,65 @@
#!/bin/bash
set -e
PLATFORM=$1
VERSION=${version:-"0.0.0"}
# ──────────────────────────────────────────────
# Utility Functions
# ──────────────────────────────────────────────
if [[ "$PLATFORM" == "ubuntu-24.04-arm" ]]; then
ARCH="arm64"
FLATPAK_ARCH="aarch64"
else
ARCH="x86-64"
FLATPAK_ARCH="x86_64"
fi
parse_platform() {
local platform="$1"
case "${platform}" in
ubuntu-24.04-arm)
echo "arm64 aarch64"
;;
*)
echo "x86-64 x86_64"
;;
esac
}
echo "Building Flatpak for architecture: $FLATPAK_ARCH (Release: $VERSION)"
# ──────────────────────────────────────────────
# Build Steps
# ──────────────────────────────────────────────
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
setup_runtime() {
local flatpak_arch="$1"
flatpak install --user -y flathub \
org.freedesktop.Platform//25.08 \
org.freedesktop.Sdk//25.08 \
org.freedesktop.Sdk.Extension.rust-stable//25.08
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub \
org.freedesktop.Platform//25.08 \
org.freedesktop.Sdk//25.08 \
org.freedesktop.Sdk.Extension.rust-stable//25.08
}
flatpak-builder --user --arch="${FLATPAK_ARCH}" --force-clean --repo=repo build-dir .github/manifests/in.suyogtandel.picoforge.json
build_flatpak() {
local flatpak_arch="$1"
flatpak-builder --user --arch="${flatpak_arch}" --force-clean --repo=repo build-dir .github/manifests/in.suyogtandel.picoforge.json
}
flatpak build-bundle repo "picoforge_${VERSION}_${ARCH}.flatpak" in.suyogtandel.picoforge
bundle_flatpak() {
local version="$1"
local arch="$2"
flatpak build-bundle repo "picoforge_${version}_${arch}.flatpak" in.suyogtandel.picoforge
}
# ──────────────────────────────────────────────
# Main
# ──────────────────────────────────────────────
main() {
local platform="${1}"
local ver
ver="${version:-0.0.0}"
local arch_flatpak_arch
arch_flatpak_arch="$(parse_platform "${platform}")"
read -r arch flatpak_arch <<< "${arch_flatpak_arch}"
echo "Building Flatpak for architecture: ${flatpak_arch} (Release: ${ver})"
setup_runtime "${flatpak_arch}"
build_flatpak "${flatpak_arch}"
bundle_flatpak "${ver}" "${arch}"
}
main "$@"
+3
View File
@@ -152,6 +152,8 @@ jobs:
mkdir -p target/release
echo "Running build inside container..."
podman run --rm \
-e GITHUB_REPOSITORY_OWNER \
-e GITHUB_REPOSITORY \
-v "${{ github.workspace }}":/workspace:z \
"${IMAGE_NAME}" \
/workspace/.github/scripts/build_appimage.sh ${{ matrix.appimage_variant }}
@@ -180,6 +182,7 @@ jobs:
target/release/*.deb
target/release/*.appimage
target/release/*.AppImage
target/release/*.zsync
target/release/*.tar.gz
target/release/*.msi
target/release/*.dmg