diff --git a/.github/workflows/are_translations_sane.yml b/.github/workflows/are_translations_sane.yml index 5f12d0429c..409d02c3fa 100644 --- a/.github/workflows/are_translations_sane.yml +++ b/.github/workflows/are_translations_sane.yml @@ -4,7 +4,7 @@ on: [pull_request] jobs: PR_test_build: - runs-on: ubuntu-24.04 + runs-on: puzl-ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/compare_overrides.yml b/.github/workflows/compare_overrides.yml index b53eb7bb9f..a305d1c336 100644 --- a/.github/workflows/compare_overrides.yml +++ b/.github/workflows/compare_overrides.yml @@ -10,7 +10,7 @@ permissions: jobs: compare-overrides: - runs-on: ubuntu-24.04 + runs-on: puzl-ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 1306f81ede..2d6799885d 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -5,11 +5,6 @@ permissions: packages: write on: - pull_request: - branches: [dev] - paths-ignore: - - "**.md" - - "docs/**" workflow_dispatch: concurrency: diff --git a/.github/workflows/no_http_imports.yaml b/.github/workflows/no_http_imports.yaml index 0bfca1d063..72a7bb1822 100644 --- a/.github/workflows/no_http_imports.yaml +++ b/.github/workflows/no_http_imports.yaml @@ -4,7 +4,7 @@ on: [pull_request] jobs: PR_test_build: - runs-on: ubuntu-24.04 + runs-on: puzl-ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/no_print_in_dart.yaml b/.github/workflows/no_print_in_dart.yaml index 69885c9b5c..74ec8752bf 100644 --- a/.github/workflows/no_print_in_dart.yaml +++ b/.github/workflows/no_print_in_dart.yaml @@ -4,7 +4,7 @@ on: [pull_request] jobs: PR_test_build: - runs-on: ubuntu-24.04 + runs-on: puzl-ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/no_restricted_imports.yaml b/.github/workflows/no_restricted_imports.yaml index 18b228dcbb..4d061a166d 100644 --- a/.github/workflows/no_restricted_imports.yaml +++ b/.github/workflows/no_restricted_imports.yaml @@ -4,7 +4,7 @@ on: [pull_request] jobs: check_restricted_imports: - runs-on: ubuntu-24.04 + runs-on: puzl-ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 80713b7d2f..38fd78f50e 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -10,44 +10,238 @@ defaults: run: shell: bash +env: + CW_DOCKER_REGISTRY: ghcr.io/cake-tech/cake_wallet + CW_DOCKER_USE_CLOUD: "true" + jobs: - PR_test_build: + guard: # Fork PRs don't have access to repository secrets, so the generated # lib/.secrets.g.dart would be empty and the build fails to compile. # Skip for forks, mirroring the Android workflow's internal-build guard. if: github.event.pull_request.head.repo.fork == false - runs-on: [Linux, amd64, forlinux] + runs-on: puzl-ubuntu-latest + steps: + - run: echo "internal PR" + + base: + needs: guard + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build base + run: ./scripts/linux/docker/build.sh --only base + + bitbox: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build bitbox + run: ./scripts/linux/docker/build.sh --only bitbox + + mwebd: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build mwebd + run: ./scripts/linux/docker/build.sh --only mwebd + + reown: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build reown + run: ./scripts/linux/docker/build.sh --only reown + + zcash: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build zcash + run: ./scripts/linux/docker/build.sh --only zcash + + torch: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build torch + run: ./scripts/linux/docker/build.sh --only torch + + monero: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + strategy: + matrix: + coin: [monero, wownero] + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build monero + run: ./scripts/linux/docker/build.sh --only monero --coin ${{ matrix.coin }} --target x86_64-linux-gnu + + monero_merge: + needs: monero + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Merge monero + run: ./scripts/linux/docker/build.sh --only monero --merge + + final: + needs: [bitbox, mwebd, reown, zcash, torch, monero_merge] + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ github.event.pull_request.head.sha }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build final + run: ./scripts/linux/docker/build.sh --only final + + app: + needs: final + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 container: image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly 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 - /var/run/docker.sock:/var/run/docker.sock - steps: - name: Fix github actions messing up $HOME... run: "echo HOME=/root | sudo tee -a $GITHUB_ENV" - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} with: ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: @@ -88,12 +282,8 @@ jobs: echo "${{ secrets.MAIN_SECRETS_FILE }}" | base64 --decode > lib/.secrets.g.dart - - name: Build dependencies utilizing ghcr cache - run: | - env \ - CW_DOCKER_REGISTRY=ghcr.io/cake-tech/cake_wallet \ - CW_DOCKER_USE_CLOUD=true \ - ./scripts/linux/docker/build.sh + - name: Extract native dependencies + run: ./scripts/linux/docker/build.sh --extract - name: Execute Build and Setup Commands run: | @@ -134,7 +324,6 @@ jobs: path: ${{ github.workspace }}/build/linux/x64/release/cakewallet_linux.zip name: cakewallet_linux - # Integration tests moved to integration_tests.yml - name: Test [cw_core] timeout-minutes: 15 run: cd cw_core && flutter test diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index b334e6ecad..846264dcc6 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -17,33 +17,276 @@ on: defaults: run: shell: bash + +env: + CW_DOCKER_REGISTRY: ghcr.io/cake-tech/cake_wallet + CW_DOCKER_USE_CLOUD: "true" + jobs: - PR_test_build: - runs-on: [Linux, amd64, android] + base: + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build base + run: ./scripts/android/docker/build.sh --only base + + bitbox: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build bitbox + run: ./scripts/android/docker/build.sh --only bitbox + + mwebd: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build mwebd + run: ./scripts/android/docker/build.sh --only mwebd + + reown: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build reown + run: ./scripts/android/docker/build.sh --only reown + + zcash: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build zcash + run: ./scripts/android/docker/build.sh --only zcash + + torch: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + strategy: + matrix: + target: + - aarch64-linux-android + - armv7a-linux-androideabi + - x86_64-linux-android + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build torch + run: ./scripts/android/docker/build.sh --only torch --target ${{ matrix.target }} + + torch_merge: + needs: torch + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Merge torch + run: ./scripts/android/docker/build.sh --only torch --merge + + monero: + needs: base + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + strategy: + matrix: + coin: [monero, wownero, zano] + target: + - aarch64-linux-android + - armv7a-linux-androideabi + - x86_64-linux-android + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build monero + run: ./scripts/android/docker/build.sh --only monero --coin ${{ matrix.coin }} --target ${{ matrix.target }} + + monero_merge: + needs: monero + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Merge monero + run: ./scripts/android/docker/build.sh --only monero --merge + + decred: + needs: [base, torch_merge] + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build decred + run: ./scripts/android/docker/build.sh --only decred + + final: + needs: [bitbox, mwebd, reown, zcash, torch_merge, monero_merge, decred] + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 + steps: + - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} + with: + ref: ${{ inputs.ref }} + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build final + run: ./scripts/android/docker/build.sh --only final + + integration-test: + needs: final + uses: ./.github/workflows/reusable-integration-test.yml + with: + ref: ${{ inputs.ref }} + suite_dir: integration_test/suites + test_tier: tier0 + slack_notify: true + extract_only: true + secrets: inherit + + app: + needs: final + runs-on: puzl-ubuntu-latest + timeout-minutes: 360 container: image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly 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 }} volumes: - - /opt/cw_cache_android/root/.cache:/root/.cache - - /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 - /var/run/docker.sock:/var/run/docker.sock - steps: - name: Fix github actions messing up $HOME... run: "echo HOME=/root | sudo tee -a $GITHUB_ENV" - uses: actions/checkout@v4 + if: ${{ env.ACT != 'true' }} with: ref: ${{ inputs.ref }} - + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: @@ -88,22 +331,10 @@ jobs: - name: Generate KeyStore run: | - 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 + ./scripts/android/generate_dev_keystore.sh android/app/key.jks - - name: Build dependencies utilizing ghcr cache - run: | - env \ - CW_DOCKER_REGISTRY=ghcr.io/cake-tech/cake_wallet \ - CW_DOCKER_USE_CLOUD=true \ - ./scripts/android/docker/build.sh + - name: Extract native dependencies + run: ./scripts/android/docker/build.sh --extract - name: Execute Build and Setup Commands run: | @@ -121,7 +352,6 @@ jobs: run: | ./model_generator.sh - - name: Generate key properties run: | dart run tool/generate_android_key_properties.dart keyAlias=testKey storeFile=key.jks storePassword=$STORE_PASS keyPassword=$KEY_PASS diff --git a/.github/workflows/reusable-integration-test.yml b/.github/workflows/reusable-integration-test.yml index 32a65632a5..e5a91aeb93 100644 --- a/.github/workflows/reusable-integration-test.yml +++ b/.github/workflows/reusable-integration-test.yml @@ -42,6 +42,10 @@ on: required: false type: boolean default: false + extract_only: + required: false + type: boolean + default: false defaults: run: @@ -61,24 +65,33 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} + - name: Authenticate git for package clones + run: | + git config --global --unset-all credential.helper || true + git config --global url."https://x-access-token:${{ github.token }}@github.com/".insteadOf "https://github.com/" - name: Free disk space run: | sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/.ghcup /usr/local/lib/node_modules /opt/hostedtoolcache/CodeQL sudo rm -rf /usr/share/swift /usr/local/share/powershell /usr/share/miniconda /opt/az /usr/local/graalvm sudo docker image prune -af > /dev/null df -h / - - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm + sudo chmod 666 /dev/kvm || true if [[ ! -c /dev/kvm ]]; then echo "::error::/dev/kvm is not a character device, this runner has no usable KVM" exit 1 fi + - name: Install protobuf compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + - uses: actions/setup-java@v4 with: distribution: temurin @@ -121,9 +134,8 @@ jobs: key: pub-cache-${{ runner.os }}-${{ hashFiles('pubspec_base.yaml', 'cw_*/pubspec.yaml') }} restore-keys: pub-cache-${{ runner.os }}- - - name: Add rust android targets - run: | - rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android + - name: Docker sock permissions + run: sudo chmod 666 /var/run/docker.sock || true - name: Log in to GitHub Container Registry uses: docker/login-action@v3 @@ -165,44 +177,26 @@ jobs: echo "${{ secrets.FUNDS_SECRETS_FILE }}" | base64 --decode > integration_test/core/funded_wallets.dart - name: Build dependencies utilizing ghcr cache + if: ${{ !inputs.extract_only }} run: | env \ CW_DOCKER_REGISTRY=ghcr.io/cake-tech/cake_wallet \ CW_DOCKER_USE_CLOUD=true \ ./scripts/android/docker/build.sh + - name: Extract native dependencies + if: ${{ inputs.extract_only }} + run: | + env \ + CW_DOCKER_REGISTRY=ghcr.io/cake-tech/cake_wallet \ + CW_DOCKER_USE_CLOUD=true \ + ./scripts/android/docker/build.sh --extract + - name: Reclaim the space the dependency images took run: | docker system prune -af --volumes > /dev/null df -h / - - name: Regenerate mweb bindings - run: | - git checkout -- cw_mweb/pubspec.yaml - rm -f cw_mweb/pubspec.lock - rm -rf cw_mweb/.dart_tool - - # Hosted runners ship libclang, only install when it is actually missing - if ! ldconfig -p | grep -q libclang; then - sudo apt-get install -y --no-install-recommends libclang-dev > /dev/null - fi - - if ! command -v protoc > /dev/null || - [[ ! -f /usr/include/google/protobuf/descriptor.proto ]]; then - sudo apt-get install -y --no-install-recommends protobuf-compiler libprotobuf-dev > /dev/null - fi - - pushd cw_mweb - flutter pub get - dart run ffigen --config ffigen_config.yaml - popd - - # Fail here in seconds rather than minutes into the gradle build - if ! grep -q "final class GoSlice" cw_mweb/lib/generated_bindings.g.dart; then - echo "::error::mweb bindings regenerated without class modifiers, ffigen resolved too old" - exit 1 - fi - - name: Execute Build and Setup Commands run: | pushd scripts/android diff --git a/scripts/android/build_monero_all.sh b/scripts/android/build_monero_all.sh index 341f14496c..4065d67fd7 100755 --- a/scripts/android/build_monero_all.sh +++ b/scripts/android/build_monero_all.sh @@ -9,11 +9,23 @@ cd "$(dirname "$0")" ../prepare_moneroc.sh -for COIN in monero wownero zano; +if [[ -n "${COIN:-}" ]]; then + COINS=("$COIN") +else + COINS=(monero wownero zano) +fi + +if [[ -n "${TARGET:-}" ]]; then + TARGETS=("$TARGET") +else + TARGETS=(x86_64-linux-android aarch64-linux-android armv7a-linux-androideabi) +fi + +for COIN in "${COINS[@]}"; do pushd ../monero_c monero_c_tag=$(git describe --tags) - for target in {x86_64,aarch64}-linux-android armv7a-linux-androideabi + for target in "${TARGETS[@]}" do if [[ -f "release/${monero_c_tag}/${target}/${COIN}_libwallet2_api_c.so" ]]; then diff --git a/scripts/android/dev-test-key.crt b/scripts/android/dev-test-key.crt new file mode 100644 index 0000000000..af84b8cddc --- /dev/null +++ b/scripts/android/dev-test-key.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUO9LtTFYNTLeRJ0Vs/BoW/RHanY4wDQYJKoZIhvcNAQEL +BQAwcDELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0FtZXJpY2ExEDAOBgNVBAcMB0Zs +b3JpZGExEzARBgNVBAoMCkNha2VXYWxsZXQxEzARBgNVBAsMCkNha2VXYWxsZXQx +EzARBgNVBAMMCkNha2VXYWxsZXQwIBcNMjYwOTEwMDgwMzU5WhgPMjA1NDAxMjYw +ODAzNTlaMHAxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdBbWVyaWNhMRAwDgYDVQQH +DAdGbG9yaWRhMRMwEQYDVQQKDApDYWtlV2FsbGV0MRMwEQYDVQQLDApDYWtlV2Fs +bGV0MRMwEQYDVQQDDApDYWtlV2FsbGV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAlHYehgoK7D3/ga26LlYMQhVqyokmUZQ9E6+S2utKQixTwHlJsAe7 +IWY0TpUhz1iDSq8XG/IQdnos8o/SEHY8EVO+STzOjbYIk4J6mm6jqZa59y+l2Brk +4IEZYe8HsIjMBFmbn8WlLhhT9PewcMZxutW3D0+swx+8azpfYHBY/5a6PaKAstey +HKSejD7atfcI7xZbWzzPGwS0hDjAcqGcfyYH/r2ziIbvEU2xDeOBYBm9yE1B6sEn +dI2KB2Q5HtKfNV8Vw1vSLvFEIAJh/vmuO7RoW+tPdUfORTydCeNvDIK8DGe1ka8/ +ZmQraNgGgTxA5YpJKZsTYgc5eMWyxIHBlwIDAQABo1MwUTAdBgNVHQ4EFgQUSTWY +iEWvKz3YwrB0bufUbxcZ2YgwHwYDVR0jBBgwFoAUSTWYiEWvKz3YwrB0bufUbxcZ +2YgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAZ+21ksy5plnb +ZCTYifmk8E8M+sGWS2m5eLTjzAl4iEEr30Eit4ghQvt7YG5r62HLHBFirv4z6k01 +5mhjU+BJp0NU1e8XOVq6M0EnMKF8OIz3lUy4aUnr6olCdEbSsRyd0l7Ht21Ci0HH +BkFbZ+HlqwXTPA5kUWMkJ0JlrMhaeyRMpA0jsE1fhgjWmrB2keeFP8CwA3GnQhq1 +HEkN6izdaobnUHnTE7dH3Eixkt0L4XwpkCsw7IV3aVt7+tsc0zICod8JS0ib4Lrw +XHVWIRtcTGwDLqYlknacR0Mh7JeDYU3JtfxvCB7jstDI9CmYnPOiAsO4NccnbzD5 +WIbD9bRlYg== +-----END CERTIFICATE----- diff --git a/scripts/android/dev-test-key.pem b/scripts/android/dev-test-key.pem new file mode 100644 index 0000000000..066eea30b1 --- /dev/null +++ b/scripts/android/dev-test-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCUdh6GCgrsPf+B +rbouVgxCFWrKiSZRlD0Tr5La60pCLFPAeUmwB7shZjROlSHPWINKrxcb8hB2eizy +j9IQdjwRU75JPM6NtgiTgnqabqOplrn3L6XYGuTggRlh7wewiMwEWZufxaUuGFP0 +97BwxnG61bcPT6zDH7xrOl9gcFj/lro9ooCy17IcpJ6MPtq19wjvFltbPM8bBLSE +OMByoZx/Jgf+vbOIhu8RTbEN44FgGb3ITUHqwSd0jYoHZDke0p81XxXDW9Iu8UQg +AmH++a47tGhb6091R85FPJ0J428MgrwMZ7WRrz9mZCto2AaBPEDlikkpmxNiBzl4 +xbLEgcGXAgMBAAECggEAILrz2sg/BTtb58qVx95noHAZVaKsBINCfeMQ5NvHyNP9 ++OqkgL8Llf7y3DpLtGtGssHsPdh9oako3KcxgDiY4JcOVhYmUZ7GwEKdZgurYFmf +ESx7iyvOtuIvZuRsVRKrq+/Xe/92fbWWWOT8jLhrXW39iE4f//qwrxE08s2CCYLq +zQFUIrICN6/7te+qTessWO4Z2zscqhzTMae2kWksUMiU0mYv9BJ6Lzb1Mj2kqSir +CMBxmxvQmjmE4+29rKulmAQlKNb+rwKgsmdk5+oYcCdMHP0VeXL8RZ+IcOkOv4JU +xj+isHr8S9mmGMmpM5aAnZoS7tF3aLDH4aojN6MNfQKBgQDQ44cB+SDoKujnfMsK +1ci21HB2R7qobbspJVGHNQx25XYJdAw1CTsnN8FwziXGR8/tpSJMHQin0M7ZLHqn +u/aTZLT+7VAmFNUOOY4mbBrQzTA21r2l0kMloXyqmO6ec820L+9bqeAZ6N4y1iNt +iQ8yVYOuS1SMi+QfqerxbXwg4wKBgQC18byTn58edwprsqWs48u7hB/zI3SLoXOn +qHPke9uCo4ynV6yEmLa/yN9iyXL2UjY7KjzbpFLhrFYE5JxXfCfCgOk3N1nF8953 +2K95/3uFrasbRSRDi2ROVmDYD1stqN+euHfx3N15uZ/E/Ol8e7GhBMIWo9WIyqJ7 +3GiU9ce+vQKBgDmTfVL96CQWL7wemZiS9I/wXk871ic2l6/vqVkrNhRPiwlwAy1b +LgLRS3BcYe0VPiqyqZFxL0GSPHF/4Q76sCxQRdM+dikI9PDGzHeqtHl5FSEIb52V +JwzoRgR/wsXKBZj03XTWjf6WQSih/YHjlVy6VWFlQhi7NlXD8A47r0shAoGAX+Pj +qzn5NqkuiMxCnPgBq/ZakF4jUmPo/He/fK/6ZMOloZyHg+qohjNr6Dibqk5x3oTI +Rko4lE+rljCWZVU97zjZsQwu+DErEqbU9Ms9YprBD0JFdoPEfbTwnW6ac1oJYTF/ +pazt3PWDyuIwfDcEywQi9qOqKsoBEJ59T+SpPVECgYBzT9XnT01Eh5MGYxoC/8cu +c19gWNqHNnvTeUYNI6y8+bUSSbP3vJ8Bytb0VuBega5QQYrT9EYILJIVu9oKSHJE +v5rSGMmM9hNbWmROGEad/afO82AilvfOR0fx6Tmyk3Sf3tg3JltA3tAzPgo/BOlc +GqVhDf37ULi8YBLGLkNOZA== +-----END PRIVATE KEY----- diff --git a/scripts/android/docker/Dockerfile.decred b/scripts/android/docker/Dockerfile.decred index 5eb6fd320d..48ee688fb7 100644 --- a/scripts/android/docker/Dockerfile.decred +++ b/scripts/android/docker/Dockerfile.decred @@ -10,6 +10,7 @@ RUN mkdir -p /w/cw_decred/lib/api COPY --from=torch_source /w/scripts/torch_dart /w/scripts/torch_dart COPY cw_core/pubspec.yaml /w/cw_core/pubspec.yaml COPY cw_decred/pubspec.yaml /w/cw_decred/pubspec.yaml +COPY pubspec_overrides.yaml /w/cw_decred/pubspec_overrides.yaml COPY scripts/android/build_decred.sh /w/scripts/android/build_decred.sh RUN /w/scripts/android/build_decred.sh diff --git a/scripts/android/docker/Dockerfile.monero b/scripts/android/docker/Dockerfile.monero index 664ae0e61a..b95081871c 100644 --- a/scripts/android/docker/Dockerfile.monero +++ b/scripts/android/docker/Dockerfile.monero @@ -2,8 +2,13 @@ ARG BASE_IMAGE FROM --platform=linux/amd64 ${BASE_IMAGE} AS build RUN mkdir -p /w/scripts/android +COPY scripts/functions.sh /w/scripts/functions.sh COPY scripts/prepare_moneroc.sh /w/scripts/prepare_moneroc.sh COPY scripts/android/build_monero_all.sh /w/scripts/android/build_monero_all.sh +ARG COIN +ARG TARGET +ENV COIN=$COIN +ENV TARGET=$TARGET RUN /w/scripts/android/build_monero_all.sh \ && rm -rf /w/scripts/monero_c/contrib \ /w/scripts/monero_c/.git \ diff --git a/scripts/android/docker/Dockerfile.torch b/scripts/android/docker/Dockerfile.torch index d3ea06f490..59ed5b08b1 100644 --- a/scripts/android/docker/Dockerfile.torch +++ b/scripts/android/docker/Dockerfile.torch @@ -13,7 +13,13 @@ RUN groupadd -g "$GID" "$USER" && \ RUN sudo chown $UID:$GID -R /w /root USER builder -RUN /w/scripts/android/build_torch.sh && rm -rf /w/scripts/torch_dart/simplybs +ARG TARGET +RUN if [ -n "$TARGET" ]; then \ + /w/scripts/android/build_torch.sh "$TARGET"; \ + else \ + /w/scripts/android/build_torch.sh; \ + fi \ + && rm -rf /w/scripts/torch_dart/simplybs FROM --platform=linux/amd64 alpine COPY --from=build /w /w diff --git a/scripts/android/docker/build.sh b/scripts/android/docker/build.sh index a6d7719eff..ca376fb22d 100755 --- a/scripts/android/docker/build.sh +++ b/scripts/android/docker/build.sh @@ -4,50 +4,233 @@ cd "$(dirname $0)" CW_DOCKER_REGISTRY="${CW_DOCKER_REGISTRY:-localhost/cake-tech/cake_wallet}" CW_DOCKER_USE_CLOUD="${CW_DOCKER_USE_CLOUD:-}" +IMAGE_PREFIX="android-deps" SCRIPT_DIR="$(pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +ONLY="" +TARGET="" +COIN="" +MERGE=false +EXTRACT=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --only) + ONLY="$2" + shift 2 + ;; + --target) + TARGET="$2" + shift 2 + ;; + --coin) + COIN="$2" + shift 2 + ;; + --merge) + MERGE=true + shift + ;; + --extract) + EXTRACT=true + shift + ;; + -h|--help) + echo "Usage: $0 [--only NAME] [--target TRIPLE] [--coin COIN] [--merge] [--extract]" + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 1 + ;; + esac +done + +TORCH_TARGETS=( + aarch64-linux-android + armv7a-linux-androideabi + x86_64-linux-android +) +MONERO_COINS=(monero wownero zano) +MONERO_TARGETS=( + aarch64-linux-android + armv7a-linux-androideabi + x86_64-linux-android +) + image_exists() { docker image inspect "$1" &>/dev/null } tinysha() { - cat "$@" | sha256sum | cut -c1-6 + cat "$@" | sha256sum | cut -c1-6 +} + +slice_extra() { + if [[ -n "$COIN" && -n "$TARGET" ]]; then + echo "-${COIN}-${TARGET}" + elif [[ -n "$TARGET" ]]; then + echo "-${TARGET}" + else + echo "" + fi +} + +# Distinct from the unsuffixed all-in-one tag. The hash of slice suffixes +# invalidates the merge image if a coin or target is added or removed. +assembled_extra() { + local tmp + tmp="$(mktemp)" + printf '%s\n' "$@" > "$tmp" + echo "-merged-$(tinysha "$tmp")" + rm -f "$tmp" +} + +torch_assembled_extra() { + local extras=() + local t + for t in "${TORCH_TARGETS[@]}"; do + extras+=("-$t") + done + assembled_extra "${extras[@]}" +} + +monero_assembled_extra() { + local extras=() + local coin t + for coin in "${MONERO_COINS[@]}"; do + for t in "${MONERO_TARGETS[@]}"; do + extras+=("-${coin}-${t}") + done + done + assembled_extra "${extras[@]}" +} + +component_extra() { + local name="$1" + local sliced + sliced="$(slice_extra)" + if [[ -n "$sliced" ]]; then + echo "$sliced" + return + fi + case "$name" in + torch) torch_assembled_extra ;; + monero) monero_assembled_extra ;; + *) echo "" ;; + esac +} + +img() { + local name="$1" + local version="$2" + local extra="${3:-}" + echo "$CW_DOCKER_REGISTRY:${IMAGE_PREFIX}-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}${extra}" +} + +remote_exists() { + local tag="$1" + if docker buildx version >/dev/null 2>&1; then + docker buildx imagetools inspect "$tag" >/dev/null 2>&1 + return + fi + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$tag" >/dev/null 2>&1 +} + +cached() { + local tag="$1" + if image_exists "$tag"; then + return 0 + fi + if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] && remote_exists "$tag"; then + return 0 + fi + return 1 +} + +maybe_push() { + local tag="$1" + if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]]; then + docker push "$tag" + fi } build() { local name="$1"; shift local version=$1; shift - if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] - then - set +e - docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | \ - grep "^${CW_DOCKER_REGISTRY}:android-deps-" | \ - awk '{print $2}' | \ - xargs -r docker rmi - docker pull "$CW_DOCKER_REGISTRY:android-deps-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}" - set -e - fi - if image_exists "$CW_DOCKER_REGISTRY:android-deps-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}"; then - echo "==> skipping $name (image already exists)" + local extra + extra="$(component_extra "$name")" + local tag + tag="$(img "$name" "$version" "$extra")" + if cached "$tag"; then + echo "==> skipping $name (cached: $tag)" return 0 fi - echo "==> building $name" + echo "==> building $name${extra}" docker build \ --platform linux/amd64 \ --file "$SCRIPT_DIR/Dockerfile.${name}" \ - --tag "$CW_DOCKER_REGISTRY:android-deps-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}" \ + --tag "$tag" \ "$@" \ "$REPO_ROOT" - if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] - then - docker push "$CW_DOCKER_REGISTRY:android-deps-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}" - fi + maybe_push "$tag" } -img() { - echo "$CW_DOCKER_REGISTRY:android-deps-${1}-$(tinysha "$SCRIPT_DIR/Dockerfile.${1}")-${2}" +merge_slices() { + local name="$1" + local version="$2" + local extra="$3" + shift 3 + local dest + dest="$(img "$name" "$version" "$extra")" + if cached "$dest"; then + echo "==> skipping merge $name (cached: $dest)" + return 0 + fi + + local slices=("$@") + if [[ ${#slices[@]} -eq 0 ]]; then + echo "merge_slices: no slice images given" >&2 + exit 1 + fi + + local dockerfile + dockerfile="$(mktemp)" + local build_args=() + local i=0 + for slice in "${slices[@]}"; do + echo "ARG SRC${i}" >> "$dockerfile" + i=$((i + 1)) + done + i=0 + for slice in "${slices[@]}"; do + if ! cached "$slice"; then + echo "merge_slices: missing slice $slice" >&2 + rm -f "$dockerfile" + exit 1 + fi + echo "FROM --platform=linux/amd64 \${SRC${i}} AS s${i}" >> "$dockerfile" + build_args+=(--build-arg "SRC${i}=$slice") + i=$((i + 1)) + done + echo "FROM --platform=linux/amd64 alpine" >> "$dockerfile" + i=0 + for slice in "${slices[@]}"; do + echo "COPY --from=s${i} /w /w" >> "$dockerfile" + i=$((i + 1)) + done + + echo "==> merging $name from ${#slices[@]} slices -> $dest" + docker build \ + --platform linux/amd64 \ + --file "$dockerfile" \ + --tag "$dest" \ + "${build_args[@]}" \ + "$SCRIPT_DIR" + rm -f "$dockerfile" + maybe_push "$dest" } base_ver="latest" @@ -55,65 +238,153 @@ torch_ver="$(tinysha $SCRIPT_DIR/Dockerfile.torch $REPO_ROOT/scripts/prepare_tor reown_ver=$(tinysha $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/prepare_reown.sh $REPO_ROOT/scripts/android/build_reown_deps.sh) bitbox_ver=$(tinysha $SCRIPT_DIR/Dockerfile.bitbox $REPO_ROOT/scripts/build_bitbox_flutter.sh) monero_ver=$(tinysha $SCRIPT_DIR/Dockerfile.monero $REPO_ROOT/scripts/prepare_moneroc.sh $REPO_ROOT/scripts/android/build_monero_all.sh) -mwebd_ver=$(tinysha $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f)) +mwebd_ver=$(tinysha $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f | sort)) zcash_ver=$(tinysha $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/android/build_zcash.sh) -decred_ver=$(tinysha $SCRIPT_DIR/Dockerfile.torch $SCRIPT_DIR/Dockerfile.decred $REPO_ROOT/scripts/android/build_decred.sh $REPO_ROOT/cw_decred/pubspec.yaml) -echo $base_ver $torch_ver $reown_ver $bitbox_ver $monero_ver $mwebd_ver $zcash_ver $decred_ver > /tmp/docker_build_versions +torch_assembled_extra > /tmp/cw_torch_assembled_extra +decred_ver=$(tinysha $SCRIPT_DIR/Dockerfile.torch $SCRIPT_DIR/Dockerfile.decred $REPO_ROOT/scripts/android/build_decred.sh $REPO_ROOT/cw_decred/pubspec.yaml $REPO_ROOT/pubspec_overrides.yaml /tmp/cw_torch_assembled_extra) +echo $base_ver $torch_ver $reown_ver $bitbox_ver $monero_ver $mwebd_ver $zcash_ver $decred_ver "$(torch_assembled_extra)" "$(monero_assembled_extra)" > /tmp/docker_build_versions final_ver=$(tinysha /tmp/docker_build_versions) -docker create --name temp_extract $(img final $final_ver) \ -&& cd $REPO_ROOT \ -&& docker cp temp_extract:/w.top w.top \ -&& rsync -av w.top/ . \ -&& rm -rf w.top \ -&& docker rm temp_extract \ -&& echo "cache ok" \ -&& exit 0 \ -|| echo "cache miss oh" - -docker rm temp_extract || true +extract_final() { + local required="${1:-}" + local tag + tag="$(img final "$final_ver")" + if ! image_exists "$tag" && [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] && remote_exists "$tag"; then + docker pull "$tag" + fi + docker rm -f temp_extract >/dev/null 2>&1 || true + if docker create --name temp_extract "$tag"; then + cd "$REPO_ROOT" + docker cp temp_extract:/w.top w.top + rsync -av w.top/ . + rm -rf w.top + docker rm temp_extract + echo "cache ok" + echo "$tag" > /tmp/cakewallet_docker + return 0 + fi + docker rm -f temp_extract >/dev/null 2>&1 || true + echo "cache miss oh" + if [[ "$required" == "required" ]]; then + echo "final image missing: $tag" >&2 + exit 1 + fi + return 1 +} -build base "$base_ver" +build_base() { + build base "$base_ver" +} -build bitbox "$bitbox_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_bitbox() { + build bitbox "$bitbox_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build mwebd "$mwebd_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_mwebd() { + build mwebd "$mwebd_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build reown "$reown_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_reown() { + build reown "$reown_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build monero "$monero_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_zcash() { + build zcash "$zcash_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build zcash "$zcash_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_torch() { + if [[ "$MERGE" == true ]]; then + local slices=() + local t + for t in "${TORCH_TARGETS[@]}"; do + slices+=("$(img torch "$torch_ver" "-$t")") + done + merge_slices torch "$torch_ver" "$(torch_assembled_extra)" "${slices[@]}" + return 0 + fi + build torch "$torch_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg TARGET="${TARGET}" +} -build torch "$torch_ver" \ - --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_monero() { + if [[ "$MERGE" == true ]]; then + local slices=() + local coin t + for coin in "${MONERO_COINS[@]}"; do + for t in "${MONERO_TARGETS[@]}"; do + slices+=("$(img monero "$monero_ver" "-${coin}-${t}")") + done + done + merge_slices monero "$monero_ver" "$(monero_assembled_extra)" "${slices[@]}" + return 0 + fi + build monero "$monero_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg COIN="${COIN}" \ + --build-arg TARGET="${TARGET}" +} -build decred "$decred_ver" \ - --build-arg BASE_IMAGE="$(img base "$base_ver")" \ - --build-arg TORCH_IMAGE="$(img torch "$torch_ver")" +build_decred() { + build decred "$decred_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg TORCH_IMAGE="$(img torch "$torch_ver" "$(torch_assembled_extra)")" +} -build final $final_ver \ - --build-arg BASE_IMAGE="$(img base $base_ver)" \ - --build-arg TORCH_IMAGE="$(img torch $torch_ver)" \ - --build-arg REOWN_IMAGE="$(img reown $reown_ver)" \ - --build-arg BITBOX_IMAGE="$(img bitbox $bitbox_ver)" \ - --build-arg MONERO_IMAGE="$(img monero $monero_ver)" \ - --build-arg DECRED_IMAGE="$(img decred $decred_ver)" \ - --build-arg MWEBD_IMAGE="$(img mwebd $mwebd_ver)" \ - --build-arg ZCASH_IMAGE="$(img zcash $zcash_ver)" +build_final() { + build final "$final_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg TORCH_IMAGE="$(img torch "$torch_ver" "$(torch_assembled_extra)")" \ + --build-arg REOWN_IMAGE="$(img reown "$reown_ver")" \ + --build-arg BITBOX_IMAGE="$(img bitbox "$bitbox_ver")" \ + --build-arg MONERO_IMAGE="$(img monero "$monero_ver" "$(monero_assembled_extra)")" \ + --build-arg DECRED_IMAGE="$(img decred "$decred_ver")" \ + --build-arg MWEBD_IMAGE="$(img mwebd "$mwebd_ver")" \ + --build-arg ZCASH_IMAGE="$(img zcash "$zcash_ver")" + echo "done: $(img final $final_ver)" + echo "$(img final $final_ver)" > /tmp/cakewallet_docker +} -echo "done: $(img final $final_ver)" -echo $(img final $final_ver) > /tmp/cakewallet_docker +run_one() { + case "$1" in + base) build_base ;; + bitbox) build_bitbox ;; + mwebd) build_mwebd ;; + reown) build_reown ;; + zcash) build_zcash ;; + torch) build_torch ;; + monero) build_monero ;; + decred) build_decred ;; + final) build_final ;; + *) + echo "Unknown component: $1" >&2 + exit 1 + ;; + esac +} +if [[ "$EXTRACT" == true ]]; then + extract_final required + exit 0 +fi -docker create --name temp_extract $(img final $final_ver) \ -&& cd $REPO_ROOT \ -&& docker cp temp_extract:/w.top w.top \ -&& rsync -av w.top/ . \ -&& rm -rf w.top \ -&& docker rm temp_extract \ -&& echo "cache ok" \ -&& exit 0 +if [[ -z "$ONLY" ]]; then + extract_final && exit 0 || true + ONLY="" + TARGET="" + COIN="" + MERGE=false + build_base + build_bitbox + build_mwebd + build_reown + build_monero + build_zcash + build_torch + build_decred + build_final + extract_final required + exit 0 +fi -echo idk. -exit 1 +run_one "$ONLY" diff --git a/scripts/android/generate_dev_keystore.sh b/scripts/android/generate_dev_keystore.sh new file mode 100755 index 0000000000..7f131e5fe2 --- /dev/null +++ b/scripts/android/generate_dev_keystore.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")" + +STORE_PASS="${STORE_PASS:-test@cake_wallet}" +KEY_PASS="${KEY_PASS:-test@cake_wallet}" +DEST="${1:-}" + +if [[ -z "$DEST" ]]; then + DEST="$(cd ../.. && pwd)/android/app/key.jks" +elif [[ "$DEST" != /* ]]; then + DEST="$(cd ../.. && pwd)/$DEST" +fi + +P12="$(mktemp /tmp/cakewallet-dev-XXXXXX.p12)" +cleanup() { rm -f "$P12"; } +trap cleanup EXIT + +openssl pkcs12 -export \ + -inkey "$PWD/dev-test-key.pem" \ + -in "$PWD/dev-test-key.crt" \ + -out "$P12" \ + -name testKey \ + -passout "pass:${STORE_PASS}" \ + -certpbe PBE-SHA1-3DES \ + -keypbe PBE-SHA1-3DES \ + -macalg sha1 + +mkdir -p "$(dirname "$DEST")" +rm -f "$DEST" + +keytool -importkeystore \ + -srckeystore "$P12" \ + -srcstoretype PKCS12 \ + -srcstorepass "$STORE_PASS" \ + -destkeystore "$DEST" \ + -deststoretype JKS \ + -deststorepass "$STORE_PASS" \ + -destkeypass "$KEY_PASS" \ + -srcalias testKey \ + -destalias testKey \ + -noprompt + +echo "Wrote $DEST" diff --git a/scripts/linux/build_monero_all.sh b/scripts/linux/build_monero_all.sh index e17073d31f..a762f38163 100755 --- a/scripts/linux/build_monero_all.sh +++ b/scripts/linux/build_monero_all.sh @@ -7,16 +7,23 @@ cd "$(dirname "$0")" ../prepare_moneroc.sh -for COIN in monero wownero; +if [[ -n "${COIN:-}" ]]; then + COINS=("$COIN") +else + COINS=(monero wownero) +fi + +if [[ -n "${TARGET:-}" ]]; then + target="$TARGET" +elif [[ $(uname -m) == "arm64" || $(uname -m) == "aarch64" ]]; then + target="aarch64-linux-gnu" +else + target="x86_64-linux-gnu" +fi + +for COIN in "${COINS[@]}"; do pushd ../monero_c - # Determine target architecture based on system architecture - if [[ $(uname -m) == "arm64" || $(uname -m) == "aarch64" ]]; then - target="aarch64-linux-gnu" - else - target="x86_64-linux-gnu" - fi - ./build_single.sh ${COIN} $target -j$MAKE_JOB_COUNT popd done diff --git a/scripts/linux/build_torch.sh b/scripts/linux/build_torch.sh index 66865aa0ce..c85415cbee 100755 --- a/scripts/linux/build_torch.sh +++ b/scripts/linux/build_torch.sh @@ -2,8 +2,18 @@ set -x -e cd "$(dirname "$0")" +DEFAULT_TARGETS=( + "x86_64-linux-gnu" +) + +if [ "$#" -gt 0 ]; then + TARGETS=("$@") +else + TARGETS=("${DEFAULT_TARGETS[@]}") +fi + ../prepare_torch.sh cd ../torch_dart -./build.sh x86_64-linux-gnu +./build.sh "${TARGETS[@]}" diff --git a/scripts/linux/docker/Dockerfile.monero b/scripts/linux/docker/Dockerfile.monero index 3540b16b23..5f2e472ff1 100644 --- a/scripts/linux/docker/Dockerfile.monero +++ b/scripts/linux/docker/Dockerfile.monero @@ -13,6 +13,10 @@ RUN mkdir -p /w/scripts/linux COPY scripts/prepare_moneroc.sh /w/scripts/prepare_moneroc.sh COPY scripts/linux/build_monero_all.sh /w/scripts/linux/build_monero_all.sh COPY scripts/functions.sh /w/scripts/functions.sh +ARG COIN +ARG TARGET +ENV COIN=$COIN +ENV TARGET=$TARGET RUN /w/scripts/linux/build_monero_all.sh \ && rm -rf /w/scripts/monero_c/contrib/depends/simplybs \ /w/scripts/monero_c/monero_libwallet2_api_c/build \ diff --git a/scripts/linux/docker/Dockerfile.torch b/scripts/linux/docker/Dockerfile.torch index 14b752a1be..63d15ed3b0 100644 --- a/scripts/linux/docker/Dockerfile.torch +++ b/scripts/linux/docker/Dockerfile.torch @@ -14,7 +14,13 @@ RUN groupadd -g "$GID" "$USER" && \ RUN sudo chown $UID:$GID -R /w /root USER builder -RUN /w/scripts/linux/build_torch.sh && rm -rf /w/scripts/torch_dart/simplybs +ARG TARGET +RUN if [ -n "$TARGET" ]; then \ + /w/scripts/linux/build_torch.sh "$TARGET"; \ + else \ + /w/scripts/linux/build_torch.sh; \ + fi \ + && rm -rf /w/scripts/torch_dart/simplybs FROM --platform=linux/amd64 alpine COPY --from=build /w /w diff --git a/scripts/linux/docker/build.sh b/scripts/linux/docker/build.sh index f4a502d959..47ee899df6 100755 --- a/scripts/linux/docker/build.sh +++ b/scripts/linux/docker/build.sh @@ -4,50 +4,214 @@ cd "$(dirname $0)" CW_DOCKER_REGISTRY="${CW_DOCKER_REGISTRY:-localhost/cake-tech/cake_wallet}" CW_DOCKER_USE_CLOUD="${CW_DOCKER_USE_CLOUD:-}" +IMAGE_PREFIX="linux-deps-amd64" SCRIPT_DIR="$(pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +ONLY="" +TARGET="" +COIN="" +MERGE=false +EXTRACT=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --only) + ONLY="$2" + shift 2 + ;; + --target) + TARGET="$2" + shift 2 + ;; + --coin) + COIN="$2" + shift 2 + ;; + --merge) + MERGE=true + shift + ;; + --extract) + EXTRACT=true + shift + ;; + -h|--help) + echo "Usage: $0 [--only NAME] [--target TRIPLE] [--coin COIN] [--merge] [--extract]" + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 1 + ;; + esac +done + +MONERO_COINS=(monero wownero) +MONERO_TARGETS=(x86_64-linux-gnu) + image_exists() { docker image inspect "$1" &>/dev/null } tinysha() { - cat "$@" | sha256sum | cut -c1-6 + cat "$@" | sha256sum | cut -c1-6 +} + +slice_extra() { + if [[ -n "$COIN" && -n "$TARGET" ]]; then + echo "-${COIN}-${TARGET}" + elif [[ -n "$TARGET" ]]; then + echo "-${TARGET}" + else + echo "" + fi +} + +# Distinct from the unsuffixed all-in-one tag. The hash of slice suffixes +# invalidates the merge image if a coin or target is added or removed. +assembled_extra() { + local tmp + tmp="$(mktemp)" + printf '%s\n' "$@" > "$tmp" + echo "-merged-$(tinysha "$tmp")" + rm -f "$tmp" +} + +monero_assembled_extra() { + local extras=() + local coin t + for coin in "${MONERO_COINS[@]}"; do + for t in "${MONERO_TARGETS[@]}"; do + extras+=("-${coin}-${t}") + done + done + assembled_extra "${extras[@]}" +} + +component_extra() { + local name="$1" + local sliced + sliced="$(slice_extra)" + if [[ -n "$sliced" ]]; then + echo "$sliced" + return + fi + case "$name" in + monero) monero_assembled_extra ;; + *) echo "" ;; + esac +} + +img() { + local name="$1" + local version="$2" + local extra="${3:-}" + echo "$CW_DOCKER_REGISTRY:${IMAGE_PREFIX}-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}${extra}" +} + +remote_exists() { + local tag="$1" + if docker buildx version >/dev/null 2>&1; then + docker buildx imagetools inspect "$tag" >/dev/null 2>&1 + return + fi + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$tag" >/dev/null 2>&1 +} + +cached() { + local tag="$1" + if image_exists "$tag"; then + return 0 + fi + if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] && remote_exists "$tag"; then + return 0 + fi + return 1 +} + +maybe_push() { + local tag="$1" + if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]]; then + docker push "$tag" + fi } build() { local name="$1"; shift local version=$1; shift - if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] - then - set +e - docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | \ - grep "^${CW_DOCKER_REGISTRY}:linux-deps-amd64-" | \ - awk '{print $2}' | \ - xargs -r docker rmi - docker pull "$CW_DOCKER_REGISTRY:linux-deps-amd64-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}" - set -e - fi - if image_exists "$CW_DOCKER_REGISTRY:linux-deps-amd64-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}"; then - echo "==> skipping $name (image already exists)" + local extra + extra="$(component_extra "$name")" + local tag + tag="$(img "$name" "$version" "$extra")" + if cached "$tag"; then + echo "==> skipping $name (cached: $tag)" return 0 fi - echo "==> building $name" + echo "==> building $name${extra}" docker build \ --platform linux/amd64 \ --file "$SCRIPT_DIR/Dockerfile.${name}" \ - --tag "$CW_DOCKER_REGISTRY:linux-deps-amd64-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}" \ + --tag "$tag" \ "$@" \ "$REPO_ROOT" - if [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] - then - docker push "$CW_DOCKER_REGISTRY:linux-deps-amd64-${name}-$(tinysha "$SCRIPT_DIR/Dockerfile.${name}")-${version}" - fi + maybe_push "$tag" } -img() { - echo "$CW_DOCKER_REGISTRY:linux-deps-amd64-${1}-$(tinysha "$SCRIPT_DIR/Dockerfile.${1}")-${2}" +merge_slices() { + local name="$1" + local version="$2" + local extra="$3" + shift 3 + local dest + dest="$(img "$name" "$version" "$extra")" + if cached "$dest"; then + echo "==> skipping merge $name (cached: $dest)" + return 0 + fi + + local slices=("$@") + if [[ ${#slices[@]} -eq 0 ]]; then + echo "merge_slices: no slice images given" >&2 + exit 1 + fi + + local dockerfile + dockerfile="$(mktemp)" + local build_args=() + local i=0 + for slice in "${slices[@]}"; do + echo "ARG SRC${i}" >> "$dockerfile" + i=$((i + 1)) + done + i=0 + for slice in "${slices[@]}"; do + if ! cached "$slice"; then + echo "merge_slices: missing slice $slice" >&2 + rm -f "$dockerfile" + exit 1 + fi + echo "FROM --platform=linux/amd64 \${SRC${i}} AS s${i}" >> "$dockerfile" + build_args+=(--build-arg "SRC${i}=$slice") + i=$((i + 1)) + done + echo "FROM --platform=linux/amd64 alpine" >> "$dockerfile" + i=0 + for slice in "${slices[@]}"; do + echo "COPY --from=s${i} /w /w" >> "$dockerfile" + i=$((i + 1)) + done + + echo "==> merging $name from ${#slices[@]} slices -> $dest" + docker build \ + --platform linux/amd64 \ + --file "$dockerfile" \ + --tag "$dest" \ + "${build_args[@]}" \ + "$SCRIPT_DIR" + rm -f "$dockerfile" + maybe_push "$dest" } base_ver="latest" @@ -55,59 +219,133 @@ torch_ver="$(tinysha $SCRIPT_DIR/Dockerfile.torch $REPO_ROOT/scripts/prepare_tor reown_ver=$(tinysha $SCRIPT_DIR/Dockerfile.reown $REPO_ROOT/scripts/prepare_reown.sh $REPO_ROOT/scripts/android/build_reown_deps.sh) bitbox_ver=$(tinysha $SCRIPT_DIR/Dockerfile.bitbox $REPO_ROOT/scripts/build_bitbox_flutter.sh) monero_ver=$(tinysha $SCRIPT_DIR/Dockerfile.monero $REPO_ROOT/scripts/prepare_moneroc.sh $REPO_ROOT/scripts/linux/build_monero_all.sh) -mwebd_ver=$(tinysha $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f)) +mwebd_ver=$(tinysha $SCRIPT_DIR/Dockerfile.mwebd $REPO_ROOT/pubspec_overrides.yaml $(find $REPO_ROOT/cw_mweb/go -type f | sort)) zcash_ver=$(tinysha $SCRIPT_DIR/Dockerfile.zcash $REPO_ROOT/scripts/prepare_zcash.sh $REPO_ROOT/scripts/linux/build_zcash.sh) -echo $base_ver $torch_ver $reown_ver $bitbox_ver $monero_ver $mwebd_ver $zcash_ver > /tmp/docker_build_versions +echo $base_ver $torch_ver $reown_ver $bitbox_ver $monero_ver $mwebd_ver $zcash_ver "$(monero_assembled_extra)" > /tmp/docker_build_versions final_ver=$(tinysha /tmp/docker_build_versions) -docker create --name temp_extract $(img final $final_ver) \ -&& cd $REPO_ROOT \ -&& docker cp temp_extract:/w.top w.top \ -&& rsync -av w.top/ . \ -&& rm -rf w.top \ -&& docker rm temp_extract \ -&& echo "cache ok" \ -&& exit 0 \ -|| echo "cache miss oh" - -docker rm temp_extract || true +extract_final() { + local required="${1:-}" + local tag + tag="$(img final "$final_ver")" + if ! image_exists "$tag" && [[ "x$CW_DOCKER_USE_CLOUD" == "xtrue" ]] && remote_exists "$tag"; then + docker pull "$tag" + fi + docker rm -f temp_extract >/dev/null 2>&1 || true + if docker create --name temp_extract "$tag"; then + cd "$REPO_ROOT" + docker cp temp_extract:/w.top w.top + rsync -av w.top/ . + rm -rf w.top + docker rm temp_extract + echo "cache ok" + echo "$tag" > /tmp/cakewallet_docker + return 0 + fi + docker rm -f temp_extract >/dev/null 2>&1 || true + echo "cache miss oh" + if [[ "$required" == "required" ]]; then + echo "final image missing: $tag" >&2 + exit 1 + fi + return 1 +} -build base "$base_ver" +build_base() { + build base "$base_ver" +} -build bitbox "$bitbox_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_bitbox() { + build bitbox "$bitbox_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build mwebd "$mwebd_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_mwebd() { + build mwebd "$mwebd_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build reown "$reown_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_reown() { + build reown "$reown_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build monero "$monero_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_zcash() { + build zcash "$zcash_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +} -build zcash "$zcash_ver" --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_torch() { + build torch "$torch_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg TARGET="${TARGET}" +} -build torch "$torch_ver" \ - --build-arg BASE_IMAGE="$(img base "$base_ver")" +build_monero() { + if [[ "$MERGE" == true ]]; then + local slices=() + local coin t + for coin in "${MONERO_COINS[@]}"; do + for t in "${MONERO_TARGETS[@]}"; do + slices+=("$(img monero "$monero_ver" "-${coin}-${t}")") + done + done + merge_slices monero "$monero_ver" "$(monero_assembled_extra)" "${slices[@]}" + return 0 + fi + build monero "$monero_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg COIN="${COIN}" \ + --build-arg TARGET="${TARGET}" +} -build final $final_ver \ - --build-arg BASE_IMAGE="$(img base $base_ver)" \ - --build-arg TORCH_IMAGE="$(img torch $torch_ver)" \ - --build-arg REOWN_IMAGE="$(img reown $reown_ver)" \ - --build-arg BITBOX_IMAGE="$(img bitbox $bitbox_ver)" \ - --build-arg MONERO_IMAGE="$(img monero $monero_ver)" \ - --build-arg MWEBD_IMAGE="$(img mwebd $mwebd_ver)" \ - --build-arg ZCASH_IMAGE="$(img zcash $zcash_ver)" +build_final() { + build final "$final_ver" \ + --build-arg BASE_IMAGE="$(img base "$base_ver")" \ + --build-arg TORCH_IMAGE="$(img torch "$torch_ver")" \ + --build-arg REOWN_IMAGE="$(img reown "$reown_ver")" \ + --build-arg BITBOX_IMAGE="$(img bitbox "$bitbox_ver")" \ + --build-arg MONERO_IMAGE="$(img monero "$monero_ver" "$(monero_assembled_extra)")" \ + --build-arg MWEBD_IMAGE="$(img mwebd "$mwebd_ver")" \ + --build-arg ZCASH_IMAGE="$(img zcash "$zcash_ver")" + echo "done: $(img final $final_ver)" + echo "$(img final $final_ver)" > /tmp/cakewallet_docker +} -echo "done: $(img final $final_ver)" -echo $(img final $final_ver) > /tmp/cakewallet_docker +run_one() { + case "$1" in + base) build_base ;; + bitbox) build_bitbox ;; + mwebd) build_mwebd ;; + reown) build_reown ;; + zcash) build_zcash ;; + torch) build_torch ;; + monero) build_monero ;; + final) build_final ;; + *) + echo "Unknown component: $1" >&2 + exit 1 + ;; + esac +} +if [[ "$EXTRACT" == true ]]; then + extract_final required + exit 0 +fi -docker create --name temp_extract $(img final $final_ver) \ -&& cd $REPO_ROOT \ -&& docker cp temp_extract:/w.top w.top \ -&& rsync -av w.top/ . \ -&& rm -rf w.top \ -&& docker rm temp_extract \ -&& echo "cache ok" \ -&& exit 0 +if [[ -z "$ONLY" ]]; then + extract_final && exit 0 || true + ONLY="" + TARGET="" + COIN="" + MERGE=false + build_base + build_bitbox + build_mwebd + build_reown + build_monero + build_zcash + build_torch + build_final + extract_final required + exit 0 +fi -echo idk. -exit 1 +run_one "$ONLY"