diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml deleted file mode 100644 index cb2afa3962..0000000000 --- a/.github/workflows/cache_dependencies.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Cache Dependencies - -on: - workflow_dispatch: - push: - branches: [ main ] - -jobs: - test: - - runs-on: ubuntu-20.04 - - steps: - - name: Free Disk Space (Ubuntu) - uses: insightsengineering/disk-space-reclaimer@v1 - with: - tools-cache: true - android: false - dotnet: true - haskell: true - large-packages: true - swap-storage: true - docker-images: true - - - uses: actions/checkout@v2 - - uses: actions/setup-java@v2 - with: - distribution: "temurin" - java-version: "17" - - name: Configure placeholder git details - run: | - git config --global user.email "CI@cakewallet.com" - git config --global user.name "Cake Github Actions" - - name: Flutter action - uses: subosito/flutter-action@v1 - with: - flutter-version: "3.24.4" - channel: stable - - - name: Install package dependencies - run: sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang - - - name: Execute Build and Setup Commands - run: | - sudo mkdir -p /opt/android - sudo chown $USER /opt/android - cd /opt/android - -y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - cargo install cargo-ndk - git clone https://github.com/cake-tech/cake_wallet.git --branch main - cd cake_wallet/scripts/android/ - ./install_ndk.sh - source ./app_env.sh cakewallet - chmod +x pubspec_gen.sh - ./app_config.sh - - - name: Cache Externals - id: cache-externals - uses: actions/cache@v3 - with: - path: | - /opt/android/cake_wallet/cw_haven/android/.cxx - /opt/android/cake_wallet/scripts/monero_c/release - key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }} - - - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} - name: Generate Externals - run: | - cd /opt/android/cake_wallet/scripts/android/ - source ./app_env.sh cakewallet - ./build_monero_all.sh - - - name: Cache Keystore - id: cache-keystore - uses: actions/cache@v3 - with: - path: /opt/android/cake_wallet/android/app - key: keystore - - - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }} - name: Generate KeyStore - run: | - cd /opt/android/cake_wallet/android/app - keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index cdd0e40b44..c5f8751287 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -1,169 +1,93 @@ -name: PR Test Build +name: Cake Wallet Android -on: - pull_request: - branches: [main] - workflow_dispatch: - inputs: - branch: - description: "Branch name to build" - required: true - default: "main" +on: [push] +defaults: + run: + shell: bash jobs: PR_test_build: - runs-on: ubuntu-20.04 + runs-on: linux-amd64 + container: + image: ghcr.io/cake-tech/cake_wallet:main-linux + env: + STORE_PASS: test@cake_wallet + KEY_PASS: test@cake_wallet + MONEROC_CACHE_DIR_ROOT: /opt/generic_cache + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + ANDROID_AVD_HOME: /root/.android/avd + volumes: + - /opt/cw_cache_android/root/.cache:/root/.cache + - /opt/cw_cache_android/root/.android/avd/:/root/.android/avd + - /opt/cw_cache_android/root/.ccache:/root/.ccache + - /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache + - /opt/cw_cache_android/root/.gradle/:/root/.gradle + - /opt/cw_cache_android/root/.android/:/root/.android + - /opt/cw_cache_android/root/go/pkg:/root/go/pkg + - /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache + - /dev/kvm:/dev/kvm strategy: matrix: api-level: [29] - env: - STORE_PASS: test@cake_wallet - KEY_PASS: test@cake_wallet - PR_NUMBER: ${{ github.event.number }} steps: - - name: is pr - if: github.event_name == 'pull_request' - run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV - - - name: is not pr - if: github.event_name != 'pull_request' - run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV - - - name: Free Disk Space (Ubuntu) - uses: insightsengineering/disk-space-reclaimer@v1 - with: - tools-cache: true - android: false - dotnet: true - haskell: true - large-packages: true - swap-storage: true - docker-images: true - - - uses: actions/checkout@v2 - - uses: actions/setup-java@v2 - with: - distribution: "temurin" - java-version: "17" - - name: Configure placeholder git details - run: | - git config --global user.email "CI@cakewallet.com" - git config --global user.name "Cake Github Actions" - - name: Flutter action - uses: subosito/flutter-action@v1 - with: - flutter-version: "3.24.0" - channel: stable - - - name: Install package dependencies - run: | - sudo apt update - sudo apt-get install -y curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake clang - - - - name: Clone Repo - run: | - sudo mkdir -p /opt/android - sudo chown $USER /opt/android - cd /opt/android - git clone https://github.com/cake-tech/cake_wallet.git --branch ${{ env.BRANCH_NAME }} - -# - name: Cache Keystore -# id: cache-keystore -# uses: actions/cache@v3 -# with: -# path: /opt/android/cake_wallet/android/app -# key: keystore -# -# - if: ${{ steps.cache-keystore.outputs.cache-hit != 'true' }} - - name: Generate KeyStore - run: | - cd /opt/android/cake_wallet/android/app - keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS - - - name: Execute Build and Setup Commands - run: | - cd /opt/android - -y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - cargo install cargo-ndk - cd cake_wallet/scripts/android/ - ./install_ndk.sh - source ./app_env.sh cakewallet - chmod +x pubspec_gen.sh - ./app_config.sh - - - name: Cache Externals - id: cache-externals - uses: actions/cache@v3 - with: - path: | - /opt/android/cake_wallet/cw_haven/android/.cxx - /opt/android/cake_wallet/scripts/monero_c/release - key: ${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }} - - - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} - name: Generate Externals - run: | - cd /opt/android/cake_wallet/scripts/android/ - source ./app_env.sh cakewallet - ./build_monero_all.sh - - - name: Install Flutter dependencies - run: | - cd /opt/android/cake_wallet - flutter pub get - - - - name: Install go and gomobile - run: | - # install go > 1.23: - wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz - export PATH=$PATH:/usr/local/go/bin - export PATH=$PATH:~/go/bin - go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init - - - name: Build mwebd - run: | - # paths are reset after each step, so we need to set them again: - export PATH=$PATH:/usr/local/go/bin - export PATH=$PATH:~/go/bin - cd /opt/android/cake_wallet/scripts/android/ - ./build_mwebd.sh --dont-install - - - name: Generate key properties - run: | - cd /opt/android/cake_wallet - dart run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS - - - name: Generate localization - run: | - cd /opt/android/cake_wallet - dart run tool/generate_localization.dart - - - name: Build generated code + - name: Fix github actions messing up $HOME... + run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV' + - uses: actions/checkout@v4 + - name: configure git run: | - cd /opt/android/cake_wallet - ./model_generator.sh - + git config --global user.email "ci@cakewallet.com" + git config --global user.name "CakeWallet CI" - name: Add secrets run: | - cd /opt/android/cake_wallet touch lib/.secrets.g.dart touch cw_evm/lib/.secrets.g.dart touch cw_solana/lib/.secrets.g.dart touch cw_core/lib/.secrets.g.dart touch cw_nano/lib/.secrets.g.dart touch cw_tron/lib/.secrets.g.dart - echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart - echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart - echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart - echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart - echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart - echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart - echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + if [[ "x${{ secrets.SALT }}" == "x" ]]; + then + echo "const salt = '954f787f12622067f7e548d9450c3832';" > lib/.secrets.g.dart + else + echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart + fi + if [[ "x${{ secrets.KEY_CHAIN_SALT }}" == "x" ]]; + then + echo "const keychainSalt = '2d2beba777dbf7dff7013b7a';" >> lib/.secrets.g.dart + else + echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.KEY }}" == "x" ]]; + then + echo "const key = '638e98820ec10a2945e968435c9397a3';" >> lib/.secrets.g.dart + else + echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.WALLET_SALT }}" == "x" ]]; + then + echo "const walletSalt = '8f7f1b70';" >> lib/.secrets.g.dart + else + echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.SHORT_KEY }}" == "x" ]]; + then + echo "const shortKey = '653f270c2c152bc7ec864afe';" >> lib/.secrets.g.dart + else + echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.BACKUP_SALT }}" == "x" ]]; + then + echo "const backupSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart + else + echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.BACKUP_KEY_CHAIN_SALT }}" == "x" ]]; + then + echo "const backupKeychainSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart + else + echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + fi echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart @@ -213,86 +137,153 @@ jobs: echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart + # for tests + echo "const moneroTestWalletSeeds ='${{ secrets.MONERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const moneroLegacyTestWalletSeeds = '${{ secrets.MONERO_LEGACY_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const bitcoinTestWalletSeeds = '${{ secrets.BITCOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const ethereumTestWalletSeeds = '${{ secrets.ETHEREUM_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const wowneroTestWalletSeeds = '${{ secrets.WOWNERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const moneroTestWalletReceiveAddress = '${{ secrets.MONERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const bitcoinTestWalletReceiveAddress = '${{ secrets.BITCOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const ethereumTestWalletReceiveAddress = '${{ secrets.ETHEREUM_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const wowneroTestWalletReceiveAddress = '${{ secrets.WOWNERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const moneroTestWalletBlockHeight = '${{ secrets.MONERO_TEST_WALLET_BLOCK_HEIGHT }}';" >> lib/.secrets.g.dart + - name: prepare monero_c and cache + run: | + export MONEROC_HASH=$(cat scripts/prepare_moneroc.sh | grep 'git checkout' | xargs | awk '{ print $3 }') + echo MONEROC_HASH=$MONEROC_HASH >> /etc/environment + mkdir -p "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c" + pushd scripts + ln -s "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c" + ./prepare_moneroc.sh + popd + pushd scripts/monero_c + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" || true + + rm -rf "$PWD/contrib/depends/built" "$PWD/monero/contrib/depends/built" "$PWD/wownero/contrib/depends/built" + rm -rf "$PWD/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources" + mkdir -p contrib/depends || true + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" "$PWD/contrib/depends/built" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" "$PWD/monero/contrib/depends/built" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" "$PWD/wownero/contrib/depends/built" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" "$PWD/contrib/depends/sources" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources" + popd - - name: Rename app + - name: Generate KeyStore run: | - echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties + pushd /opt/generic_cache + if [[ ! -f key.jks ]]; + then + keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias testKey -noprompt -dname "CN=CakeWallet, OU=CakeWallet, O=CakeWallet, L=Florida, S=America, C=USA" -storepass $STORE_PASS -keypass $KEY_PASS + else + echo "$PWD/key.jks exist, not generating" + fi + popd + cp /opt/generic_cache/key.jks android/app - # Step 3: Download previous build number - - name: Download previous build number - id: download-build-number + - name: Execute Build and Setup Commands run: | - # Download the artifact if it exists - if [[ ! -f build_number.txt ]]; then - echo "1" > build_number.txt - fi + pushd scripts/android + source ./app_env.sh cakewallet + ./app_config.sh + popd + + - name: Build monero_c + run: | + pushd scripts/android/ + source ./app_env.sh cakewallet + ./build_monero_all.sh + popd + + - name: Install Flutter dependencies + run: | + flutter pub get - # Step 4: Read and Increment Build Number - - name: Increment Build Number - id: increment-build-number + - name: Build mwebd + run: | + set -x -e + export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }') + echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment + pushd scripts/android + gomobile init; + ./build_mwebd.sh --dont-install + popd + + - name: Build generated code run: | - # Read current build number from file - BUILD_NUMBER=$(cat build_number.txt) - BUILD_NUMBER=$((BUILD_NUMBER + 1)) - echo "New build number: $BUILD_NUMBER" + ./model_generator.sh async - # Save the incremented build number - echo "$BUILD_NUMBER" > build_number.txt + - name: Generate key properties + run: | + dart run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS - # Export the build number to use in later steps - echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV + - name: Generate localization + run: | + dart run tool/generate_localization.dart - # Step 5: Update pubspec.yaml with new build number - - name: Update build number + - name: Rename app run: | - cd /opt/android/cake_wallet - sed -i "s/^version: .*/version: 1.0.$BUILD_NUMBER/" pubspec.yaml + sanitized_branch_name=${BRANCH_NAME#origin/} # Remove 'origin/' prefix if it exists + sanitized_branch_name=${sanitized_branch_name:0:16} # Take only the first 16 characters + sanitized_branch_name=$(echo "$sanitized_branch_name" | tr '[:upper:]' '[:lower:]') # Convert to lowercase + sanitized_branch_name=$(echo "$sanitized_branch_name" | sed 's/[^a-z0-9]//g') # Remove all special characters + + echo -e "id=com.cakewallet.test_${sanitized_branch_name}\nname=${BRANCH_NAME}" > android/app.properties - name: Build run: | - cd /opt/android/cake_wallet flutter build apk --release --split-per-abi - # - name: Push to App Center - # run: | - # echo 'Installing App Center CLI tools' - # npm install -g appcenter-cli - # echo "Publishing test to App Center" - # appcenter distribute release \ - # --group "Testers" \ - # --file "/opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk" \ - # --release-notes ${{ env.BRANCH_NAME }} \ - # --app Cake-Labs/Cake-Wallet \ - # --token ${{ secrets.APP_CENTER_TOKEN }} \ - # --quiet - - name: Rename apk file run: | - cd /opt/android/cake_wallet/build/app/outputs/flutter-apk + cd build/app/outputs/flutter-apk mkdir test-apk - cp app-arm64-v8a-release.apk test-apk/${{env.BRANCH_NAME}}.apk - cp app-x86_64-release.apk test-apk/${{env.BRANCH_NAME}}_x86.apk - - - name: Upload Artifact - uses: kittaakos/upload-artifact-as-is@v0 - with: - path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/ - - # Re-upload updated build number for the next run - - name: Upload updated build number - uses: actions/upload-artifact@v3 - with: - name: build_number - path: build_number.txt + cp app-arm64-v8a-release.apk test-apk/${BRANCH_NAME}.apk + cp app-x86_64-release.apk test-apk/${BRANCH_NAME}_x86.apk + cd test-apk + cp ${BRANCH_NAME}.apk ${BRANCH_NAME}_slack.apk - - name: Send Test APK + - name: Find APK file + id: find_apk + run: | + set -x + apk_file=$(ls build/app/outputs/flutter-apk/test-apk/*_slack.apk || exit 1) + echo "APK_FILE=$apk_file" >> $GITHUB_ENV + + - name: Upload artifact to slack + if: ${{ !contains(github.event.head_commit.message, 'skip slack') }} continue-on-error: true uses: adrey/slack-file-upload-action@1.0.5 with: token: ${{ secrets.SLACK_APP_TOKEN }} - path: /opt/android/cake_wallet/build/app/outputs/flutter-apk/test-apk/${{env.BRANCH_NAME}}.apk + path: ${{ env.APK_FILE }} channel: ${{ secrets.SLACK_APK_CHANNEL }} - title: "${{ env.BRANCH_NAME }}.apk" - filename: ${{ env.BRANCH_NAME }}.apk initial_comment: ${{ github.event.head_commit.message }} + - name: cleanup + run: rm -rf build/app/outputs/flutter-apk/test-apk/ + + - name: Upload Artifact to github + uses: actions/upload-artifact@v4 + with: + path: ${{ github.workspace }}/build/app/outputs/flutter-apk + name: "android apk" \ No newline at end of file diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 891327d1e7..345c95c4f7 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -1,139 +1,89 @@ -name: PR Test Build linux +name: Cake Wallet Linux -on: - pull_request: - branches: [main] - workflow_dispatch: - inputs: - branch: - description: "Branch name to build" - required: true - default: "main" +on: [push] +defaults: + run: + shell: bash jobs: PR_test_build: - runs-on: ubuntu-20.04 - env: - STORE_PASS: test@cake_wallet - KEY_PASS: test@cake_wallet - PR_NUMBER: ${{ github.event.number }} + runs-on: linux-amd64 + container: + image: ghcr.io/cake-tech/cake_wallet:main-linux + env: + STORE_PASS: test@cake_wallet + KEY_PASS: test@cake_wallet + MONEROC_CACHE_DIR_ROOT: /opt/generic_cache + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + DESKTOP_FORCE_MOBILE: Y + volumes: + - /opt/cw_cache_linux/root/.cache:/root/.cache + - /opt/cw_cache_linux/root/.ccache:/root/.ccache + - /opt/cw_cache_linux/root/.pub-cache/:/root/.pub-cache + - /opt/cw_cache_linux/root/go/pkg:/root/go/pkg + - /opt/cw_cache_linux/opt/generic_cache:/opt/generic_cache + strategy: + matrix: + api-level: [29] steps: - - name: is pr - if: github.event_name == 'pull_request' - run: echo "BRANCH_NAME=${GITHUB_HEAD_REF}" >> $GITHUB_ENV - - - name: is not pr - if: github.event_name != 'pull_request' - run: echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENVg - - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: "17.x" - - name: Configure placeholder git details - run: | - git config --global user.email "CI@cakewallet.com" - git config --global user.name "Cake Github Actions" - - name: Flutter action - uses: subosito/flutter-action@v1 - with: - flutter-version: "3.24.0" - channel: stable - - - name: Install package dependencies - run: | - sudo apt update - sudo apt-get install -y curl unzip automake build-essential file pkg-config git python-is-python3 libtool libtinfo5 cmake clang - - - name: Install desktop dependencies - run: | - sudo apt update - sudo apt install -y ninja-build libgtk-3-dev gperf - - name: Execute Build and Setup Commands - run: | - sudo mkdir -p /opt/android - sudo chown $USER /opt/android - cd /opt/android - -y curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - cargo install cargo-ndk - git clone https://github.com/cake-tech/cake_wallet.git --branch ${{ env.BRANCH_NAME }} - cd scripts && ./gen_android_manifest.sh && cd .. - cd cake_wallet/scripts/android/ - source ./app_env.sh cakewallet - ./app_config.sh - cd ../../.. - cd cake_wallet/scripts/linux/ - source ./app_env.sh cakewallet - ./app_config.sh - cd ../../.. - - - name: Cache Externals - id: cache-externals - uses: actions/cache@v3 - with: - path: | - /opt/android/cake_wallet/cw_haven/android/.cxx - /opt/android/cake_wallet/scripts/monero_c/release - key: linux_${{ hashFiles('**/prepare_moneroc.sh' ,'**/build_monero_all.sh' ,'**/cache_dependencies.yml') }} - - - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} - name: Generate Externals - run: | - cd /opt/android/cake_wallet/scripts/linux/ - source ./app_env.sh cakewallet - ./build_monero_all.sh - - - name: Install Flutter dependencies - run: | - cd /opt/android/cake_wallet - flutter pub get - - - name: Install go and gomobile - run: | - # install go > 1.23: - wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz - export PATH=$PATH:/usr/local/go/bin - export PATH=$PATH:~/go/bin - go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init - - - name: Build mwebd - run: | - # paths are reset after each step, so we need to set them again: - export PATH=$PATH:/usr/local/go/bin - export PATH=$PATH:~/go/bin - # build mwebd: - cd /opt/android/cake_wallet/scripts/android/ - ./build_mwebd.sh --dont-install - - - name: Generate localization + - name: Fix github actions messing up $HOME... + run: 'echo HOME=/root | sudo tee -a $GITHUB_ENV' + - uses: actions/checkout@v4 + - name: configure git run: | - cd /opt/android/cake_wallet - dart run tool/generate_localization.dart - - - name: Build generated code - run: | - cd /opt/android/cake_wallet - ./model_generator.sh - + git config --global user.email "ci@cakewallet.com" + git config --global user.name "CakeWallet CI" - name: Add secrets run: | - cd /opt/android/cake_wallet touch lib/.secrets.g.dart touch cw_evm/lib/.secrets.g.dart touch cw_solana/lib/.secrets.g.dart touch cw_core/lib/.secrets.g.dart touch cw_nano/lib/.secrets.g.dart touch cw_tron/lib/.secrets.g.dart - echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart - echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart - echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart - echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart - echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart - echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart - echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + if [[ "x${{ secrets.SALT }}" == "x" ]]; + then + echo "const salt = '954f787f12622067f7e548d9450c3832';" > lib/.secrets.g.dart + else + echo "const salt = '${{ secrets.SALT }}';" > lib/.secrets.g.dart + fi + if [[ "x${{ secrets.KEY_CHAIN_SALT }}" == "x" ]]; + then + echo "const keychainSalt = '2d2beba777dbf7dff7013b7a';" >> lib/.secrets.g.dart + else + echo "const keychainSalt = '${{ secrets.KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.KEY }}" == "x" ]]; + then + echo "const key = '638e98820ec10a2945e968435c9397a3';" >> lib/.secrets.g.dart + else + echo "const key = '${{ secrets.KEY }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.WALLET_SALT }}" == "x" ]]; + then + echo "const walletSalt = '8f7f1b70';" >> lib/.secrets.g.dart + else + echo "const walletSalt = '${{ secrets.WALLET_SALT }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.SHORT_KEY }}" == "x" ]]; + then + echo "const shortKey = '653f270c2c152bc7ec864afe';" >> lib/.secrets.g.dart + else + echo "const shortKey = '${{ secrets.SHORT_KEY }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.BACKUP_SALT }}" == "x" ]]; + then + echo "const backupSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart + else + echo "const backupSalt = '${{ secrets.BACKUP_SALT }}';" >> lib/.secrets.g.dart + fi + if [[ "x${{ secrets.BACKUP_KEY_CHAIN_SALT }}" == "x" ]]; + then + echo "const backupKeychainSalt = 'bf630d24ff0b6f60';" >> lib/.secrets.g.dart + else + echo "const backupKeychainSalt = '${{ secrets.BACKUP_KEY_CHAIN_SALT }}';" >> lib/.secrets.g.dart + fi echo "const changeNowApiKey = '${{ secrets.CHANGE_NOW_API_KEY }}';" >> lib/.secrets.g.dart echo "const changeNowApiKeyDesktop = '${{ secrets.CHANGE_NOW_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const wyreSecretKey = '${{ secrets.WYRE_SECRET_KEY }}';" >> lib/.secrets.g.dart @@ -144,8 +94,6 @@ jobs: echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart - echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> lib/.secrets.g.dart - echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart echo "const anypayToken = '${{ secrets.ANY_PAY_TOKEN }}';" >> lib/.secrets.g.dart echo "const ioniaClientId = '${{ secrets.IONIA_CLIENT_ID }}';" >> lib/.secrets.g.dart @@ -156,8 +104,11 @@ jobs: echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> lib/.secrets.g.dart echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> lib/.secrets.g.dart + echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> lib/.secrets.g.dart + echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> lib/.secrets.g.dart echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart + echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart echo "const exolixApiKey = '${{ secrets.EXOLIX_API_KEY }}';" >> lib/.secrets.g.dart echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart @@ -165,7 +116,6 @@ jobs: echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> lib/.secrets.g.dart echo "const polygonScanApiKey = '${{ secrets.POLYGON_SCAN_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart - echo "const nowNodesApiKey = '${{ secrets.EVM_NOWNODES_API_KEY }}';" >> cw_evm/lib/.secrets.g.dart echo "const ankrApiKey = '${{ secrets.ANKR_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const chainStackApiKey = '${{ secrets.CHAIN_STACK_API_KEY }}';" >> cw_solana/lib/.secrets.g.dart echo "const testCakePayApiKey = '${{ secrets.TEST_CAKE_PAY_API_KEY }}';" >> lib/.secrets.g.dart @@ -178,39 +128,172 @@ jobs: echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart echo "const meldTestApiKey = '${{ secrets.MELD_TEST_API_KEY }}';" >> lib/.secrets.g.dart - echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dar + echo "const meldTestPublicKey = '${{ secrets.MELD_TEST_PUBLIC_KEY}}';" >> lib/.secrets.g.dart echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart + # tests + echo "const moneroTestWalletSeeds ='${{ secrets.MONERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const moneroLegacyTestWalletSeeds = '${{ secrets.MONERO_LEGACY_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const bitcoinTestWalletSeeds = '${{ secrets.BITCOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const ethereumTestWalletSeeds = '${{ secrets.ETHEREUM_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const litecoinTestWalletSeeds = '${{ secrets.LITECOIN_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const bitcoinCashTestWalletSeeds = '${{ secrets.BITCOIN_CASH_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const polygonTestWalletSeeds = '${{ secrets.POLYGON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const solanaTestWalletSeeds = '${{ secrets.SOLANA_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const tronTestWalletSeeds = '${{ secrets.TRON_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const nanoTestWalletSeeds = '${{ secrets.NANO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const wowneroTestWalletSeeds = '${{ secrets.WOWNERO_TEST_WALLET_SEEDS }}';" >> lib/.secrets.g.dart + echo "const moneroTestWalletReceiveAddress = '${{ secrets.MONERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const bitcoinTestWalletReceiveAddress = '${{ secrets.BITCOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const ethereumTestWalletReceiveAddress = '${{ secrets.ETHEREUM_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const litecoinTestWalletReceiveAddress = '${{ secrets.LITECOIN_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const bitcoinCashTestWalletReceiveAddress = '${{ secrets.BITCOIN_CASH_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const polygonTestWalletReceiveAddress = '${{ secrets.POLYGON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const solanaTestWalletReceiveAddress = '${{ secrets.SOLANA_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const tronTestWalletReceiveAddress = '${{ secrets.TRON_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const nanoTestWalletReceiveAddress = '${{ secrets.NANO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const wowneroTestWalletReceiveAddress = '${{ secrets.WOWNERO_TEST_WALLET_RECEIVE_ADDRESS }}';" >> lib/.secrets.g.dart + echo "const moneroTestWalletBlockHeight = '${{ secrets.MONERO_TEST_WALLET_BLOCK_HEIGHT }}';" >> lib/.secrets.g.dart + - name: prepare monero_c and cache + run: | + export MONEROC_HASH=$(cat scripts/prepare_moneroc.sh | grep 'git checkout' | xargs | awk '{ print $3 }') + echo MONEROC_HASH=$MONEROC_HASH >> /etc/environment + mkdir -p "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c" + pushd scripts + ln -s "$MONEROC_CACHE_DIR_ROOT/moneroc-$MONEROC_HASH/monero_c" + ./prepare_moneroc.sh + popd + pushd scripts/monero_c + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" || true + mkdir -p "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" || true + + rm -rf "$PWD/contrib/depends/built" "$PWD/monero/contrib/depends/built" "$PWD/wownero/contrib/depends/built" + rm -rf "$PWD/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources" + mkdir -p contrib/depends || true + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/built" "$PWD/contrib/depends/built" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/built" "$PWD/monero/contrib/depends/built" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/built" "$PWD/wownero/contrib/depends/built" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/contrib/depends/sources" "$PWD/contrib/depends/sources" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/monero/contrib/depends/sources" "$PWD/monero/contrib/depends/sources" + ln -sf "$MONEROC_CACHE_DIR_ROOT/_cache/wownero/contrib/depends/sources" "$PWD/wownero/contrib/depends/sources" + popd - - name: Rename app + - name: Execute Build and Setup Commands run: | - echo -e "id=com.cakewallet.test_${{ env.PR_NUMBER }}\nname=${{ env.BRANCH_NAME }}" > /opt/android/cake_wallet/android/app.properties + pushd scripts/linux + source ./app_env.sh cakewallet + ./app_config.sh + popd - - name: Build + - name: Build monero_c + run: | + pushd scripts/linux/ + source ./app_env.sh cakewallet + ./build_monero_all.sh + popd + + - name: Install Flutter dependencies + run: | + flutter pub get + + - name: Build generated code + run: | + ./model_generator.sh async + + - name: Generate localization + run: | + dart run tool/generate_localization.dart + + - name: Build linux run: | - cd /opt/android/cake_wallet flutter build linux --release - - name: Prepare release zip file + - name: Compress release run: | - cd /opt/android/cake_wallet/build/linux/x64/release - zip -r ${{env.BRANCH_NAME}}.zip bundle + pushd build/linux/x64/release + zip -r cakewallet_linux.zip bundle + popd - - name: Upload Artifact - uses: kittaakos/upload-artifact-as-is@v0 + - name: Upload Artifact to github + uses: actions/upload-artifact@v4 with: - path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip + path: ${{ github.workspace }}/build/linux/x64/release/cakewallet_linux.zip + name: cakewallet_linux + + - name: Prepare virtual desktop + if: ${{ contains(github.event.head_commit.message, 'run tests') }} + run: | + nohup Xvfb :99 -screen 0 720x1280x16 & + echo DISPLAY=:99 | sudo tee -a $GITHUB_ENV + dbus-daemon --system --fork + nohup NetworkManager & + nohup ffmpeg -framerate 60 -video_size 720x1280 -f x11grab -i :99 -c:v libx264 -c:a aac /opt/screen_grab.mkv & -# Just as an artifact would be enough -# - name: Send Test APK -# continue-on-error: true -# uses: adrey/slack-file-upload-action@1.0.5 -# with: -# token: ${{ secrets.SLACK_APP_TOKEN }} -# path: /opt/android/cake_wallet/build/linux/x64/release/${{env.BRANCH_NAME}}.zip -# channel: ${{ secrets.SLACK_APK_CHANNEL }} -# title: "${{ env.BRANCH_NAME }}_linux.zip" -# filename: ${{ env.BRANCH_NAME }}_linux.zip -# initial_comment: ${{ github.event.head_commit.message }} + # Note for people adding tests: + # - Tests are ran on Linux, with some things being mocked out. + # - Screen recording is being provided for the entire length of the test, you can download it in github articats. + # - Screen recordeding is encrypted, look at step "Stop screen recording, encrypt and upload", and add your key if you want + # Reason for encryption is the fact that we restore the wallet from seed, and we don't want to leak that, while there + # isn't much in those wallets anyway, we still wouldn't like to leak it to anyone who is able to access github. + + - name: Test [confirm_seeds_flow_test] + if: ${{ contains(github.event.head_commit.message, 'run tests') }} + timeout-minutes: 20 + run: | + xmessage -timeout 30 "confirm_seeds_flow_test" & + rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet + exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/confirm_seeds_flow_test.dart + - name: Test [create_wallet_flow_test] + if: ${{ contains(github.event.head_commit.message, 'run tests') }} + timeout-minutes: 20 + run: | + xmessage -timeout 30 "create_wallet_flow_test" & + rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet + exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/create_wallet_flow_test.dart + - name: Test [exchange_flow_test] + if: ${{ contains(github.event.head_commit.message, 'run tests') }} + timeout-minutes: 20 + run: | + xmessage -timeout 30 "exchange_flow_test" & + rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet + exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/exchange_flow_test.dart + - name: Test [restore_wallet_through_seeds_flow_test] + if: ${{ contains(github.event.head_commit.message, 'run tests') }} + timeout-minutes: 20 + run: | + xmessage -timeout 30 "restore_wallet_through_seeds_flow_test" & + rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet + exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart + - name: Stop screen recording, encrypt and upload + if: always() + run: | + if [[ ! -f "/opt/screen_grab.mkv" ]]; + then + exit 0; + fi + killall ffmpeg + sleep 5 + killall -9 ffmpeg || true + sleep 5 + # Feel free to add your own public key if you wish + gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 6B3199AD9B3D23B8 # konstantin@cakewallet.com + gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 35C8DBAFB8D9ACAC # cyjan@mrcyjanek.net + gpg --trust-model always --encrypt --output /opt/screen_grab.mkv.gpg \ + --recipient 6B3199AD9B3D23B8 \ + --recipient 35C8DBAFB8D9ACAC \ + /opt/screen_grab.mkv + rm /opt/screen_grab.mkv + mv /opt/screen_grab.mkv.gpg ./screen_grab.mkv.gpg + - name: Upload Artifact to github + if: always() + continue-on-error: true + uses: actions/upload-artifact@v4 + with: + path: ${{ github.workspace }}/screen_grab.mkv.gpg + name: tests_screen_grab diff --git a/build-guide-linux.md b/build-guide-linux.md index 99c2ed0c84..df5f0f6017 100644 --- a/build-guide-linux.md +++ b/build-guide-linux.md @@ -115,7 +115,7 @@ Install Flutter package dependencies with this command: > `$ ./cakewallet.sh` > and back to project root directory: > `$ cd ../..` -> and fetch dependecies again +> and fetch dependencies again > `$ flutter pub get` Your CakeWallet binary will be built with some specific keys for iterate with 3rd party services. You may generate these secret keys placeholders with the following command: diff --git a/build-guide-win.md b/build-guide-win.md index 6ace961af5..8cfd02c4ca 100644 --- a/build-guide-win.md +++ b/build-guide-win.md @@ -16,14 +16,14 @@ These steps will help you configure and execute a build of CakeWallet from its s ### 1. Installing Package Dependencies For build CakeWallet windows application from sources you will be needed to have: -> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windwos`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`. +> [Install Flutter]Follow installation guide (https://docs.flutter.dev/get-started/install/windows) and install do not miss to dev tools (install https://docs.flutter.dev/get-started/install/windows/desktop#development-tools) which are required for windows desktop development (need to install Git for Windows and Visual Studio 2022). Then install `Desktop development with C++` packages via GUI Visual Studio 2022, or Visual Studio Build Tools 2022 including: `C++ Build Tools core features`, `C++ 2022 Redistributable Update`, `C++ core desktop features`, `MVC v143 - VS 2022 C++ x64/x86 build tools`, `C++ CMake tools for Windows`, `Testing tools core features - Build Tools`, `C++ AddressSanitizer`. > [Install WSL] for building monero dependencies need to install Windows WSL (https://learn.microsoft.com/en-us/windows/wsl/install) and required packages for WSL (Ubuntu): `$ sudo apt update ` `$ sudo apt build-essential cmake gcc-mingw-w64 g++-mingw-w64 autoconf libtool pkg-config` ### 2. Pull CakeWallet source code -You can downlaod CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command: +You can download CakeWallet source code from our [GitHub repository](github.com/cake-tech/cake_wallet) via git by following next command: `$ git clone https://github.com/cake-tech/cake_wallet.git --branch MrCyjaneK-cyjan-monerodart` OR you can download it as [Zip archive](https://github.com/cake-tech/cake_wallet/archive/refs/heads/MrCyjaneK-cyjan-monerodart.zip) diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 537c9802e7..2a343d430a 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -9,6 +9,8 @@ import 'package:flutter/foundation.dart'; import 'package:monero/monero.dart' as monero; import 'package:mutex/mutex.dart'; +bool debugMonero = false; + int getSyncingHeight() { // final height = monero.MONERO_cw_WalletListener_height(getWlptr()); final h2 = monero.Wallet_blockChainHeight(wptr!); @@ -132,7 +134,7 @@ Future setupNodeSync( } } - if (kDebugMode) { + if (kDebugMode && debugMonero) { monero.Wallet_init3( wptr!, argv0: '', defaultLogBaseName: 'moneroc', diff --git a/integration_test/components/common_test_cases.dart b/integration_test/components/common_test_cases.dart index 83bbb0449c..cc1e6d6d71 100644 --- a/integration_test/components/common_test_cases.dart +++ b/integration_test/components/common_test_cases.dart @@ -32,6 +32,11 @@ class CommonTestCases { expect(textWidget, hasWidget ? findsOneWidget : findsNothing); } + void hasTextAtLestOnce(String text, {bool hasWidget = true}) { + final textWidget = find.text(text); + expect(textWidget, hasWidget ? findsAny : findsNothing); + } + void hasType() { final typeWidget = find.byType(T); expect(typeWidget, findsOneWidget); diff --git a/integration_test/components/common_test_flows.dart b/integration_test/components/common_test_flows.dart index 8350b58590..c9e6053393 100644 --- a/integration_test/components/common_test_flows.dart +++ b/integration_test/components/common_test_flows.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/reactions/bip39_wallet_utils.dart'; import 'package:cake_wallet/wallet_types.g.dart'; @@ -85,6 +87,7 @@ class CommonTestFlows { await _confirmPreSeedInfo(); await _confirmWalletDetails(); + await _commonTestCases.defaultSleepTime(); } //* ========== Handles flow from welcome to restoring wallet from seeds =============== @@ -168,8 +171,8 @@ class CommonTestFlows { await _walletListPageRobot.navigateToRestoreWalletOptionsPage(); await _commonTestCases.defaultSleepTime(); - await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage(); - await _commonTestCases.defaultSleepTime(); + if (!Platform.isLinux) await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage(); + if (!Platform.isLinux) await _commonTestCases.defaultSleepTime(); await _selectWalletTypeForWallet(walletType); await _commonTestCases.defaultSleepTime(); @@ -180,6 +183,7 @@ class CommonTestFlows { //* ========== Handles setting up pin code for wallet on first install =============== Future setupPinCodeForWallet(List pin) async { + if (Platform.isLinux) return; // ----------- SetupPinCode Page ------------- // Confirm initial defaults - Widgets to be displayed etc await _setupPinCodeRobot.isSetupPinCodePage(); @@ -212,7 +216,7 @@ class CommonTestFlows { await _welcomePageRobot.navigateToRestoreWalletPage(); - await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage(); + if (!Platform.isLinux) await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage(); await _selectWalletTypeForWallet(walletTypeToRestore); } @@ -234,6 +238,12 @@ class CommonTestFlows { await _newWalletPageRobot.generateWalletName(); + if (Platform.isLinux) { + // manual pin input + await _restoreFromSeedOrKeysPageRobot.enterPasswordForWalletRestore(CommonTestConstants.pin.join("")); + await _restoreFromSeedOrKeysPageRobot.enterPasswordRepeatForWalletRestore(CommonTestConstants.pin.join("")); + } + await _newWalletPageRobot.onNextButtonPressed(); } @@ -252,11 +262,15 @@ class CommonTestFlows { _walletSeedPageRobot.confirmWalletSeedReminderDisplays(); - await _walletSeedPageRobot.onCopySeedsButtonPressed(); - - await _walletSeedPageRobot.onNextButtonPressed(); + // await _walletSeedPageRobot.onCopySeedsButtonPressed(); - await _walletSeedPageRobot.onConfirmButtonOnSeedAlertDialogPressed(); + await _walletSeedPageRobot.onSeedPageVerifyButtonPressed(); + // Turns out the popup about "Copied to clipboard" prevents + //the button from being pressed on the first try, by just + //tapping it again we fix it. + // await _walletSeedPageRobot.onSeedPageVerifyButtonPressed(); + + await _walletSeedPageRobot.onOpenWalletButtonPressed(); } //* Main Restore Actions - On the RestoreFromSeed/Keys Page - Restore from Seeds Action @@ -277,6 +291,12 @@ class CommonTestFlows { .enterBlockHeightForWalletRestore(secrets.moneroTestWalletBlockHeight); } + if (Platform.isLinux) { + // manual pin input + await _restoreFromSeedOrKeysPageRobot.enterPasswordForWalletRestore(CommonTestConstants.pin.join("")); + await _restoreFromSeedOrKeysPageRobot.enterPasswordRepeatForWalletRestore(CommonTestConstants.pin.join("")); + } + await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); } diff --git a/integration_test/funds_related_tests.dart b/integration_test/funds_related_tests.dart index db24fbc0b8..27187dc2fa 100644 --- a/integration_test/funds_related_tests.dart +++ b/integration_test/funds_related_tests.dart @@ -67,6 +67,11 @@ void main() { await authPageRobot.enterPinCode(CommonTestConstants.pin); } + final onAuthPageDesktop = authPageRobot.onAuthPageDesktop(); + if (onAuthPageDesktop) { + await authPageRobot.enterPassword(CommonTestConstants.pin.join("")); + } + // ----------- Exchange Confirm Page ------------- await exchangeConfirmPageRobot.isExchangeConfirmPage(); diff --git a/integration_test/robots/auth_page_robot.dart b/integration_test/robots/auth_page_robot.dart index 6358d43980..2f5c436273 100644 --- a/integration_test/robots/auth_page_robot.dart +++ b/integration_test/robots/auth_page_robot.dart @@ -20,6 +20,11 @@ class AuthPageRobot extends PinCodeWidgetRobot { return hasPin; } + bool onAuthPageDesktop() { + final hasWalletPasswordInput = find.byKey(ValueKey('enter_wallet_password')); + return hasWalletPasswordInput.tryEvaluate(); + } + Future isAuthPage() async { await commonTestCases.isSpecificPage(); } diff --git a/integration_test/robots/pin_code_widget_robot.dart b/integration_test/robots/pin_code_widget_robot.dart index 18dc5fba46..62e606703d 100644 --- a/integration_test/robots/pin_code_widget_robot.dart +++ b/integration_test/robots/pin_code_widget_robot.dart @@ -24,6 +24,20 @@ class PinCodeWidgetRobot { commonTestCases.hasValueKey('pin_code_button_0_key'); } + Future enterPassword(String password, {int pumpDuration = 100}) async { + await commonTestCases.enterText( + password, + 'enter_wallet_password', + ); + await tester.pumpAndSettle(); + await commonTestCases.tapItemByKey( + 'unlock', + ); + await tester.pumpAndSettle(); + + await commonTestCases.defaultSleepTime(); + } + Future enterPinCode(List pinCode, {int pumpDuration = 100}) async { for (int pin in pinCode) { await commonTestCases.tapItemByKey( diff --git a/integration_test/robots/restore_from_seed_or_key_robot.dart b/integration_test/robots/restore_from_seed_or_key_robot.dart index 9d973061bf..015a9e46ff 100644 --- a/integration_test/robots/restore_from_seed_or_key_robot.dart +++ b/integration_test/robots/restore_from_seed_or_key_robot.dart @@ -1,7 +1,9 @@ import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; +import 'package:cake_wallet/src/widgets/seed_widget.dart'; import 'package:cake_wallet/src/widgets/validable_annotated_editable_text.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import '../components/common_test_cases.dart'; @@ -65,12 +67,28 @@ class RestoreFromSeedOrKeysPageRobot { Future enterSeedPhraseForWalletRestore(String text) async { ValidatableAnnotatedEditableTextState seedTextState = - await tester.state(find.byType(ValidatableAnnotatedEditableText)); + await tester.state(find.byType(ValidatableAnnotatedEditableText)); seedTextState.widget.controller.text = text; await tester.pumpAndSettle(); } + Future enterPasswordForWalletRestore(String text) async { + await commonTestCases.enterText( + text, + 'password', + ); + await tester.pumpAndSettle(); + } + + Future enterPasswordRepeatForWalletRestore(String text) async { + await commonTestCases.enterText( + text, + 'repeat_wallet_password', + ); + await tester.pumpAndSettle(); + } + Future enterBlockHeightForWalletRestore(String blockHeight) async { await commonTestCases.enterText( blockHeight, diff --git a/integration_test/robots/send_page_robot.dart b/integration_test/robots/send_page_robot.dart index f8e1a49adc..b705c803f0 100644 --- a/integration_test/robots/send_page_robot.dart +++ b/integration_test/robots/send_page_robot.dart @@ -183,32 +183,15 @@ class SendPageRobot { } Future _handleAuthPage() async { - tester.printToConsole('Inside _handleAuth'); - await tester.pump(); - tester.printToConsole('starting auth checks'); - - final authPage = authPageRobot.onAuthPage(); - - tester.printToConsole('hasAuth:$authPage'); - - if (authPage) { - await tester.pump(); - tester.printToConsole('Starting inner _handleAuth loop checks'); - - try { - await authPageRobot.enterPinCode(CommonTestConstants.pin, pumpDuration: 500); - tester.printToConsole('Auth done'); - - await tester.pump(); + final onAuthPage = authPageRobot.onAuthPage(); + if (onAuthPage) { + await authPageRobot.enterPinCode(CommonTestConstants.pin); + } - tester.printToConsole('Auth pump done'); - } catch (e) { - tester.printToConsole('Auth failed, retrying'); - await tester.pump(); - _handleAuthPage(); - } + final onAuthPageDesktop = authPageRobot.onAuthPageDesktop(); + if (onAuthPageDesktop) { + await authPageRobot.enterPassword(CommonTestConstants.pin.join("")); } - await tester.pump(); } Future handleSendResult() async { @@ -221,7 +204,7 @@ class SendPageRobot { tester.printToConsole('Has an Error in the handle: $hasError'); - int maxRetries = 20; + int maxRetries = 3; int retries = 0; while (hasError && retries < maxRetries) { diff --git a/integration_test/robots/wallet_seed_page_robot.dart b/integration_test/robots/wallet_seed_page_robot.dart index d52f3b1ec9..576bff0d62 100644 --- a/integration_test/robots/wallet_seed_page_robot.dart +++ b/integration_test/robots/wallet_seed_page_robot.dart @@ -14,8 +14,13 @@ class WalletSeedPageRobot { await commonTestCases.isSpecificPage(); } - Future onNextButtonPressed() async { - await commonTestCases.tapItemByKey('wallet_seed_page_next_button_key'); + Future onSeedPageVerifyButtonPressed() async { + await commonTestCases.tapItemByKey('wallet_seed_page_verify_seed_button_key'); + await commonTestCases.defaultSleepTime(); + } + + Future onOpenWalletButtonPressed() async { + await commonTestCases.tapItemByKey('wallet_seed_page_open_wallet_button_key'); await commonTestCases.defaultSleepTime(); } @@ -38,11 +43,14 @@ class WalletSeedPageRobot { final walletSeeds = walletSeedViewModel.seed; commonTestCases.hasText(walletName); - commonTestCases.hasText(walletSeeds); + final seedList = walletSeeds.trim().split(" "); + for (final seedWord in seedList) { + commonTestCases.hasTextAtLestOnce(seedWord); + } } void confirmWalletSeedReminderDisplays() { - commonTestCases.hasText(S.current.seed_reminder); + commonTestCases.hasText(S.current.cake_seeds_save_disclaimer); } Future onSaveSeedsButtonPressed() async { diff --git a/integration_test/test_suites/confirm_seeds_flow_test.dart b/integration_test/test_suites/confirm_seeds_flow_test.dart index 2d11a2cc49..a62ce3f604 100644 --- a/integration_test/test_suites/confirm_seeds_flow_test.dart +++ b/integration_test/test_suites/confirm_seeds_flow_test.dart @@ -1,9 +1,13 @@ +import 'dart:io'; + import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import '../components/common_test_cases.dart'; import '../components/common_test_constants.dart'; import '../components/common_test_flows.dart'; import '../robots/auth_page_robot.dart'; @@ -95,6 +99,10 @@ Future _confirmSeedsFlowForWalletType( await authPageRobot.enterPinCode(CommonTestConstants.pin); } + final onAuthPageDesktop = authPageRobot.onAuthPageDesktop(); + if (onAuthPageDesktop) { + await authPageRobot.enterPassword(CommonTestConstants.pin.join("")); + } await tester.pumpAndSettle(); await walletKeysAndSeedPageRobot.isWalletKeysAndSeedPage(); diff --git a/integration_test/test_suites/exchange_flow_test.dart b/integration_test/test_suites/exchange_flow_test.dart index c36ef93962..8ec2e54e77 100644 --- a/integration_test/test_suites/exchange_flow_test.dart +++ b/integration_test/test_suites/exchange_flow_test.dart @@ -56,6 +56,10 @@ void main() { await authPageRobot.enterPinCode(CommonTestConstants.pin); } + final onAuthPageDesktop = authPageRobot.onAuthPageDesktop(); + if (onAuthPageDesktop) { + await authPageRobot.enterPassword(CommonTestConstants.pin.join("")); + } await exchangeConfirmPageRobot.onSavedTradeIdButtonPressed(); await exchangeTradePageRobot.onGotItButtonPressed(); }); diff --git a/integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart b/integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart index a810aa7221..0589d16ba8 100644 --- a/integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart +++ b/integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart @@ -1,4 +1,7 @@ +import 'dart:io'; + import 'package:cake_wallet/wallet_types.g.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/integration_test_runner.sh b/integration_test_runner.sh index 34c9227c06..86e28f0b85 100755 --- a/integration_test_runner.sh +++ b/integration_test_runner.sh @@ -1,4 +1,5 @@ #!/bin/bash +export DESKTOP_FORCE_MOBILE="Y" declare -a targets declare -a passed_tests @@ -12,6 +13,10 @@ done < <(find integration_test/test_suites -name "*.dart" -type f -print0) # Run each test and collect results for target in "${targets[@]}" do + if [[ "x$REMOVE_DATA_DIRECTORY" == "xY" ]]; + then + rm -rf ~/.local/share/com.example.cake_wallet ~/Documents/cake_wallet + fi echo "Running test: $target" if flutter drive \ --driver=test_driver/integration_test.dart \ diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index f101ed7e10..03f20363d5 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -293,6 +293,7 @@ class BackupService { final lookupsUnstoppableDomains = data[PreferencesKey.lookupsUnstoppableDomains] as bool?; final lookupsOpenAlias = data[PreferencesKey.lookupsOpenAlias] as bool?; final lookupsENS = data[PreferencesKey.lookupsENS] as bool?; + final lookupsWellKnown = data[PreferencesKey.lookupsWellKnown] as bool?; final syncAll = data[PreferencesKey.syncAllKey] as bool?; final syncMode = data[PreferencesKey.syncModeKey] as int?; final autoGenerateSubaddressStatus = @@ -403,6 +404,9 @@ class BackupService { if (lookupsENS != null) await _sharedPreferences.setBool(PreferencesKey.lookupsENS, lookupsENS); + if (lookupsWellKnown != null) + await _sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, lookupsWellKnown); + if (syncAll != null) await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll); if (syncMode != null) await _sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode); @@ -542,6 +546,8 @@ class BackupService { _sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains), PreferencesKey.lookupsOpenAlias: _sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias), PreferencesKey.lookupsENS: _sharedPreferences.getBool(PreferencesKey.lookupsENS), + PreferencesKey.lookupsWellKnown: + _sharedPreferences.getBool(PreferencesKey.lookupsWellKnown), PreferencesKey.syncModeKey: _sharedPreferences.getInt(PreferencesKey.syncModeKey), PreferencesKey.syncAllKey: _sharedPreferences.getBool(PreferencesKey.syncAllKey), PreferencesKey.autoGenerateSubaddressStatusKey: diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index b13dfa9ad4..9be1250812 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/openalias_record.dart'; import 'package:cake_wallet/entities/parsed_address.dart'; import 'package:cake_wallet/entities/unstoppable_domain_address.dart'; import 'package:cake_wallet/entities/emoji_string_extension.dart'; +import 'package:cake_wallet/entities/wellknown_record.dart'; import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart'; import 'package:cake_wallet/mastodon/mastodon_api.dart'; import 'package:cake_wallet/nostr/nostr_api.dart'; @@ -208,6 +209,17 @@ class AddressResolver { } } + // .well-known scheme: + if (text.contains('.') && text.contains('@')) { + if (settingsStore.lookupsWellKnown) { + final record = + await WellKnownRecord.fetchAddressAndName(formattedName: text, currency: currency); + if (record != null) { + return ParsedAddress.fetchWellKnownAddress(address: record.address, name: text); + } + } + } + if (!text.startsWith('@') && text.contains('@') && !text.contains('.')) { final bool isFioRegistered = await FioAddressProvider.checkAvail(text); if (isFioRegistered) { diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart index cfd69acbe3..eabc606db7 100644 --- a/lib/entities/parsed_address.dart +++ b/lib/entities/parsed_address.dart @@ -12,7 +12,8 @@ enum ParseFrom { contact, mastodon, nostr, - thorChain + thorChain, + wellKnown } class ParsedAddress { @@ -142,6 +143,14 @@ class ParsedAddress { ); } + factory ParsedAddress.fetchWellKnownAddress({required String address, required String name}) { + return ParsedAddress( + addresses: [address], + name: name, + parseFrom: ParseFrom.wellKnown, + ); + } + final List addresses; final String name; final String description; diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 58a5402786..4955690e2e 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -76,6 +76,7 @@ class PreferencesKey { static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain'; static const lookupsOpenAlias = 'looks_up_open_alias'; static const lookupsENS = 'looks_up_ens'; + static const lookupsWellKnown = 'looks_up_well_known'; static const showCameraConsent = 'show_camera_consent'; static String moneroWalletUpdateV1Key(String name) => diff --git a/lib/entities/wellknown_record.dart b/lib/entities/wellknown_record.dart new file mode 100644 index 0000000000..dbe808281c --- /dev/null +++ b/lib/entities/wellknown_record.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/utils/print_verbose.dart'; +import 'package:http/http.dart' as http; + +class WellKnownRecord { + WellKnownRecord({ + required this.address, + required this.name, + }); + + final String name; + final String address; + + static Future checkWellKnownUsername(String username, CryptoCurrency currency) async { + String jsonLocation = ""; + switch (currency) { + case CryptoCurrency.nano: + jsonLocation = "nano-currency"; + break; + // TODO: add other currencies + default: + return null; + } + + // split the string by the @ symbol: + try { + final List splitStrs = username.split("@"); + String name = splitStrs.first.toLowerCase(); + final String domain = splitStrs.last; + + if (splitStrs.length == 3) { + // for username like @alice@domain.org instead of alice@domain.org + name = splitStrs[1]; + } + + if (name.isEmpty) { + name = "_"; + } + + // lookup domain/.well-known/nano-currency.json and check if it has a nano address: + final http.Response response = await http.get( + Uri.parse("https://$domain/.well-known/$jsonLocation.json?names=$name"), + headers: {"Accept": "application/json"}, + ); + + if (response.statusCode != 200) { + return null; + } + final Map decoded = json.decode(response.body) as Map; + + // Access the first element in the names array and retrieve its address + final List names = decoded["names"] as List; + for (final dynamic item in names) { + if (item["name"].toLowerCase() == name) { + return item["address"] as String; + } + } + } catch (e) { + printV("error checking well-known username: $e"); + } + return null; + } + + static String formatDomainName(String name) { + String formattedName = name; + + if (name.contains("@")) { + formattedName = name.replaceAll("@", "."); + } + + return formattedName; + } + + static Future fetchAddressAndName({ + required String formattedName, + required CryptoCurrency currency, + }) async { + String name = formattedName; + + printV("formattedName: $formattedName"); + + final address = await checkWellKnownUsername(formattedName, currency); + + if (address == null) { + return null; + } + + return WellKnownRecord(address: address, name: name); + } +} diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index e63a01f61c..e2d5a953c0 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -221,6 +221,7 @@ class _WalletNameFormState extends State { ), if (_walletNewVM.hasWalletPassword) ...[ TextFormField( + key: ValueKey('password'), onChanged: (value) => _walletNewVM.walletPassword = value, controller: _passwordController, textAlign: TextAlign.center, @@ -257,6 +258,7 @@ class _WalletNameFormState extends State { ), ), TextFormField( + key: ValueKey('repeat_wallet_password'), onChanged: (value) => _walletNewVM.repeatedWalletPassword = value, controller: _repeatedPasswordController, textAlign: TextAlign.center, diff --git a/lib/src/screens/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart index 83772f866c..0c9af69103 100644 --- a/lib/src/screens/restore/wallet_restore_from_keys_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -148,12 +148,14 @@ class WalletRestoreFromKeysFromState extends State { ), if (widget.displayWalletPassword) ...[Container( + key: ValueKey('password'), padding: EdgeInsets.only(top: 20.0), child: BaseTextFormField( controller: passwordTextEditingController, hintText: S.of(context).password, obscureText: true)), Container( + key: ValueKey('repeat_wallet_password'), padding: EdgeInsets.only(top: 20.0), child: BaseTextFormField( controller: repeatedPasswordTextEditingController, diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index 1684f6f925..6a493087bf 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -223,12 +223,14 @@ class WalletRestoreFromSeedFormState extends State { ), if (widget.displayWalletPassword) ...[BaseTextFormField( + key: ValueKey('password'), controller: passwordTextEditingController, hintText: S .of(context) .password, obscureText: true), BaseTextFormField( + key: ValueKey('repeat_wallet_password'), controller: repeatedPasswordTextEditingController, hintText: S .of(context) diff --git a/lib/src/screens/send/widgets/extract_address_from_parsed.dart b/lib/src/screens/send/widgets/extract_address_from_parsed.dart index 9ce3ca2b11..106be97ee8 100644 --- a/lib/src/screens/send/widgets/extract_address_from_parsed.dart +++ b/lib/src/screens/send/widgets/extract_address_from_parsed.dart @@ -30,6 +30,11 @@ Future extractAddressFromParsed( content = S.of(context).extracted_address_content('${parsedAddress.name} (OpenAlias)'); address = parsedAddress.addresses.first; break; + case ParseFrom.wellKnown: + title = S.of(context).address_detected; + content = S.of(context).extracted_address_content('${parsedAddress.name} (Well-Known)'); + address = parsedAddress.addresses.first; + break; case ParseFrom.fio: title = S.of(context).address_detected; content = S.of(context).extracted_address_content('${parsedAddress.name} (FIO)'); diff --git a/lib/src/screens/settings/domain_lookups_page.dart b/lib/src/screens/settings/domain_lookups_page.dart index aa7e68cd0c..0eb559817a 100644 --- a/lib/src/screens/settings/domain_lookups_page.dart +++ b/lib/src/screens/settings/domain_lookups_page.dart @@ -45,6 +45,10 @@ class DomainLookupsPage extends BasePage { title: 'Ethereum Name Service', value: _privacySettingsViewModel.looksUpENS, onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsENS(value)), + SettingsSwitcherCell( + title: '.well-known', + value: _privacySettingsViewModel.looksUpWellKnown, + onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsWellKnown(value)), //if (!isHaven) it does not work correctly ], diff --git a/lib/src/screens/wallet_unlock/wallet_unlock_page.dart b/lib/src/screens/wallet_unlock/wallet_unlock_page.dart index 3e6966f9d2..4afbfe2c17 100644 --- a/lib/src/screens/wallet_unlock/wallet_unlock_page.dart +++ b/lib/src/screens/wallet_unlock/wallet_unlock_page.dart @@ -170,6 +170,7 @@ class WalletUnlockPageState extends AuthPageState { SizedBox(height: 24), Form( child: TextFormField( + key: ValueKey('enter_wallet_password'), onChanged: (value) => null, controller: _passwordController, textAlign: TextAlign.center, @@ -205,6 +206,7 @@ class WalletUnlockPageState extends AuthPageState { ), ), Padding( + key: ValueKey('unlock'), padding: EdgeInsets.only(bottom: 24), child: Observer( builder: (_) => LoadingPrimaryButton( diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index aa7df4ba96..318be637e5 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -115,6 +115,7 @@ abstract class SettingsStoreBase with Store { required this.lookupsUnstoppableDomains, required this.lookupsOpenAlias, required this.lookupsENS, + required this.lookupsWellKnown, required this.customBitcoinFeeRate, required this.silentPaymentsCardDisplay, required this.silentPaymentsAlwaysScan, @@ -459,6 +460,11 @@ abstract class SettingsStoreBase with Store { reaction((_) => lookupsENS, (bool looksUpENS) => _sharedPreferences.setBool(PreferencesKey.lookupsENS, looksUpENS)); + reaction( + (_) => lookupsWellKnown, + (bool looksUpWellKnown) => + _sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, looksUpWellKnown)); + // secure storage keys: reaction( (_) => allowBiometricalAuthentication, @@ -772,6 +778,8 @@ abstract class SettingsStoreBase with Store { @observable bool lookupsENS; + @observable + bool lookupsWellKnown; @observable SyncMode currentSyncMode; @@ -967,6 +975,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true; final lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true; final lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; + final lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true; final customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1; final silentPaymentsCardDisplay = sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; @@ -1245,6 +1254,7 @@ abstract class SettingsStoreBase with Store { lookupsUnstoppableDomains: lookupsUnstoppableDomains, lookupsOpenAlias: lookupsOpenAlias, lookupsENS: lookupsENS, + lookupsWellKnown: lookupsWellKnown, customBitcoinFeeRate: customBitcoinFeeRate, silentPaymentsCardDisplay: silentPaymentsCardDisplay, silentPaymentsAlwaysScan: silentPaymentsAlwaysScan, @@ -1414,6 +1424,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains) ?? true; lookupsOpenAlias = sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias) ?? true; lookupsENS = sharedPreferences.getBool(PreferencesKey.lookupsENS) ?? true; + lookupsWellKnown = sharedPreferences.getBool(PreferencesKey.lookupsWellKnown) ?? true; customBitcoinFeeRate = sharedPreferences.getInt(PreferencesKey.customBitcoinFeeRate) ?? 1; silentPaymentsCardDisplay = sharedPreferences.getBool(PreferencesKey.silentPaymentsCardDisplay) ?? true; diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index eaa9f9e840..67f0d88a03 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -94,6 +94,9 @@ abstract class PrivacySettingsViewModelBase with Store { @computed bool get looksUpENS => _settingsStore.lookupsENS; + @computed + bool get looksUpWellKnown => _settingsStore.lookupsWellKnown; + bool get canUseEtherscan => _wallet.type == WalletType.ethereum; bool get canUsePolygonScan => _wallet.type == WalletType.polygon; @@ -130,6 +133,9 @@ abstract class PrivacySettingsViewModelBase with Store { @action void setLookupsENS(bool value) => _settingsStore.lookupsENS = value; + @action + void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value; + @action void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value; diff --git a/linux/my_application.cc b/linux/my_application.cc index 49ed75499b..49f9ae139a 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -46,8 +46,11 @@ static void my_application_activate(GApplication* application) { } else { gtk_window_set_title(window, "Cake Wallet"); } - - gtk_window_set_default_size(window, 1280, 720); + if (getenv("DESKTOP_FORCE_MOBILE")) { + gtk_window_set_default_size(window, 720, 1280); + } else { + gtk_window_set_default_size(window, 1280, 720); + } gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); diff --git a/model_generator.sh b/model_generator.sh index 730817c24f..1443b0fc98 100755 --- a/model_generator.sh +++ b/model_generator.sh @@ -1,18 +1,24 @@ #!/bin/bash set -x -e -cd cw_core; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_evm; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_monero; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_bitcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_haven; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_nano; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_bitcoin_cash; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_solana; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_tron; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_wownero; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .. -cd cw_polygon; flutter pub get; cd .. -cd cw_ethereum; flutter pub get; cd .. -cd cw_mweb && flutter pub get && cd .. -dart run build_runner build --delete-conflicting-outputs +for cwcoin in cw_{core,evm,monero,bitcoin,haven,nano,bitcoin_cash,solana,tron,wownero} +do + if [[ "x$1" == "xasync" ]]; + then + bash -c "cd $cwcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .." & + else + bash -c "cd $cwcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd .." + fi +done +for cwcoin in cw_{polygon,ethereum,mwebd}; +do + if [[ "x$1" == "xasync" ]]; + then + bash -c "cd $cwcoin; flutter pub get; cd .." & + else + bash -c "cd $cwcoin; flutter pub get; cd .." + fi +done +flutter pub get +dart run build_runner build --delete-conflicting-outputs diff --git a/pubspec_base.yaml b/pubspec_base.yaml index e87b5a44ed..450977a40b 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -107,7 +107,10 @@ dependencies: polyseed: ^0.0.6 nostr_tools: ^1.0.9 solana: ^0.31.0+1 - ledger_flutter_plus: ^1.4.1 + ledger_flutter_plus: + git: + url: https://github.com/vespr-wallet/ledger-flutter-plus + ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76 hashlib: ^1.19.2 dev_dependencies: @@ -146,6 +149,10 @@ dependency_overrides: url: https://github.com/cake-tech/bitcoin_base ref: cake-update-v9 ffi: 2.1.0 + ledger_flutter_plus: + git: + url: https://github.com/vespr-wallet/ledger-flutter-plus + ref: c2e341d8038f1108690ad6f80f7b4b7156aacc76 flutter_icons: image_path: "assets/images/app_logo.png" diff --git a/scripts/android/.gitignore b/scripts/android/.gitignore new file mode 100644 index 0000000000..f7e94b7c08 --- /dev/null +++ b/scripts/android/.gitignore @@ -0,0 +1 @@ +mwebd \ No newline at end of file diff --git a/scripts/android/build_monero_all.sh b/scripts/android/build_monero_all.sh index 261ebd5601..71a6b62282 100755 --- a/scripts/android/build_monero_all.sh +++ b/scripts/android/build_monero_all.sh @@ -8,50 +8,20 @@ cd "$(dirname "$0")" NPROC="-j$(nproc)" -if [[ "x$(uname)" == "xDarwin" ]]; -then - USE_DOCKER="ON" - NPROC="-j1" -fi - ../prepare_moneroc.sh -if [[ ! "x$RUNNER_OS" == "x" ]]; -then - REMOVE_CACHES=ON -fi - -# NOTE: -j1 is intentional. Otherwise you will run into weird behaviour on macos -if [[ ! "x$USE_DOCKER" == "x" ]]; -then - for COIN in monero wownero; - do - pushd ../monero_c - docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} x86_64-linux-android $NPROC" - # docker run --platform linux/amd64 -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} i686-linux-android $NPROC" - docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} armv7a-linux-androideabi $NPROC" - docker run --platform linux/amd64 -v$HOME/.cache/ccache:/root/.ccache -v$PWD:$PWD -w $PWD --rm -it git.mrcyjanek.net/mrcyjanek/debian:buster bash -c "git config --global --add safe.directory '*'; apt update; apt install -y ccache gcc g++ libtinfo5 gperf; ./build_single.sh ${COIN} aarch64-linux-android $NPROC" - popd - done -else - for COIN in monero wownero; - do - pushd ../monero_c - env -i ./build_single.sh ${COIN} x86_64-linux-android $NPROC - [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/x86_64-linux-android - # ./build_single.sh ${COIN} i686-linux-android $NPROC - # [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/i686-linux-android - env -i ./build_single.sh ${COIN} armv7a-linux-androideabi $NPROC - [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/armv7a-linux-androideabi - env -i ./build_single.sh ${COIN} aarch64-linux-android $NPROC - [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/aarch64-linux-android - - popd - unxz -f ../monero_c/release/${COIN}/x86_64-linux-android_libwallet2_api_c.so.xz - - unxz -f ../monero_c/release/${COIN}/armv7a-linux-androideabi_libwallet2_api_c.so.xz - - unxz -f ../monero_c/release/${COIN}/aarch64-linux-android_libwallet2_api_c.so.xz - [[ ! "x$REMOVE_CACHES" == "x" ]] && rm -rf ${COIN}/contrib/depends/{built,sources} - done -fi +for COIN in monero wownero; +do + pushd ../monero_c + for target in {x86_64,aarch64}-linux-android armv7a-linux-androideabi + do + if [[ -f "release/${COIN}/${target}_libwallet2_api_c.so" ]]; + then + echo "file exist, not building monero_c for ${COIN}/$target."; + else + env -i ./build_single.sh ${COIN} $target $NPROC + unxz -f ../monero_c/release/${COIN}/${target}_libwallet2_api_c.so.xz + fi + done + popd +done \ No newline at end of file diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index 4434e30f1e..cd4e2c1f49 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -16,7 +16,4 @@ cd mwebd git reset --hard 555349415f76a42ec5c76152b64c4ab9aabc448f gomobile bind -target=android -androidapi 21 . mkdir -p ../../../cw_mweb/android/libs/ -mv ./mwebd.aar $_ -# cleanup: -cd .. -rm -rf mwebd \ No newline at end of file +cp ./mwebd.aar $_ \ No newline at end of file diff --git a/scripts/linux/Dockerfile.linux b/scripts/linux/Dockerfile.linux new file mode 100644 index 0000000000..c8f4d3bdef --- /dev/null +++ b/scripts/linux/Dockerfile.linux @@ -0,0 +1,148 @@ +# Usage: +# docker build . -f Dockerfile.linux -t ghcr.io/cake-tech/cake_wallet:main-linux +# docker push ghcr.io/cake-tech/cake_wallet:main-linux + +FROM --platform=linux/amd64 docker.io/debian:12 + +LABEL org.opencontainers.image.source=https://github.com/cake-tech/cake_wallet + +ENV GOLANG_VERSION=1.23.4 +# comes from https://developer.android.com/studio/#command-tools +ENV ANDROID_SDK_TOOLS_VERSION=11076708 +# https://developer.android.com/studio/releases/build-tools +ENV ANDROID_PLATFORM_VERSION=34 +ENV ANDROID_BUILD_TOOLS_VERSION=34.0.0 + +ENV FLUTTER_VERSION=3.24.0 + +# If we ever need to migrate the home directory... +RUN sed -i 's|^root:[^:]*:[^:]*:[^:]*:[^:]*:/root:|root:x:0:0:root:/root:|' /etc/passwd +# mkdir -p /root && rm -rf /root && cp -a /root /root +ENV HOME=/root +# Heavily inspired by cirrusci images +# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile +# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/34/Dockerfile +# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/34-ndk/Dockerfile +# https://github.com/cirruslabs/docker-images-flutter/blob/master/sdk/Dockerfile + +ENV ANDROID_HOME=/opt/android-sdk-linux \ + LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 \ + LANGUAGE=en_US:en + +ENV ANDROID_SDK_ROOT=$ANDROID_HOME \ + PATH=${PATH}:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/emulator + +RUN set -o xtrace \ + && cd /opt \ + && apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y jq \ + && apt-get install -y default-jdk \ + && apt-get install -y sudo wget zip unzip git openssh-client curl bc software-properties-common build-essential ruby-full ruby-bundler libstdc++6 libpulse0 libglu1-mesa locales lcov libsqlite3-dev --no-install-recommends \ + # for x86 emulators + && apt-get install -y libxtst6 libnss3-dev libnspr4 libxss1 libatk-bridge2.0-0 libgtk-3-0 libgdk-pixbuf2.0-0 \ + && apt-get install -y -qq xxd \ + && apt-get install -y lftp \ + && apt-get install -qq -y sqlite3 libsqlite3-dev \ + # linux desktop dependencies + && apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev \ + # monero_c dependencies + && apt-get install -y ccache build-essential autoconf libtool gperf llvm \ + # extra stuff for KVM + && apt-get install -y udev qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils \ + # for linux tests + && apt-get install -y xvfb network-manager ffmpeg x11-utils \ + && rm -rf /var/lib/apt/lists/* \ + && sh -c 'echo "en_US.UTF-8 UTF-8" > /etc/locale.gen' \ + && locale-gen \ + && update-locale LANG=en_US.UTF-8 + +# install nodejs for actions +RUN apt-get update && \ + apt-get install -y curl && \ + curl -fsSL https://deb.nodesource.com/setup_23.x | bash - && \ + apt-get install -y nodejs && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN wget https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz &&\ + rm -rf /usr/local/go &&\ + tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz + +ENV PATH=${PATH}:/usr/local/go/bin:${HOME}/go/bin +ENV GOROOT=/usr/local/go +ENV GOPATH=${HOME}/go +RUN go install golang.org/x/mobile/cmd/gomobile@latest +RUN gomobile init + +RUN wget -q https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip -O android-sdk-tools.zip \ + && mkdir -p ${ANDROID_HOME}/cmdline-tools/ \ + && unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}/cmdline-tools/ \ + && mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \ + && chown -R root:root $ANDROID_HOME \ + && rm android-sdk-tools.zip \ + && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \ + && yes | sdkmanager --licenses \ + && wget -O /usr/bin/android-wait-for-emulator https://raw.githubusercontent.com/travis-ci/travis-cookbooks/master/community-cookbooks/android-sdk/files/default/android-wait-for-emulator \ + && chmod +x /usr/bin/android-wait-for-emulator \ + && sdkmanager platform-tools \ + && mkdir -p ${HOME}/.android \ + && touch ${HOME}/.android/repositories.cfg \ + && git config --global user.email "czarek@cakewallet.com" \ + && git config --global user.name "CakeWallet CI" + +# emulator is not available on linux/arm64 (https://issuetracker.google.com/issues/227219818) +RUN if [ $(uname -m) == "x86_64" ]; then sdkmanager emulator ; fi + +# Extra dependencies to not download them for cake wallet build +RUN yes | sdkmanager \ + "platforms;android-$ANDROID_PLATFORM_VERSION" \ + "build-tools;$ANDROID_BUILD_TOOLS_VERSION" \ + "platforms;android-33" \ + "build-tools;33.0.2" \ + "build-tools;33.0.1" \ + "build-tools;33.0.0" \ + "build-tools;35.0.0" + +ENV ANDROID_NDK_VERSION=27.2.12479018 + +# Extra ndk dependency for sp_scanner +RUN yes | sdkmanager "ndk;$ANDROID_NDK_VERSION" \ + "ndk;27.0.12077973" + +# https://github.com/ReactiveCircus/android-emulator-runner dependencies for tests +RUN yes | sdkmanager "system-images;android-29;default;x86" \ + "system-images;android-29;default;x86_64" \ + "system-images;android-31;default;x86_64" \ + "platforms;android-29" + +# fake the KVM status so android emulator doesn't complain (that much) +RUN (addgroup kvm || true) && \ + adduser root kvm && \ + mkdir -p /etc/udev/rules.d/ && \ + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | tee /etc/udev/rules.d/99-kvm4all.rules + +ENV PATH=${HOME}/.cargo/bin:${PATH} + +RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \ + cargo install cargo-ndk && \ + for target in aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android x86_64-unknown-linux-gnu; \ + do \ + rustup target add --toolchain stable $target; \ + done + + +ENV HOME=${HOME} +ENV FLUTTER_HOME=${HOME}/sdks/flutter/${FLUTTER_VERSION} +ENV FLUTTER_ROOT=$FLUTTER_HOME + +ENV PATH=${PATH}:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin + +RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git ${FLUTTER_HOME} + +RUN yes | flutter doctor --android-licenses \ + && flutter doctor \ + && chown -R root:root ${FLUTTER_HOME} + +RUN flutter precache diff --git a/scripts/linux/build_monero_all.sh b/scripts/linux/build_monero_all.sh index 5dc5125271..558423219a 100755 --- a/scripts/linux/build_monero_all.sh +++ b/scripts/linux/build_monero_all.sh @@ -1,9 +1,5 @@ #!/bin/bash - -. ./config.sh - - set -x -e cd "$(dirname "$0")" @@ -15,7 +11,15 @@ NPROC="-j$(nproc)" for COIN in monero wownero; do pushd ../monero_c - ./build_single.sh ${COIN} $(gcc -dumpmachine) $NPROC + for target in x86_64-linux-gnu + do + if [[ -f "release/${COIN}/${target}_libwallet2_api_c.so" ]]; + then + echo "file exist, not building monero_c for ${COIN}/$target."; + else + ./build_single.sh ${COIN} $target $NPROC + unxz -f ../monero_c/release/${COIN}/${target}_libwallet2_api_c.so.xz + fi + done popd - unxz -f ../monero_c/release/${COIN}/$(gcc -dumpmachine)_libwallet2_api_c.so.xz -done +done \ No newline at end of file diff --git a/scripts/prepare_moneroc.sh b/scripts/prepare_moneroc.sh index c345408dd5..c0de33f6f1 100755 --- a/scripts/prepare_moneroc.sh +++ b/scripts/prepare_moneroc.sh @@ -4,9 +4,9 @@ set -x -e cd "$(dirname "$0")" -if [[ ! -d "monero_c" ]]; +if [[ ! -d "monero_c/.git" ]]; then - git clone https://github.com/mrcyjanek/monero_c --branch master + git clone https://github.com/mrcyjanek/monero_c --branch master monero_c cd monero_c git checkout af5277f96073917185864d3596e82b67bee54e78 git reset --hard diff --git a/scripts/windows/.gitignore b/scripts/windows/.gitignore new file mode 100644 index 0000000000..bb28076cdc --- /dev/null +++ b/scripts/windows/.gitignore @@ -0,0 +1 @@ +actions-runner \ No newline at end of file diff --git a/scripts/windows/Dockerfile.windows b/scripts/windows/Dockerfile.windows new file mode 100644 index 0000000000..f2a08b41ce --- /dev/null +++ b/scripts/windows/Dockerfile.windows @@ -0,0 +1,68 @@ +# Usage: +# docker build . -f Dockerfile.windows -t ghcr.io/cake-tech/cake_wallet:main-windows +# docker push ghcr.io/cake-tech/cake_wallet:main-windows + +FROM mcr.microsoft.com/windows/servercore:ltsc2022 + +ENV FLUTTER_VERSION=3.24.0 +ENV GIT_VERSION=2.47.1 +ENV VS_INSTALLED_DIR="C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools" +ENV PATH="C:\Users\ContainerAdministrator\.cargo\bin;C:\ProgramData\chocolatey\bin;C:\flutter\flutter\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\ContainerAdministrator\AppData\Local\Microsoft\WindowsApps" +ENV RUNNER_VERSION=2.321.0 +ENV RUNNER_URL=https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-win-x64-${RUNNER_VERSION}.zip +ENV RUNNER_WORKDIR=_work + +RUN powershell -Command \ + curl.exe -L https://aka.ms/vs/17/release/vc_redist.x64.exe -o vc_redist.x64.exe ; \ + Start-Process -Wait -FilePath .\vc_redist.x64.exe -ArgumentList '/quiet', '/install' ; \ + Remove-Item -Force vc_redist.x64.exe + +RUN powershell -Command \ + $GIT_VERSION = [Environment]::GetEnvironmentVariable('GIT_VERSION'); \ + curl.exe -L https://github.com/git-for-windows/git/releases/download/v$($GIT_VERSION).windows.1/Git-$($GIT_VERSION)-64-bit.exe -o git_installer.exe ; \ + Start-Process -Wait -FilePath .\git_installer.exe -ArgumentList '/SILENT', '/NOICONS' ; \ + Remove-Item -Force git_installer.exe + +RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \ + Set-ExecutionPolicy RemoteSigned -Scope Process; \ + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; \ + Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicP -OutFile install.ps1; \ + powershell -NoProfile -ExecutionPolicy Bypass -File install.ps1; \ + Remove-Item -Force install.ps1 + +RUN choco install -y visualstudio2022community +RUN choco install -y visualstudio2022-workload-nativedesktop +RUN choco install -y nodejs +RUN choco install -y go +RUN choco install -y 7zip + +RUN powershell -Command \ + curl.exe -L https://win.rustup.rs -o rustup-init.exe; \ + Start-Process -Wait -FilePath .\rustup-init.exe -ArgumentList "-y"; \ + Remove-Item -Force .\rustup-init.exe + +RUN powershell -Command \ + curl.exe -L https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -o C:\Windows\System32\nuget.exe + +RUN powershell -Command \ + $FLUTTER_VERSION = [Environment]::GetEnvironmentVariable('FLUTTER_VERSION'); \ + curl.exe -L https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_$($FLUTTER_VERSION)-stable.zip -o flutter.zip ; \ + 7z x flutter.zip -oC:\flutter -bsp1 -bse1 ; \ + Remove-Item -Force flutter.zip + +RUN flutter precache + +WORKDIR C:\\actions-runner + +RUN powershell -Command \ + curl.exe -L $env:RUNNER_URL -o 'actions-runner.zip'; \ + 7z x actions-runner.zip -oC:\actions-runner -bsp1 -bse1 ; \ + Remove-Item -Path 'actions-runner.zip' + +COPY actions-runner/.credentials /actions-runner/.credentials +COPY actions-runner/.credentials_rsaparams /actions-runner/.credentials_rsaparams +COPY actions-runner/.runner /actions-runner/.runner + +COPY ci_entrypoint.ps1 /actions-runner/ci_entrypoint.ps1 + +ENTRYPOINT ["powershell", "-File", "ci_entrypoint.ps1"] \ No newline at end of file diff --git a/scripts/windows/ci_entrypoint.ps1 b/scripts/windows/ci_entrypoint.ps1 new file mode 100644 index 0000000000..d68d0f5ecc --- /dev/null +++ b/scripts/windows/ci_entrypoint.ps1 @@ -0,0 +1,5 @@ +$runnerDir = "C:\actions-runner" +$runCmd = "$runnerDir\run.cmd" + +Write-Host "Starting the runner..." +& $runCmd \ No newline at end of file diff --git a/scripts/windows/ci_register.ps1 b/scripts/windows/ci_register.ps1 new file mode 100644 index 0000000000..a39048cdb6 --- /dev/null +++ b/scripts/windows/ci_register.ps1 @@ -0,0 +1,30 @@ +# Variables for paths and config +$runnerDir = "C:\actions-runner" +$configCmd = "$runnerDir\config.cmd" +$runCmd = "$runnerDir\run.cmd" + +# Check required environment variables +if (-not $env:RUNNER_TOKEN) { + Write-Error "RUNNER_TOKEN is not set. Exiting." + exit 1 +} +if (-not $env:RUNNER_REPO_URL) { + Write-Error "RUNNER_REPO_URL is not set. Exiting." + exit 1 +} +$env:RUNNER_NAME = "windows-amd64-cake" +$env:RUNNER_WORKDIR = "_work" + +# Register the runner +Write-Host "Registering the runner..." +Write-Host "--url $env:RUNNER_REPO_URL" +Write-Host "--token $env:RUNNER_TOKEN" +Write-Host "--name $env:RUNNER_NAME" +Write-Host "--work $env:RUNNER_WORKDIR" + +& $configCmd --url $env:RUNNER_REPO_URL ` + --token $env:RUNNER_TOKEN ` + --name $env:RUNNER_NAME ` + --work $env:RUNNER_WORKDIR ` + --unattended ` + --replace \ No newline at end of file