diff --git a/.gitignore b/.gitignore index d9243f1d09..4b10a3065c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ libraries/source/spirv-reflect/* !libraries/source/**/patches/ # Tools +appimage-build/ archives/ build/bin/ source/tools/spirv/rules.*.json @@ -68,6 +69,7 @@ binaries/data/mods/public/gui/credits/texts/translators.json *.pot # Packaged mods and tarballs +*.AppImage *.pyromod *.zip *.tar diff --git a/build/jenkins/dockerfiles/debian-12-appimage.Dockerfile b/build/jenkins/dockerfiles/debian-12-appimage.Dockerfile new file mode 100644 index 0000000000..201adc2d7c --- /dev/null +++ b/build/jenkins/dockerfiles/debian-12-appimage.Dockerfile @@ -0,0 +1,40 @@ +FROM debian-12 + +# minisign only needed for signing in pipeline +RUN apt-get -qqy update \ + && apt-get upgrade -qqy \ + && apt-get install -qqy --no-install-recommends \ + cimg-dev \ + desktop-file-utils \ + file \ + git \ + libgcrypt20-dev \ + libgpgme-dev \ + libjpeg-dev \ + minisign \ + patchelf \ + squashfs-tools \ + wget \ + && apt-get clean + +RUN \ + git clone --depth 1 --branch 1-alpha-20250213-2 https://github.com/linuxdeploy/linuxdeploy --recurse-submodules && \ + cd linuxdeploy && cp src/core/copyright/copyright.h src/core && \ + cmake -B build -S . \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DBUILD_TESTING=OFF \ + -DUSE_CCACHE=OFF \ + && cmake --build build --target install && \ + cd .. && \ + rm -rf linuxdeploy + +RUN \ + git clone --depth 1 --branch continuous https://github.com/AppImage/appimagetool.git && \ + cd appimagetool && \ + cmake -B build -S . \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + && cmake --build build --target install && \ + cd .. && \ + rm -rf appimagetool diff --git a/build/jenkins/pipelines/bundles.Jenkinsfile b/build/jenkins/pipelines/bundles.Jenkinsfile index fb4eb91c69..357ac2b108 100644 --- a/build/jenkins/pipelines/bundles.Jenkinsfile +++ b/build/jenkins/pipelines/bundles.Jenkinsfile @@ -140,24 +140,67 @@ pipeline { stage('Create Windows Installer & Tarballs') { steps { sh "BUNDLE_VERSION=${params.BUNDLE_VERSION} DO_GZIP=${params.DO_GZIP} source/tools/dist/build-unix-win32.sh" + stash(name: 'unix-sources', includes: '*.tar.gz') + } + } + + stage('Create AppImage') { + stages { + stage('Docker Setup') { + agent { + node { + label 'LinuxAgent' + customWorkspace 'workspace/appimage' + } + } + steps { + checkout scm + sh 'git clean -dxf' + sh 'docker build -t debian-12 -f build/jenkins/dockerfiles/debian-12.Dockerfile .' + } + } + + stage('Build AppImage') { + agent { + dockerfile { + label 'LinuxAgent' + customWorkspace 'workspace/appimage' + dir 'build/jenkins/dockerfiles' + filename 'debian-12-appimage.Dockerfile' + // Prevent Jenkins from running commands with the UID of the host's jenkins user + // https://stackoverflow.com/a/42822143 + args '-u root' + } + } + + steps { + unstash('unix-sources') + untar(dir: 'appimage-build', file: "0ad-${params.BUNDLE_VERSION}-unix-build.tar.gz", keepPermissions: true) + untar(dir: 'appimage-build', file: "0ad-${params.BUNDLE_VERSION}-unix-data.tar.gz", keepPermissions: true) + + sh "source/tools/dist/build-appimage.sh --version ${params.BUNDLE_VERSION} --root appimage-build/0ad-${params.BUNDLE_VERSION}" + stash(name: 'appimage', includes: '*AppImage') + } + } } } stage('Generate Signatures and Checksums') { steps { + unstash('appimage') withCredentials([sshUserPrivateKey(credentialsId: 'minisign-releases-key', keyFileVariable: 'MINISIGN_KEY', passphraseVariable: 'MINISIGN_PASS')]) { - sh 'echo ${MINISIGN_PASS} | minisign -s ${MINISIGN_KEY} -Sm *.{dmg,exe,tar.gz,tar.xz}' + sh 'echo ${MINISIGN_PASS} | minisign -s ${MINISIGN_KEY} -Sm *.{AppImage,dmg,exe,tar.gz,tar.xz}' } - sh 'for file in *.{dmg,exe,tar.gz,tar.xz}; do md5sum "${file}" > "${file}".md5sum; done' - sh 'for file in *.{dmg,exe,tar.gz,tar.xz}; do sha1sum "${file}" > "${file}".sha1sum; done' - sh 'for file in *.{dmg,exe,tar.gz,tar.xz}; do sha256sum "${file}" > "${file}".sha256sum; done' + sh 'for file in *.{AppImage,dmg,exe,tar.gz,tar.xz}; do md5sum "${file}" > "${file}".md5sum; done' + sh 'for file in *.{AppImage,dmg,exe,tar.gz,tar.xz}; do sha1sum "${file}" > "${file}".sha1sum; done' + sh 'for file in *.{AppImage,dmg,exe,tar.gz,tar.xz}; do sha256sum "${file}" > "${file}".sha256sum; done' } } } post { success { - archiveArtifacts '*.dmg,*.exe,*.tar.gz,*.tar.xz,*.minisig,*.md5sum,*.sha1sum,*.sha256sum' + archiveArtifacts '*AppImage,*.dmg,*.exe,*.tar.gz,*.tar.xz,*.minisig,*.md5sum,*.sha1sum,*.sha256sum' } cleanup { sh 'svn revert -R .' diff --git a/source/tools/LICENSE.txt b/source/tools/LICENSE.txt index 6aa382dd4b..fe663aa199 100644 --- a/source/tools/LICENSE.txt +++ b/source/tools/LICENSE.txt @@ -15,6 +15,7 @@ in particular, let us know and we can try to clarify it. MIT dist + GPL-2.0-or-later MIT zlib/libpng (FileAssociation.nsh) diff --git a/source/tools/dist/build-appimage-podman.sh b/source/tools/dist/build-appimage-podman.sh new file mode 100755 index 0000000000..30ea567726 --- /dev/null +++ b/source/tools/dist/build-appimage-podman.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +podman build -t 0ad-debian-12 -f build/jenkins/dockerfiles/debian-12.Dockerfile +podman build -t 0ad-debian-12-appimage --build-context debian-12=container-image://0ad-debian-12 -f build/jenkins/dockerfiles/debian-12-appimage.Dockerfile + +podman run --mount=type=bind,source=.,destination=/mnt --workdir /mnt 0ad-debian-12-appimage source/tools/dist/build-appimage.sh "$@" diff --git a/source/tools/dist/build-appimage.sh b/source/tools/dist/build-appimage.sh new file mode 100755 index 0000000000..b886a33527 --- /dev/null +++ b/source/tools/dist/build-appimage.sh @@ -0,0 +1,99 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +# shellcheck disable=SC2120 +die() +{ + [ $# -ne 0 ] && echo ERROR: "$*" + exit 1 +} + +doins() +{ + mkdir -p "$2" + cp -a "$1" "$2" || die +} + +JOBS=${JOBS#-j} +njobs=${JOBS:-1} +arch=$(uname -m) +version=9999 +while [ "$#" -gt 0 ]; do + case "$1" in + --jobs) + njobs=$2 + shift + ;; + --root) + root=$2 + shift + ;; + --skip-prepacking) + skip_prepacking="yes" + ;; + --version) + version=$2 + shift + ;; + *) + printf "Unknown option: %s\n\n" "$1" + exit 1 + ;; + esac + shift +done + +TOP_SRC_DIR=$(realpath .) +APPDIR="${TOP_SRC_DIR}/appimage-build/AppDir" +APPIMAGE="${TOP_SRC_DIR}/0ad-${version}-${arch}.AppImage" +ROOT="${TOP_SRC_DIR}" +if [ -n "${root}" ]; then + ROOT=$(realpath "${root}") +fi + +cd "${ROOT}" || die + +./libraries/build-source-libs.sh "-j${njobs}" || die +./build/workspaces/update-workspaces.sh || die +make -C build/workspaces/gcc "-j${njobs}" || die +if [ -z "${root}" ] && [ -z "${skip_prepacking}" ]; then + ./source/tools/i18n/get-nightly-translations.sh || die + ./source/tools/spirv/get-nightly-shaders.sh || die + # TODO: Fails at check for root user currently when generating mods archives + #./source/tools/dist/build-archives.sh +fi + +rm -rf "${APPDIR}" "${APPIMAGE}" + +doins binaries/system/pyrogenesis "${APPDIR}/usr/bin" +ln -rs "${APPDIR}/usr/bin/pyrogenesis" "${APPDIR}/usr/bin/0ad" || die +for lib in \ + libmozjs*-release.so \ + libnvcore.so \ + libnvimage.so \ + libnvmath.so \ + libnvtt.so; do + patchelf --set-rpath "${lib}:${ROOT}/binaries/system" "${APPDIR}/usr/bin/pyrogenesis" || die +done + +# dlopen lib +doins binaries/system/libCollada.so "${APPDIR}/usr/lib" + +doins binaries/data/config/default.cfg "${APPDIR}/usr/data/config" +doins binaries/data/l10n "${APPDIR}/usr/data" +doins binaries/data/tools "${APPDIR}/usr/data" + +doins binaries/data/mods/mod "${APPDIR}/usr/data/mods" +doins binaries/data/mods/public "${APPDIR}/usr/data/mods" + +doins build/resources/0ad.appdata.xml "${APPDIR}/usr/share/metainfo" +doins build/resources/0ad.png "${APPDIR}/usr/share/pixmaps" + +linuxdeploy \ + --appdir "${APPDIR}" \ + --executable "${APPDIR}/usr/bin/pyrogenesis" \ + --desktop-file "${ROOT}/build/resources/0ad.desktop" \ + --icon-file "${ROOT}/build/resources/0ad.png" \ + --icon-filename 0ad || die + +appimagetool --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 20 "${APPDIR}" "${APPIMAGE}" || die