diff --git a/.ci/citron-canary-step1.yml b/.ci/citron-canary-step1.yml new file mode 100755 index 000000000..6106bc2d2 --- /dev/null +++ b/.ci/citron-canary-step1.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +trigger: +- master + +pool: + vmImage: 'ubuntu-latest' + +stages: +- stage: merge + displayName: 'merge' + jobs: + - template: ./templates/merge-private.yml diff --git a/.ci/citron-canary-step2.yml b/.ci/citron-canary-step2.yml new file mode 100755 index 000000000..8fb53a288 --- /dev/null +++ b/.ci/citron-canary-step2.yml @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +trigger: +- master + +variables: + DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] + +stages: +- stage: build + displayName: 'build' + jobs: + - job: linux + timeoutInMinutes: 120 + displayName: 'linux' + pool: + vmImage: ubuntu-latest + strategy: + maxParallel: 10 + matrix: + linux: + BuildSuffix: 'linux' + ScriptFolder: 'linux' + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-single.yml + parameters: + artifactSource: 'false' + cache: $(parameters.cache) + version: $(DisplayVersion) + - job: msvc + timeoutInMinutes: 120 + displayName: 'windows' + pool: + vmImage: windows-2022 + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-msvc.yml + parameters: + artifactSource: 'false' + cache: $(parameters.cache) + version: $(DisplayVersion) +- stage: release + displayName: 'release' + dependsOn: build + jobs: + - job: release + displayName: 'source' + pool: + vmImage: 'ubuntu-latest' + steps: + - template: ./templates/release-private-tag.yml diff --git a/.ci/citron-pipeline.yml b/.ci/citron-pipeline.yml new file mode 100644 index 000000000..7a100fb81 --- /dev/null +++ b/.ci/citron-pipeline.yml @@ -0,0 +1,106 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +trigger: +- master + +variables: + DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] + BuildName: 'stable' + +stages: +- stage: build + displayName: 'build' + jobs: + - job: build + timeoutInMinutes: 120 + displayName: 'standard' + pool: + vmImage: ubuntu-latest + strategy: + maxParallel: 10 + matrix: + linux: + BuildSuffix: 'linux' + ScriptFolder: 'linux' + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-single.yml + parameters: + artifactSource: 'false' + cache: 'true' + version: $(DisplayVersion) + # Make tokens available to scripts + - task: Bash@3 + inputs: + targetType: 'inline' + script: | + echo "##vso[task.setvariable variable=EARLY_ACCESS_TOKEN]$(EARLY_ACCESS_TOKEN)" + echo "##vso[task.setvariable variable=CI_RELEASE_TOKEN]$(CI_RELEASE_TOKEN)" + +- stage: build_win + displayName: 'build-windows' + jobs: + - job: build + timeoutInMinutes: 120 + displayName: 'msvc' + pool: + vmImage: windows-2022 + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-msvc.yml + parameters: + artifactSource: 'false' + cache: 'true' + version: $(DisplayVersion) + # Make tokens available to scripts + - task: PowerShell@2 + inputs: + targetType: 'inline' + script: | + Write-Host "##vso[task.setvariable variable=EARLY_ACCESS_TOKEN]$(EARLY_ACCESS_TOKEN)" + Write-Host "##vso[task.setvariable variable=CI_RELEASE_TOKEN]$(CI_RELEASE_TOKEN)" + +- stage: build_android + displayName: 'build-android' + jobs: + - job: build + timeoutInMinutes: 120 + displayName: 'android' + pool: + vmImage: ubuntu-latest + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - task: Bash@3 + inputs: + targetType: 'inline' + script: | + export USE_DEBUG_KEYSTORE=true + chmod +x ./.ci/scripts/android/build.sh + ./.ci/scripts/android/build.sh + - publish: artifacts + artifact: 'citron-$(BuildName)-android' + displayName: 'Upload Android Artifacts' + +- stage: release + displayName: 'release' + dependsOn: + - build + - build_win + - build_android + jobs: + - job: github + displayName: 'github' + pool: + vmImage: ubuntu-latest + steps: + - template: ./templates/release-github.yml \ No newline at end of file diff --git a/.ci/citron-repo-sync.yml b/.ci/citron-repo-sync.yml new file mode 100755 index 000000000..d0b8a4e20 --- /dev/null +++ b/.ci/citron-repo-sync.yml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +trigger: +- master + +jobs: +- job: copy + displayName: 'Sync Repository' + pool: + vmImage: 'ubuntu-latest' + steps: + - script: echo 'https://$(GitUsername):$(GitAccessToken)@dev.azure.com' > $HOME/.git-credentials + displayName: 'Load Credentials' + - script: git config --global credential.helper store + displayName: 'Register Credential Helper' + - script: git remote add other $(GitRepoPushChangesURL) + displayName: 'Register Repository' + - script: git push --force other HEAD:$(GitPushBranch) + displayName: 'Update Code' + - script: rm -rf $HOME/.git-credentials + displayName: 'Clear Cached Credentials' diff --git a/.ci/citron-stable-step1.yml b/.ci/citron-stable-step1.yml new file mode 100755 index 000000000..e94fb6ad3 --- /dev/null +++ b/.ci/citron-stable-step1.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +trigger: +- master + +pool: + vmImage: 'ubuntu-latest' + +stages: +- stage: merge + displayName: 'merge' + jobs: + - template: ./templates/merge.yml diff --git a/.ci/citron-stable-step2.yml b/.ci/citron-stable-step2.yml new file mode 100755 index 000000000..7bcbb6afe --- /dev/null +++ b/.ci/citron-stable-step2.yml @@ -0,0 +1,64 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +trigger: +- master + +variables: + DisplayVersion: $[counter(variables['DisplayPrefix'], 1)] + +stages: +- stage: build + displayName: 'build' + jobs: + - job: build + timeoutInMinutes: 120 + displayName: 'standard' + pool: + vmImage: ubuntu-latest + strategy: + maxParallel: 10 + matrix: + linux: + BuildSuffix: 'linux' + ScriptFolder: 'linux' + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-single.yml + parameters: + artifactSource: 'false' + cache: 'true' + version: $(DisplayVersion) +- stage: build_win + displayName: 'build-windows' + jobs: + - job: build + timeoutInMinutes: 120 + displayName: 'msvc' + pool: + vmImage: windows-2022 + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-msvc.yml + parameters: + artifactSource: 'false' + cache: 'true' + version: $(DisplayVersion) +- stage: release + displayName: 'release' + dependsOn: + - build + - build_win + jobs: + - job: github + displayName: 'github' + pool: + vmImage: ubuntu-latest + steps: + - template: ./templates/release-github.yml diff --git a/.ci/citron-verify.yml b/.ci/citron-verify.yml new file mode 100755 index 000000000..3cb038d50 --- /dev/null +++ b/.ci/citron-verify.yml @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +stages: +- stage: format + displayName: 'format' + jobs: + - job: format + displayName: 'clang' + pool: + vmImage: ubuntu-latest + steps: + - template: ./templates/format-check.yml + parameters: + artifactSource: 'false' +- stage: build + displayName: 'build' + dependsOn: format + jobs: + - template: ./templates/build-standard.yml + parameters: + cache: 'false' + - template: ./templates/build-testing.yml diff --git a/.ci/scripts/android/build.sh b/.ci/scripts/android/build.sh new file mode 100755 index 000000000..48d6d28bb --- /dev/null +++ b/.ci/scripts/android/build.sh @@ -0,0 +1,28 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +export NDK_CCACHE="$(which ccache)" +ccache -s + +BUILD_FLAVOR="stable" + +mkdir -p "artifacts" + +BUILD_TYPE="release" +if [ "${GITHUB_REPOSITORY}" == "Citron-Project/Cit" ]; then + BUILD_TYPE="relWithDebInfo" +fi + +# We'll use the Android debug keystore for builds +# This removes the requirement for storing a keystore in secrets +# For production builds, this should be replaced with a proper signing configuration +export USE_DEBUG_KEYSTORE=true + +cd src/android +chmod +x ./gradlew +./gradlew clean +./gradlew app:assemble${BUILD_FLAVOR}${BUILD_TYPE} + +ccache -s diff --git a/.ci/scripts/android/canarybuild.sh b/.ci/scripts/android/canarybuild.sh new file mode 100755 index 000000000..269965fee --- /dev/null +++ b/.ci/scripts/android/canarybuild.sh @@ -0,0 +1,21 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2024 Citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +export NDK_CCACHE="$(which ccache)" +ccache -s + +export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" +export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/service-account-key.json" + +base64 --decode <<< "${CANARY_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}" +chmod 600 "${ANDROID_KEYSTORE_FILE}" + +mkdir -p "$(dirname "${SERVICE_ACCOUNT_KEY_PATH}")" +base64 --decode <<< "${CANARY_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}" +./gradlew "publishCanaryReleaseBundle" + +ccache -s + +rm "${ANDROID_KEYSTORE_FILE}" "${SERVICE_ACCOUNT_KEY_PATH}" \ No newline at end of file diff --git a/.ci/scripts/android/eabuild.sh b/.ci/scripts/android/eabuild.sh new file mode 100755 index 000000000..ab908084c --- /dev/null +++ b/.ci/scripts/android/eabuild.sh @@ -0,0 +1,21 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2024 citron Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +export NDK_CCACHE="$(which ccache)" +ccache -s + +export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" +base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}" +export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}" +export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}" +export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json" +base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}" +./gradlew "publishEaReleaseBundle" + +ccache -s + +if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then + rm "${ANDROID_KEYSTORE_FILE}" +fi diff --git a/.ci/scripts/android/stablebuild.sh b/.ci/scripts/android/stablebuild.sh new file mode 100755 index 000000000..ed48420ba --- /dev/null +++ b/.ci/scripts/android/stablebuild.sh @@ -0,0 +1,21 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +export NDK_CCACHE="$(which ccache)" +ccache -s + +export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks" +export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/service-account-key.json" + +base64 --decode <<< "${STABLE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}" +chmod 600 "${ANDROID_KEYSTORE_FILE}" + +mkdir -p "$(dirname "${SERVICE_ACCOUNT_KEY_PATH}")" +base64 --decode <<< "${STABLE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}" +./gradlew "publishStableReleaseBundle" + +ccache -s + +rm "${ANDROID_KEYSTORE_FILE}" "${SERVICE_ACCOUNT_KEY_PATH}" diff --git a/.ci/scripts/android/upload.sh b/.ci/scripts/android/upload.sh new file mode 100755 index 000000000..dc8d4a70c --- /dev/null +++ b/.ci/scripts/android/upload.sh @@ -0,0 +1,41 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +. .ci/scripts/common/pre-upload.sh + +APPNAME="org.citron.citron" + +REV_NAME="${APPNAME}-${GITDATE}-${GITREV}" + +APK="${REV_NAME}.apk" + +if [ "${GITHUB_REPOSITORY}" == "Citron-Project/Cit" ]; then + cd src/android + ./gradlew app:bundleRelease + # RELEASE_BODY gets reset here, but its fine since we're uploading after the GitHub Release + . .ci/scripts/common/post-upload.sh + + sudo apt-get install -y jq + + ls app/build/outputs/bundle/release/ + + echo "Downloading CI scripts bundle" + ARTIFACTS_DIR="/tmp/artifact-pool" + mkdir -p "$ARTIFACTS_DIR" +fi + +BUILD_FLAVOR="stable" + +BUILD_TYPE_LOWER="release" +BUILD_TYPE_UPPER="Release" +if [ "${GITHUB_REPOSITORY}" == "Citron-Project/Cit" ]; then + BUILD_TYPE_LOWER="relWithDebInfo" + BUILD_TYPE_UPPER="RelWithDebInfo" +fi + +cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/${BUILD_TYPE_LOWER}/app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.apk" \ + "artifacts/${REV_NAME}.apk" +cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}${BUILD_TYPE_UPPER}"/"app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.aab" \ + "artifacts/${REV_NAME}.aab" diff --git a/.ci/scripts/clang/docker.sh b/.ci/scripts/clang/docker.sh new file mode 100755 index 000000000..470ace51b --- /dev/null +++ b/.ci/scripts/clang/docker.sh @@ -0,0 +1,32 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2021 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Exit on error, rather than continuing with the rest of the script. +set -e + +ccache -s + +mkdir build || true && cd build +cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ + -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ \ + -DCMAKE_C_COMPILER=/usr/lib/ccache/clang \ + -DCMAKE_INSTALL_PREFIX="/usr" \ + -DDISPLAY_VERSION=$1 \ + -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ + -DENABLE_QT_TRANSLATION=ON \ + -DUSE_DISCORD_PRESENCE=ON \ + -DYUZU_CRASH_DUMPS=ON \ + -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \ + -DYUZU_USE_BUNDLED_FFMPEG=ON \ + -GNinja + +ninja + +ccache -s + +ctest -VV -C Release + diff --git a/.ci/scripts/clang/exec.sh b/.ci/scripts/clang/exec.sh new file mode 100755 index 000000000..779bec97c --- /dev/null +++ b/.ci/scripts/clang/exec.sh @@ -0,0 +1,11 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2021 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +mkdir -p "ccache" || true +chmod a+x ./.ci/scripts/clang/docker.sh +# the UID for the container citron user is 1027 +sudo chown -R 1027 ./ +docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/citron/ccache -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-fresh /bin/bash /citron/.ci/scripts/clang/docker.sh "$1" +sudo chown -R $UID ./ diff --git a/.ci/scripts/clang/upload.sh b/.ci/scripts/clang/upload.sh new file mode 100755 index 000000000..057f1dbcf --- /dev/null +++ b/.ci/scripts/clang/upload.sh @@ -0,0 +1,23 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +. .ci/scripts/common/pre-upload.sh + +REV_NAME="citron-linux-clang-${GITDATE}-${GITREV}" +ARCHIVE_NAME="${REV_NAME}.tar.xz" +COMPRESSION_FLAGS="-cJvf" + +if [ "${RELEASE_NAME}" = "stable" ]; then + DIR_NAME="${REV_NAME}" +else + DIR_NAME="${REV_NAME}_${RELEASE_NAME}" +fi + +mkdir -p $DIR_NAME + +cp build/bin/citron "$DIR_NAME" +cp build/bin/citron-cmd "$DIR_NAME" + +. .ci/scripts/common/post-upload.sh diff --git a/.ci/scripts/common/post-upload.sh b/.ci/scripts/common/post-upload.sh new file mode 100755 index 000000000..b5bbbe5aa --- /dev/null +++ b/.ci/scripts/common/post-upload.sh @@ -0,0 +1,20 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Copy documentation +cp LICENSE.txt "$DIR_NAME" +cp README.md "$DIR_NAME" + +if [[ -z "${NO_SOURCE_PACK}" ]]; then + git clone --depth 1 file://$(readlink -e .) ${REV_NAME}-source + tar -cJvf "${REV_NAME}-source.tar.xz" ${REV_NAME}-source + cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME" + cp -v "${REV_NAME}-source.tar.xz" "${ARTIFACTS_DIR}/" +fi + +tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME" + +# move the compiled archive into the artifacts directory to be uploaded by travis releases +mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/" diff --git a/.ci/scripts/common/pre-upload.sh b/.ci/scripts/common/pre-upload.sh new file mode 100755 index 000000000..5d0db90b9 --- /dev/null +++ b/.ci/scripts/common/pre-upload.sh @@ -0,0 +1,10 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" +GITREV="`git show -s --format='%h'`" +ARTIFACTS_DIR="$PWD/artifacts" + +mkdir -p "${ARTIFACTS_DIR}/" diff --git a/.ci/scripts/format/docker.sh b/.ci/scripts/format/docker.sh new file mode 100755 index 000000000..9547a1360 --- /dev/null +++ b/.ci/scripts/format/docker.sh @@ -0,0 +1,9 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Run clang-format +cd /citron +chmod a+x ./.ci/scripts/format/script.sh +./.ci/scripts/format/script.sh diff --git a/.ci/scripts/format/exec.sh b/.ci/scripts/format/exec.sh new file mode 100755 index 000000000..a64d08362 --- /dev/null +++ b/.ci/scripts/format/exec.sh @@ -0,0 +1,10 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +chmod a+x ./.ci/scripts/format/docker.sh +# the UID for the container citron user is 1027 +sudo chown -R 1027 ./ +docker run -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-clang-format /bin/bash -ex /citron/.ci/scripts/format/docker.sh +sudo chown -R $UID ./ diff --git a/.ci/scripts/format/script.sh b/.ci/scripts/format/script.sh new file mode 100755 index 000000000..690f927b2 --- /dev/null +++ b/.ci/scripts/format/script.sh @@ -0,0 +1,37 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +shopt -s nullglob globstar + +if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then + echo Trailing whitespace found, aborting + exit 1 +fi + +# Default clang-format points to default 3.5 version one +CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}" +"$CLANG_FORMAT" --version + +# Turn off tracing for this because it's too verbose +set +x + +# Check everything for branch pushes +FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')" + +for f in $FILES_TO_LINT; do + echo "$f" + "$CLANG_FORMAT" -i "$f" +done + +DIFF=$(git -c core.fileMode=false diff) + +if [ ! -z "$DIFF" ]; then + echo "!!! Not compliant to coding style, here is the fix:" + echo "$DIFF" + exit 1 +fi + +cd src/android +./gradlew ktlintCheck diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh new file mode 100755 index 000000000..815ea0517 --- /dev/null +++ b/.ci/scripts/linux/docker.sh @@ -0,0 +1,79 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Exit on error, rather than continuing with the rest of the script. +set -e + +ccache -s + +mkdir build || true && cd build +cmake .. \ + -DBoost_USE_STATIC_LIBS=ON \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ + -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ + -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ + -DCMAKE_INSTALL_PREFIX="/usr" \ + -DDISPLAY_VERSION=$1 \ + -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ + -DENABLE_QT_TRANSLATION=ON \ + -DUSE_DISCORD_PRESENCE=ON \ + -DCITRON_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \ + -DCITRON_USE_BUNDLED_FFMPEG=ON \ + -DCITRON_ENABLE_LTO=ON \ + -DCITRON_CRASH_DUMPS=ON \ + -GNinja + +ninja + +ccache -s + +ctest -VV -C Release + +# Separate debug symbols from specified executables +for EXE in citron; do + EXE_PATH="bin/$EXE" + # Copy debug symbols out + objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug + # Add debug link and strip debug symbols + objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out + # Overwrite original with stripped copy + mv $EXE_PATH.out $EXE_PATH +done +# Strip debug symbols from all executables +find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';' + +DESTDIR="$PWD/AppDir" ninja install +rm -vf AppDir/usr/bin/citron-cmd AppDir/usr/bin/citron-tester + +# Download tools needed to build an AppImage +wget -nc https://raw.githubusercontent.com/yuzu-emu-mirror/ext-linux-bin/main/appimage/deploy-linux.sh || wget -nc https://raw.githubusercontent.com/AppImage/AppImageKit/master/appimagetool/deploy-linux.sh +wget -nc https://raw.githubusercontent.com/yuzu-emu-mirror/AppImageKit-checkrt/old/AppRun.sh || wget -nc https://raw.githubusercontent.com/darealshinji/AppImageKit-checkrt/master/AppRun.sh +wget -nc https://github.com/yuzu-emu-mirror/ext-linux-bin/raw/main/appimage/exec-x86_64.so || wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so +# Set executable bit +chmod 755 \ + deploy-linux.sh \ + AppRun.sh \ + exec-x86_64.so + +# Workaround for https://github.com/AppImage/AppImageKit/issues/828 +export APPIMAGE_EXTRACT_AND_RUN=1 + +mkdir -p AppDir/usr/optional +mkdir -p AppDir/usr/optional/libstdc++ +mkdir -p AppDir/usr/optional/libgcc_s + +# Deploy citron's needed dependencies +DEPLOY_QT=1 ./deploy-linux.sh AppDir/usr/bin/citron AppDir + +# Workaround for libQt5MultimediaGstTools indirectly requiring libwayland-client and breaking Vulkan usage on end-user systems +find AppDir -type f -regex '.*libwayland-client\.so.*' -delete -print + +# Workaround for building citron with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al. +# See https://github.com/darealshinji/AppImageKit-checkrt +cp exec-x86_64.so AppDir/usr/optional/exec.so +cp AppRun.sh AppDir/AppRun +cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6 +cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1 diff --git a/.ci/scripts/linux/exec.sh b/.ci/scripts/linux/exec.sh new file mode 100755 index 000000000..3178ed7b0 --- /dev/null +++ b/.ci/scripts/linux/exec.sh @@ -0,0 +1,16 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +mkdir -p "ccache" || true +chmod a+x ./.ci/scripts/linux/docker.sh +# the UID for the container citron user is 1027 +sudo chown -R 1027 ./ + +# The environment variables listed below: +# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION +# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps + +docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/citron/ccache -v "$(pwd):/citron" -w /citron yuzu-emu-mirror/build-environments:linux-fresh /bin/bash /citron/.ci/scripts/linux/docker.sh "$1" +sudo chown -R $UID ./ diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh new file mode 100755 index 000000000..2a995605c --- /dev/null +++ b/.ci/scripts/linux/upload.sh @@ -0,0 +1,67 @@ +#!/bin/bash -ex + +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +. .ci/scripts/common/pre-upload.sh + +APPIMAGE_NAME="citron-${RELEASE_NAME}-${GITDATE}-${GITREV}.AppImage" +BASE_NAME="citron-linux" +REV_NAME="${BASE_NAME}-${GITDATE}-${GITREV}" +ARCHIVE_NAME="${REV_NAME}.tar.xz" +COMPRESSION_FLAGS="-cJvf" + +if [ "${RELEASE_NAME}" = "stable" ] || [ "${RELEASE_NAME}" = "canary" ]; then + DIR_NAME="${BASE_NAME}-${RELEASE_NAME}" +else + DIR_NAME="${REV_NAME}-${RELEASE_NAME}" +fi + +mkdir "$DIR_NAME" + +cp build/bin/citron-cmd "$DIR_NAME" +if [ "${RELEASE_NAME}" != "canary" ] && [ "${RELEASE_NAME}" != "stable" ]; then + cp build/bin/citron "$DIR_NAME" +fi + +# Build an AppImage +cd build + +wget -nc https://github.com/yuzu-emu-mirror/ext-linux-bin/raw/main/appimage/appimagetool-x86_64.AppImage || wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage +chmod 755 appimagetool-x86_64.AppImage + +# if FUSE is not available, then fallback to extract and run +if ! ./appimagetool-x86_64.AppImage --version; then + export APPIMAGE_EXTRACT_AND_RUN=1 +fi + +# Don't let AppImageLauncher ask to integrate EA +if [ "${RELEASE_NAME}" = "stable" ] || [ "${RELEASE_NAME}" = "canary" ]; then + echo "X-AppImage-Integrate=false" >> AppDir/org.citron_emu.citron.desktop +fi + +if [ "${RELEASE_NAME}" = "stable" ]; then + # Generate update information if releasing to stable + ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|Citron-Project|Cit|latest|citron-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}" +else + ./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}" +fi +cd .. + +# Copy the AppImage and update info to the artifacts directory and avoid compressing it +cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/" +if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then + cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/" +fi + +# Copy the AppImage to the general release directory and remove git revision info +if [ "${RELEASE_NAME}" = "stable" ] || [ "${RELEASE_NAME}" = "canary" ]; then + cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/citron-${RELEASE_NAME}.AppImage" +fi + +# Copy debug symbols to artifacts +cd build/bin +tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug +cd - + +. .ci/scripts/common/post-upload.sh diff --git a/.ci/scripts/merge/apply-patches-by-label-private.py b/.ci/scripts/merge/apply-patches-by-label-private.py new file mode 100755 index 000000000..b62b8e5bc --- /dev/null +++ b/.ci/scripts/merge/apply-patches-by-label-private.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Apply patches that are tagged with a specific label +# Current labels include: +# "canary merge benefit" + +import os +import sys +org = os.getenv("PRIVATEMERGEORG", "Citron-Project") +base = os.getenv("PRIVATEMERGEBASE", "Cit") +head = os.getenv("PRIVATEMERGEBASE", "Cit-canary") + +# We use different files for the 3 PR checkers. +check_label_presence_file = ".ci/scripts/merge/check-label-presence.py" +check_reviews_file = ".ci/scripts/merge/check-reviews-private.py" + +# python3 python_file org repo pr# label +os.system(f"python3 .ci/scripts/merge/apply-patches-by-label.py {sys.argv[1]} {org} {base} {head} {check_label_presence_file} {check_reviews_file}") diff --git a/.ci/scripts/merge/apply-patches-by-label.py b/.ci/scripts/merge/apply-patches-by-label.py new file mode 100755 index 000000000..75df9d25a --- /dev/null +++ b/.ci/scripts/merge/apply-patches-by-label.py @@ -0,0 +1,60 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Apply patches that are tagged with a specific label +# Current labels include: +# "canary merge benefit" + +import json +import os +import sys +import subprocess +import re +from urllib import request + + +def get_pull_request_list(page=1): + url = f"https://api.github.com/repos/Citron-Project/Cit/pulls?page={page}" + headers = {'Accept': 'application/vnd.github.v3+json'} + token = os.environ.get('EARLY_ACCESS_TOKEN') + if token: + headers['Authorization'] = f'token {token}' + req = request.Request(url, headers=headers) + response = request.urlopen(req) + result = json.loads(response.read().decode('utf-8')) + if len(result) > 0: + return result + get_pull_request_list(page + 1) + return [] + + +seen = {} + +for pr in get_pull_request_list(): + pn = pr['number'] + if pn in seen: + continue + seen[pn] = True + print(subprocess.check_output(["git", "fetch", "https://github.com/Citron-Project/Cit.git", f"pull/{pn}/head:pr-{pn}", "-f", "--no-recurse-submodules"])) + out = subprocess.check_output(["python3", ".ci/scripts/merge/check-label-presence.py", str(pn), sys.argv[1]]) + if b'true' in out: + print(f'Applying PR #{pn} matching label {sys.argv[1]}...') + try: + if b'changes requested' in subprocess.check_output(["python3", ".ci/scripts/merge/check-reviews.py", str(pn)]): + print(f'PR #{pn} has requested changes, skipping') + continue + else: + print(f'PR #{pn} has approving reviews, applying') + except: + print('Could not find any valid reviews, applying anyway') + try: + print(subprocess.check_output(["git", "merge", "--squash", f"pr-{pn}", "-m", f"Merge PR #{pn} tagged with: {sys.argv[1]}"])) + except: + print("Failed to merge cleanly, are there conflicts?") + exit(0) + +# Apply a tag to the EA release +try: + print(subprocess.check_output(["git", "tag", "-f", f"canary/{sys.argv[1]}"])) +except: + print("Failed to apply canary tag") + exit(0) diff --git a/.ci/scripts/merge/check-label-presence.py b/.ci/scripts/merge/check-label-presence.py new file mode 100755 index 000000000..7274968f0 --- /dev/null +++ b/.ci/scripts/merge/check-label-presence.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2019 citron Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +# Check if a pull request has a specific label +# Usage: python3 check-label-presence.py