From 81cbc3fa15e98daf50769bd343f75b8edbd71f08 Mon Sep 17 00:00:00 2001 From: Brendan Szymanski Date: Wed, 7 Nov 2018 21:33:36 -0500 Subject: [PATCH] Flatpak support (#4383) * Initial flatpak support * Fix compatibility list directory * Hard-code SSH mount location * Add workaround documentation * Change SSH repo directory * Change SSH repo directory (again) * Fix variable name * Remove temporary testing branch placeholder * Use a flatpak-specific docker image * Enable network access during the flatpak build so we can download compatibility list the right way * Fix flatpak tag support * Fix typo * Use cloned git for the build * Change SSH repo location * Disable shallow git cloning, needed for tagged building --- .gitignore | 4 + .travis.yml | 12 ++ .travis/linux-flatpak/build.sh | 4 + .travis/linux-flatpak/deps.sh | 4 + .travis/linux-flatpak/docker.sh | 35 +++++ .travis/linux-flatpak/finish.sh | 9 ++ .travis/linux-flatpak/generate-data.sh | 142 ++++++++++++++++++++ .travis/linux-flatpak/travis-ci-flatpak.env | 9 ++ keys.tar.enc | Bin 0 -> 10256 bytes 9 files changed, 219 insertions(+) create mode 100755 .travis/linux-flatpak/build.sh create mode 100755 .travis/linux-flatpak/deps.sh create mode 100755 .travis/linux-flatpak/docker.sh create mode 100755 .travis/linux-flatpak/finish.sh create mode 100644 .travis/linux-flatpak/generate-data.sh create mode 100644 .travis/linux-flatpak/travis-ci-flatpak.env create mode 100644 keys.tar.enc diff --git a/.gitignore b/.gitignore index 9e11b5d75..37591363a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,7 @@ Thumbs.db # Python files *.pyc + +# Flatpak generated files +.flatpak-builder/ +repo/ diff --git a/.travis.yml b/.travis.yml index 54b7a9091..402c9538e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,6 +61,18 @@ matrix: script: "./.travis/linux-mingw/build.sh" after_success: "./.travis/linux-mingw/upload.sh" cache: ccache + - if: repo =~ ^.*\/(citra-canary|citra-nightly)$ AND tag IS present + git: + depth: false + os: linux + env: NAME="flatpak build" + sudo: required + dist: trusty + services: docker + cache: ccache + install: "./.travis/linux-flatpak/deps.sh" + script: "./.travis/linux-flatpak/build.sh" + after_script: "./.travis/linux-flatpak/finish.sh" deploy: provider: releases diff --git a/.travis/linux-flatpak/build.sh b/.travis/linux-flatpak/build.sh new file mode 100755 index 000000000..91fba01aa --- /dev/null +++ b/.travis/linux-flatpak/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash -ex +mkdir -p "$HOME/.ccache" +# Configure docker and call the script that generates application data and build scripts +docker run --env-file .travis/common/travis-ci.env --env-file .travis/linux-flatpak/travis-ci-flatpak.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache -v "$HOME/.ssh":/root/.ssh --privileged citraemu/build-environments:linux-flatpak /bin/bash -ex /citra/.travis/linux-flatpak/generate-data.sh diff --git a/.travis/linux-flatpak/deps.sh b/.travis/linux-flatpak/deps.sh new file mode 100755 index 000000000..b8fd4974c --- /dev/null +++ b/.travis/linux-flatpak/deps.sh @@ -0,0 +1,4 @@ +#!/bin/sh -ex + +# Download the docker image that contains flatpak build dependencies +docker pull citraemu/build-environments:linux-flatpak diff --git a/.travis/linux-flatpak/docker.sh b/.travis/linux-flatpak/docker.sh new file mode 100755 index 000000000..31e2bb777 --- /dev/null +++ b/.travis/linux-flatpak/docker.sh @@ -0,0 +1,35 @@ +#!/bin/bash -ex + +# Converts "citra-emu/citra-nightly" to "citra-nightly" +REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d'/' -f 2) +CITRA_SRC_DIR="/citra" +BUILD_DIR="$CITRA_SRC_DIR/build" +REPO_DIR="$CITRA_SRC_DIR/repo" +STATE_DIR="$CITRA_SRC_DIR/.flatpak-builder" +KEYS_ARCHIVE="/tmp/keys.tar" +SSH_DIR="/upload" +SSH_KEY="/tmp/ssh.key" +GPG_KEY="/tmp/gpg.key" + +# Extract keys +openssl aes-256-cbc -K $FLATPAK_ENC_K -iv $FLATPAK_ENC_IV -in "$CITRA_SRC_DIR/keys.tar.enc" -out "$KEYS_ARCHIVE" -d +tar -C /tmp -xvf $KEYS_ARCHIVE + +# Configure SSH keys +eval "$(ssh-agent -s)" +chmod -R 600 "$HOME/.ssh" +chown -R root "$HOME/.ssh" +chmod 600 "$SSH_KEY" +ssh-add "$SSH_KEY" +echo "[$FLATPAK_SSH_HOSTNAME]:$FLATPAK_SSH_PORT,[$(dig +short $FLATPAK_SSH_HOSTNAME)]:$FLATPAK_SSH_PORT $FLATPAK_SSH_PUBLIC_KEY" > ~/.ssh/known_hosts + +# Configure GPG keys +gpg2 --import "$GPG_KEY" + +# Mount our flatpak repository +mkdir -p "$REPO_DIR" +sshfs "$FLATPAK_SSH_USER@$FLATPAK_SSH_HOSTNAME:$SSH_DIR" "$REPO_DIR" -C -p "$FLATPAK_SSH_PORT" -o IdentityFile="$SSH_KEY" + +# Build the citra flatpak +flatpak-builder -v --jobs=4 --ccache --force-clean --state-dir="$STATE_DIR" --gpg-sign="$FLATPAK_GPG_PUBLIC_KEY" --repo="$REPO_DIR" "$BUILD_DIR" "/tmp/org.citra.$REPO_NAME.json" +flatpak build-update-repo "$REPO_DIR" -v --generate-static-deltas --gpg-sign="$FLATPAK_GPG_PUBLIC_KEY" diff --git a/.travis/linux-flatpak/finish.sh b/.travis/linux-flatpak/finish.sh new file mode 100755 index 000000000..c9f0c0731 --- /dev/null +++ b/.travis/linux-flatpak/finish.sh @@ -0,0 +1,9 @@ +#!/bin/bash -ex + +CITRA_SRC_DIR="/citra" +REPO_DIR="$CITRA_SRC_DIR/repo" + +# When the script finishes, unmount the repository and delete sensitive files, +# regardless of whether the build passes or fails +umount "$REPO_DIR" +rm -rf "$REPO_DIR" "/tmp/*" diff --git a/.travis/linux-flatpak/generate-data.sh b/.travis/linux-flatpak/generate-data.sh new file mode 100644 index 000000000..d8ca90aab --- /dev/null +++ b/.travis/linux-flatpak/generate-data.sh @@ -0,0 +1,142 @@ +#!/bin/bash -ex +# This script generates the appdata.xml and org.citra.$REPO_NAME.json files +# needed to define application metadata and build citra depending on what version +# of citra we're building (nightly or canary) + +# Converts "citra-emu/citra-nightly" to "citra-nightly" +REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d'/' -f 2) +# Converts "citra-nightly" to "Citra Nightly" +REPO_NAME_FRIENDLY=$(echo $REPO_NAME | sed -e 's/-/ /g' -e 's/\b\(.\)/\u\1/g') + +# Generate the correct appdata.xml for the version of Citra we're building +cat > /tmp/appdata.xml < + + org.citra.$REPO_NAME.desktop + $REPO_NAME_FRIENDLY + Nintendo 3DS emulator + CC0-1.0 + GPL-2.0 + +

Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS.

+

Citra emulates a subset of 3DS hardware and therefore is useful for running/debugging homebrew applications, and it is also able to run many commercial games! Some of these do not run at a playable state, but we are working every day to advance the project forward. (Playable here means compatibility of at least "Okay" on our game compatibility list.)

+
+ https://citra-emu.org/ + https://citra-emu.org/donate/ + https://github.com/citra-emu/citra/issues + https://citra-emu.org/wiki/faq/ + https://citra-emu.org/wiki/home/ + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/01-Super%20Mario%203D%20Land.jpg + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/02-Mario%20Kart%207.jpg + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/28-The%20Legend%20of%20Zelda%20Ocarina%20of%20Time%203D.jpg + https://raw.githubusercontent.com/citra-emu/citra-web/master/site/static/images/screenshots/35-Pok%C3%A9mon%20ORAS.png + + Games + Emulator + +
+EOF + +# Generate the citra flatpak manifest, appending certain variables depending on +# whether we're building nightly or canary. +cat > /tmp/org.citra.$REPO_NAME.json <> /app/share/applications/citra.desktop", + "install -Dm644 ../dist/citra.svg /app/share/icons/hicolor/scalable/apps/citra.svg", + "install -Dm644 ../dist/icon.png /app/share/icons/hicolor/512x512/apps/citra.png", + "mv /app/share/mime/packages/citra.xml /app/share/mime/packages/org.citra.$REPO_NAME.xml", + "sed 's/citra/org.citra.citra-nightly/g' -i /app/share/mime/packages/org.citra.$REPO_NAME.xml", + "install -m644 --target-directory=/app/lib /usr/lib/sdk/gcc7/lib/libstdc++.so*" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/citra-emu/$REPO_NAME.git", + "branch": "$TRAVIS_BRANCH", + "disable-shallow-clone": true + }, + { + "type": "file", + "path": "/tmp/appdata.xml" + } + ] + } + ] +} +EOF + +# Call the script to build citra +/bin/bash -ex /citra/.travis/linux-flatpak/docker.sh diff --git a/.travis/linux-flatpak/travis-ci-flatpak.env b/.travis/linux-flatpak/travis-ci-flatpak.env new file mode 100644 index 000000000..0823d91c8 --- /dev/null +++ b/.travis/linux-flatpak/travis-ci-flatpak.env @@ -0,0 +1,9 @@ +# Flatpak specific environment variables +FLATPAK_ENC_IV +FLATPAK_ENC_K +FLATPAK_GPG_PUBLIC_KEY +FLATPAK_SSH_HOSTNAME +FLATPAK_SSH_LOCATION +FLATPAK_SSH_PORT +FLATPAK_SSH_PUBLIC_KEY +FLATPAK_SSH_USER diff --git a/keys.tar.enc b/keys.tar.enc new file mode 100644 index 0000000000000000000000000000000000000000..6a8c1484e5720cc05baa3892d97c41e940587b09 GIT binary patch literal 10256 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 J!|%}~8vp|w2&@1A literal 0 HcmV?d00001