name: 'Deploy workflow' on: push: branches: - '**' env: QT_MIRROR: https://mirrors.ocf.berkeley.edu/qt/ # https://download.qt.io/static/mirrorlist/ jobs: Build-Linux-Ubuntu: runs-on: android-runner env: QT_VERSION: 6.10.1 QIF_VERSION: 4.7 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install Qt' uses: jurplel/install-qt-action@v3 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'desktop' arch: 'linux_gcc_64' modules: 'qtremoteobjects qt5compat qtshadertools' dir: ${{ runner.temp }} setup-python: 'true' tools: 'tools_ifw' set-env: 'true' aqtversion: '==3.3.0' py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Setup python' uses: actions/setup-python@v6 with: python-version: 3.14 - name: 'Install conan' run: pip install "conan==2.26.2" - name: 'Install system packages' run: sudo apt-get install libxkbcommon-x11-0 libsecret-1-dev - name: 'Get sources' uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 10 - name: 'Build project' shell: bash env: QT_INSTALL_DIR: ${{ runner.temp }} run: ./deploy/build.sh - name: 'Upload installer artifact' uses: actions/upload-artifact@v7 with: path: deploy/build/AmneziaVPN-*-Linux.run archive: false retention-days: 7 - name: 'Upload translations artifact' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_translations path: client/translations retention-days: 7 # ------------------------------------------------------ Build-Windows: runs-on: windows-latest env: QT_VERSION: 6.10.1 QIF_VERSION: 4.7 PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Get sources' uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 10 - name: 'Install Qt' uses: jurplel/install-qt-action@v3 with: version: ${{ env.QT_VERSION }} host: 'windows' target: 'desktop' arch: 'win64_msvc2022_64' modules: 'qtremoteobjects qt5compat qtshadertools' dir: ${{ runner.temp }} setup-python: 'true' tools: 'tools_ifw' set-env: 'true' aqtversion: '==3.3.0' py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Setup mvsc' uses: ilammy/msvc-dev-cmd@v1 with: arch: 'x64' - name: 'Setup .NET SDK' uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - name: 'Install WiX Toolset' shell: powershell run: | dotnet tool install --global wix --version 4.0.6 wix extension add -g WixToolset.UI.wixext/4.0.6 wix extension add -g WixToolset.Util.wixext/4.0.6 wix extension list -g $wixBinDir = Join-Path $env:USERPROFILE ".dotnet\tools" echo "WIX_BIN_DIR=$wixBinDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: 'Setup python' uses: actions/setup-python@v6 with: python-version: 3.14 - name: 'Install conan' run: pip install "conan==2.26.2" - name: 'Build project' shell: cmd env: QT_INSTALL_DIR: ${{ runner.temp }} run: | set WIX_ROOT_PATH="${{ env.USERPROFILE }}\.dotnet\tools" deploy\build.bat --installer all - name: 'Upload WIX installer artifact' uses: actions/upload-artifact@v7 with: path: deploy/build/AmneziaVPN-*-win64.msi archive: false retention-days: 7 - name: 'Upload IFW installer artifact' uses: actions/upload-artifact@v7 with: path: deploy/build/AmneziaVPN-*-win64.exe archive: false retention-days: 7 # ------------------------------------------------------ Build-iOS: runs-on: macos-latest env: QT_VERSION: 6.10.1 CC: cc CXX: c++ PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '26.1' - name: 'Install desktop Qt' uses: jurplel/install-qt-action@v3 with: version: ${{ env.QT_VERSION }} host: 'mac' target: 'desktop' modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia' arch: 'clang_64' dir: ${{ runner.temp }} set-env: 'true' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Install iOS Qt' uses: jurplel/install-qt-action@v3 with: version: ${{ env.QT_VERSION }} host: 'mac' target: 'ios' modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia' dir: ${{ runner.temp }} setup-python: 'true' set-env: 'true' extra: '--external 7z --base ${{ env.QT_MIRROR }}' - name: 'Install go' uses: actions/setup-go@v5 with: go-version: '1.24' cache: false - name: 'Setup gomobile' run: | export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest gomobile init - name: 'Get sources' uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 10 # - name: 'Setup ccache' # uses: hendrikmuhs/ccache-action@v1.2 - name: 'Install dependencies' run: pip install jsonschema jinja2 - name: 'Build project' run: | git submodule update --init --recursive export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/ios/bin" export QT_MACOS_ROOT_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos" export PATH=$PATH:~/go/bin sh deploy/build_ios.sh | \ sed -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DPROD_AGW_PUBLIC_KEY/d' | \ sed -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/,/-Xcc/ { /-Xcc/!d; }' -e '/-Xcc -DDEV_AGW_PUBLIC_KEY/d' | \ sed -e '/-DPROD_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DPROD_AGW_PUBLIC_KEY/d' | \ sed -e '/-DDEV_AGW_PUBLIC_KEY/,/-D/ { /-D/!d; }' -e '/-DDEV_AGW_PUBLIC_KEY/d' env: IOS_TRUST_CERT_BASE64: ${{ secrets.IOS_TRUST_CERT_BASE64 }} IOS_SIGNING_CERT_BASE64: ${{ secrets.IOS_SIGNING_CERT_BASE64 }} IOS_SIGNING_CERT_PASSWORD: ${{ secrets.IOS_SIGNING_CERT_PASSWORD }} APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }} APPSTORE_CONNECT_ISSUER_ID: ${{ secrets.APPSTORE_CONNECT_ISSUER_ID }} APPSTORE_CONNECT_PRIVATE_KEY: ${{ secrets.APPSTORE_CONNECT_PRIVATE_KEY }} IOS_APP_PROVISIONING_PROFILE: ${{ secrets.IOS_APP_PROVISIONING_PROFILE }} IOS_NE_PROVISIONING_PROFILE: ${{ secrets.IOS_NE_PROVISIONING_PROFILE }} # ------------------------------------------------------ Build-MacOS: runs-on: macos-latest env: QT_VERSION: 6.10.1 KEYCHAIN_NAME: "build.keychain" KEYCHAIN_PASSWORD: "" PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '16.2.0' - name: 'Install Qt' uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'mac' target: 'desktop' arch: 'clang_64' modules: 'qtremoteobjects qt5compat qtshadertools' dir: ${{ runner.temp }} setup-python: 'true' set-env: 'true' aqtversion: '==3.3.0' py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Setup python' uses: actions/setup-python@v6 with: python-version: 3.14 - name: 'Install conan' run: pip install "conan==2.26.2" - name: 'Get sources' uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 10 - name: 'Install certs' uses: ./.github/actions/setup-keychain with: keychain-path: ${{ env.KEYCHAIN_NAME }} keychain-password: ${{ env.KEYCHAIN_PASSWORD }} app-cert-base64: ${{ secrets.MAC_APP_CERT_CERT }} app-cert-password: ${{ secrets.MAC_APP_CERT_PW }} installer-cert-base64: ${{ secrets.MAC_INSTALLER_SIGNER_CERT }} installer-cert-password: ${{ secrets.MAC_INSTALL_CERT_PW }} - name: 'Build project' env: QT_INSTALL_DIR: ${{ runner.temp }} CODESIGN_KEYCHAIN: ${{ env.KEYCHAIN_NAME }} CODESIGN_SIGNATURE: ${{ secrets.MAC_SIGNER_ID }} CODESIGN_INSTALLER_KEYCHAIN: ${{ env.KEYCHAIN_NAME }} CODESIGN_INSTALLER_SIGNATURE: ${{ secrets.MAC_INSTALLER_SIGNER_ID }} NOTARYTOOL_TEAM_ID: ${{ secrets.MAC_TEAM_ID }} NOTARYTOOL_EMAIL: ${{ secrets.APPLE_DEV_EMAIL }} NOTARYTOOL_PASSWORD: ${{ secrets.APPLE_DEV_PASSWORD }} shell: bash run: deploy/build.sh - name: 'Upload installer artifact' uses: actions/upload-artifact@v7 with: path: deploy/build/AmneziaVPN-*-Darwin.pkg archive: false retention-days: 7 Build-MacOS-NE: runs-on: macos-latest env: QT_VERSION: 6.10.1 MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }} MAC_APP_CERT_CERT: ${{ secrets.MAC_APP_CERT_CERT }} MAC_SIGNER_ID: ${{ secrets.MAC_SIGNER_ID }} MAC_APP_CERT_PW: ${{ secrets.MAC_APP_CERT_PW }} PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Setup xcode' uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '26.1' - name: 'Install desktop Qt' uses: jurplel/install-qt-action@v3 with: version: ${{ env.QT_VERSION }} host: 'mac' target: 'desktop' modules: 'qtremoteobjects qt5compat qtshadertools qtmultimedia' arch: 'clang_64' dir: ${{ runner.temp }} set-env: 'true' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Install go' uses: actions/setup-go@v5 with: go-version: '1.24' cache: false - name: 'Setup gomobile' run: | export PATH=$PATH:~/go/bin go install golang.org/x/mobile/cmd/gomobile@latest gomobile init - name: 'Get sources' uses: actions/checkout@v4 with: submodules: 'true' fetch-depth: 10 # - name: 'Setup ccache' # uses: hendrikmuhs/ccache-action@v1.2 - name: 'Build project' run: | export QT_BIN_DIR="${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos/bin" bash deploy/build_macos_ne.sh - name: 'Upload unpacked artifact' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_MacOS_unpacked path: deploy/build/client/AmneziaVPN.app retention-days: 7 # ------------------------------------------------------ Build-Android: runs-on: android-runner env: ANDROID_BUILD_PLATFORM: android-36 QT_VERSION: 6.10.1 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }} DEV_AGW_PUBLIC_KEY: ${{ secrets.DEV_AGW_PUBLIC_KEY }} DEV_AGW_ENDPOINT: ${{ secrets.DEV_AGW_ENDPOINT }} DEV_S3_ENDPOINT: ${{ secrets.DEV_S3_ENDPOINT }} FREE_V2_ENDPOINT: ${{ secrets.FREE_V2_ENDPOINT }} PREM_V1_ENDPOINT: ${{ secrets.PREM_V1_ENDPOINT }} steps: - name: 'Install desktop Qt' uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'desktop' arch: 'linux_gcc_64' modules: ${{ env.QT_MODULES }} dir: ${{ runner.temp }} py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Install android_x86_64 Qt' uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'android' arch: 'android_x86_64' modules: ${{ env.QT_MODULES }} dir: ${{ runner.temp }} py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Install android_x86 Qt' uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'android' arch: 'android_x86' modules: ${{ env.QT_MODULES }} dir: ${{ runner.temp }} py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Install android_armv7 Qt' uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'android' arch: 'android_armv7' modules: ${{ env.QT_MODULES }} dir: ${{ runner.temp }} py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Install android_arm64_v8a Qt' uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'android' arch: 'android_arm64_v8a' modules: ${{ env.QT_MODULES }} dir: ${{ runner.temp }} py7zrversion: '==0.22.*' extra: '--base ${{ env.QT_MIRROR }}' - name: 'Grant execute permission for qt-cmake' shell: bash run: | chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake - name: 'Get sources' uses: actions/checkout@v4 with: submodules: 'true' - name: 'Get version from CMakeLists.txt' id: get_version run: | VERSION=$(grep 'set(AMNEZIAVPN_VERSION' CMakeLists.txt | sed -E 's/.*AMNEZIAVPN_VERSION ([0-9]+.[0-9]+.[0-9]+.[0-9]+)\)/\1/') echo "VERSION=$VERSION" >> $GITHUB_ENV echo "Version: $VERSION" # - name: 'Setup ccache' # uses: hendrikmuhs/ccache-action@v1.2 - name: 'Setup Java' uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' # cache: 'gradle' - name: 'Setup Android NDK' id: setup-ndk uses: nttld/setup-ndk@v1 with: ndk-version: 'r26b' - name: 'Decode keystore secret to file' env: KEYSTORE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }} shell: bash run: | echo $KEYSTORE_BASE64 | base64 --decode > android.keystore - name: 'Build project' env: ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64 ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }} ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }} shell: bash run: ./deploy/build_android.sh --aab --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }} - name: 'Rename Android APKs' run: | cd deploy/build mv AmneziaVPN-x86_64-release.apk AmneziaVPN_${VERSION}_android9+_x86_64.apk mv AmneziaVPN-x86-release.apk AmneziaVPN_${VERSION}_android9+_x86.apk mv AmneziaVPN-arm64-v8a-release.apk AmneziaVPN_${VERSION}_android9+_arm64-v8a.apk mv AmneziaVPN-armeabi-v7a-release.apk AmneziaVPN_${VERSION}_android9+_armeabi-v7a.apk cd ../.. - name: 'Upload x86_64 apk' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_${{ env.VERSION }}_android9+_x86_64.apk path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_x86_64.apk compression-level: 0 retention-days: 7 - name: 'Upload x86 apk' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_${{ env.VERSION }}_android9+_x86.apk path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_x86.apk compression-level: 0 retention-days: 7 - name: 'Upload arm64-v8a apk' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_${{ env.VERSION }}_android9+_arm64-v8a.apk path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_arm64-v8a.apk compression-level: 0 retention-days: 7 - name: 'Upload armeabi-v7a apk' uses: actions/upload-artifact@v4 with: name: AmneziaVPN_${{ env.VERSION }}_android9+_armeabi-v7a.apk path: deploy/build/AmneziaVPN_${{ env.VERSION }}_android9+_armeabi-v7a.apk compression-level: 0 retention-days: 7 - name: 'Upload aab' uses: actions/upload-artifact@v4 with: name: AmneziaVPN-android path: deploy/build/AmneziaVPN-release.aab compression-level: 0 retention-days: 7 Extra: runs-on: ubuntu-latest steps: - name: Search a corresponding PR uses: octokit/request-action@v2.x id: pull_request with: route: GET /repos/${{ github.repository }}/pulls head: ${{ github.repository_owner }}:${{ github.ref_name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Add PR link to build summary if: ${{ fromJSON(steps.pull_request.outputs.data)[0].number != '' }} run: | echo "Pull request:" >> $GITHUB_STEP_SUMMARY echo "[[#${{ fromJSON(steps.pull_request.outputs.data)[0].number }}] ${{ fromJSON(steps.pull_request.outputs.data)[0].title }}](${{ fromJSON(steps.pull_request.outputs.data)[0].html_url }})" >> $GITHUB_STEP_SUMMARY