5151 artifact_name : ${{ steps.determine-target-paths.outputs.artifact_name }}
5252
5353 steps :
54+ - name : Checkout repo (Namespace)
55+ uses : namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
56+ if : ${{ inputs.runner_provider == 'namespace' }}
5457 - name : Checkout repo
5558 uses : actions/checkout@v6
59+ if : ${{ inputs.runner_provider != 'namespace' }}
5660
5761 - name : Check force-builds override
5862 id : force-builds
@@ -64,13 +68,29 @@ jobs:
6468 if : ${{ inputs.runner_provider == 'namespace' }}
6569 uses : namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1
6670 with :
71+ cache : |
72+ gradle
73+ maven
6774 path : |
6875 ~/.cache/yarn
6976 .metamask
7077 node_modules
7178 .yarn/cache
7279 ${{ env.GRADLE_USER_HOME }}/caches
7380 ${{ env.GRADLE_USER_HOME }}/wrapper
81+ ${{ env.GRADLE_USER_HOME }}/apk-cache
82+
83+ - name : Configure Gradle remote build cache
84+ if : ${{ inputs.runner_provider == 'namespace' }}
85+ run : |
86+ mkdir -p "$GRADLE_USER_HOME/init.d"
87+ REF="${GITHUB_REF_NAME}"
88+ PUSH=true
89+ if [[ "$REF" != "main" && "$REF" != release/* && "$REF" != stable/* ]]; then
90+ PUSH=false
91+ fi
92+ nsc cache gradle setup --push="$PUSH" --init-gradle "$GRADLE_USER_HOME/init.d/namespace-cache.init.gradle"
93+ echo "Gradle remote build cache configured (push=$PUSH) at $GRADLE_USER_HOME/init.d/namespace-cache.init.gradle"
7494
7595 - name : Report source fingerprint
7696 run : |
@@ -102,6 +122,50 @@ jobs:
102122 exit 1
103123 fi
104124
125+ - name : Check Namespace E2E APK cache
126+ id : namespace-apk-cache
127+ if : ${{ inputs.runner_provider == 'namespace' && steps.force-builds.outputs.force != 'true' && inputs.source-fingerprint != '' }}
128+ shell : bash
129+ env :
130+ APK_TARGET : ${{ steps.determine-target-paths.outputs.apk-target-path }}
131+ TEST_APK_TARGET : ${{ steps.determine-target-paths.outputs.test-apk-target-path }}
132+ ARTIFACT_NAME : ${{ steps.determine-target-paths.outputs.artifact_name }}
133+ FINGERPRINT : ${{ inputs.source-fingerprint }}
134+ CACHE_GENERATION : ${{ env.CACHE_GENERATION }}
135+ GRADLE_FILES_HASH : ${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
136+ REF_NAME : ${{ github.ref_name }}
137+ BUILD_TYPE : ${{ inputs.build_type }}
138+ run : |
139+ MARKER="${GRADLE_USER_HOME}/apk-cache/.e2e-apk-cache-marker"
140+ APK_CACHED="${GRADLE_USER_HOME}/apk-cache/${ARTIFACT_NAME}.apk"
141+ TEST_APK_CACHED="${GRADLE_USER_HOME}/apk-cache/${ARTIFACT_NAME}-androidTest.apk"
142+ EXPECTED="${REF_NAME}"$'\n'"${BUILD_TYPE}"$'\n'"${CACHE_GENERATION}"$'\n'"${FINGERPRINT}"$'\n'"${GRADLE_FILES_HASH}"
143+ echo "--- Debug: APK cache marker diagnostics ---"
144+ echo "MARKER=$MARKER"
145+ echo "APK_CACHED=$APK_CACHED"
146+ echo "TEST_APK_CACHED=$TEST_APK_CACHED"
147+ echo "marker exists: $([[ -f "${MARKER}" ]] && echo yes || echo NO)"
148+ echo "apk exists: $([[ -f "${APK_CACHED}" ]] && echo yes || echo NO)"
149+ echo "test-apk exists: $([[ -f "${TEST_APK_CACHED}" ]] && echo yes || echo NO)"
150+ ls -la "${GRADLE_USER_HOME}/apk-cache/" 2>&1 || echo "apk-cache dir not found"
151+ if [[ -f "${MARKER}" ]]; then
152+ echo "--- marker contents ---"
153+ cat "${MARKER}"
154+ echo "--- expected ---"
155+ echo "${EXPECTED}"
156+ echo "--- match: $([[ "$(<"${MARKER}")" == "${EXPECTED}" ]] && echo YES || echo NO) ---"
157+ fi
158+ if [[ -f "${MARKER}" ]] && [[ -f "${APK_CACHED}" ]] && [[ -f "${TEST_APK_CACHED}" ]] && [[ "$(<"${MARKER}")" == "${EXPECTED}" ]]; then
159+ echo "cache-hit=true" >> "${GITHUB_OUTPUT}"
160+ echo "Namespace APK cache hit — restoring APKs to build output dirs."
161+ mkdir -p "${APK_TARGET}" "${TEST_APK_TARGET}"
162+ cp "${APK_CACHED}" "${APK_TARGET}/"
163+ cp "${TEST_APK_CACHED}" "${TEST_APK_TARGET}/"
164+ else
165+ echo "cache-hit=false" >> "${GITHUB_OUTPUT}"
166+ echo "Namespace APK cache miss — full build required."
167+ fi
168+
105169 - name : Find reusable build from prior run
106170 id : find-reusable-build
107171 if : ${{ inputs.runner_provider != 'namespace' && steps.force-builds.outputs.force != 'true' && inputs.source-fingerprint != '' }}
@@ -167,7 +231,10 @@ jobs:
167231 - name : Compute native-build gate
168232 id : gate
169233 run : |
170- if [[ "${{ steps.find-reusable-build.outputs.found }}" == "true" \
234+ if [[ "${{ steps.namespace-apk-cache.outputs.cache-hit }}" == "true" ]]; then
235+ echo "needs-native-build=false" >> "$GITHUB_OUTPUT"
236+ echo "Namespace APK cache hit; heavy Android setup + Gradle restore will be skipped."
237+ elif [[ "${{ steps.find-reusable-build.outputs.found }}" == "true" \
171238 && "${{ steps.download-reusable-apk.outcome }}" == "success" \
172239 && "${{ steps.download-reusable-test-apk.outcome }}" == "success" ]]; then
173240 echo "needs-native-build=false" >> "$GITHUB_OUTPUT"
@@ -266,6 +333,14 @@ jobs:
266333 # sibling "Restore Gradle dependencies from branch cache" step above.
267334 key : gradle-main-${{ env.GRADLE_CACHE_VERSION }}-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
268335
336+ - name : Configure Namespace Gradle remote cache
337+ id : namespace-gradle-cache
338+ if : ${{ inputs.runner_provider == 'namespace' && steps.gate.outputs.needs-native-build == 'true' }}
339+ run : |
340+ nsc cache gradle setup --init-gradle "$RUNNER_TEMP/namespace-gradle-init.gradle"
341+ echo "init-script=$RUNNER_TEMP/namespace-gradle-init.gradle" >> "$GITHUB_OUTPUT"
342+ shell : bash
343+
269344 - name : Build Android E2E APKs
270345 if : ${{ steps.gate.outputs.needs-native-build == 'true' }}
271346 run : |
@@ -283,6 +358,7 @@ jobs:
283358 IGNORE_BOXLOGS_DEVELOPMENT : true
284359 GITHUB_CI : ' true'
285360 CI : ' true'
361+ GRADLE_INIT_SCRIPT : ${{ steps.namespace-gradle-cache.outputs.init-script }}
286362 NODE_OPTIONS : ' --max-old-space-size=4096'
287363 # Limit Metro workers to prevent OOM (each worker uses ~3GB)
288364 METRO_MAX_WORKERS : ' 4'
@@ -315,6 +391,27 @@ jobs:
315391 MM_INFURA_PROJECT_ID : ${{ secrets.MM_INFURA_PROJECT_ID }}
316392 MM_PREDICT_GTM_MODAL_ENABLED : ' false'
317393
394+ - name : Record Namespace E2E APK cache marker
395+ if : ${{ inputs.runner_provider == 'namespace' && steps.gate.outputs.needs-native-build == 'true' && inputs.source-fingerprint != '' }}
396+ shell : bash
397+ env :
398+ APK_SOURCE : ${{ steps.determine-target-paths.outputs.apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}.apk
399+ TEST_APK_SOURCE : ${{ steps.determine-target-paths.outputs.test-apk-target-path }}/${{ steps.determine-target-paths.outputs.artifact_name }}-androidTest.apk
400+ ARTIFACT_NAME : ${{ steps.determine-target-paths.outputs.artifact_name }}
401+ FINGERPRINT : ${{ inputs.source-fingerprint }}
402+ CACHE_GENERATION : ${{ env.CACHE_GENERATION }}
403+ GRADLE_FILES_HASH : ${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
404+ REF_NAME : ${{ github.ref_name }}
405+ BUILD_TYPE : ${{ inputs.build_type }}
406+ run : |
407+ CACHE_DIR="${GRADLE_USER_HOME}/apk-cache"
408+ mkdir -p "${CACHE_DIR}"
409+ cp "${APK_SOURCE}" "${CACHE_DIR}/${ARTIFACT_NAME}.apk"
410+ cp "${TEST_APK_SOURCE}" "${CACHE_DIR}/${ARTIFACT_NAME}-androidTest.apk"
411+ EXPECTED="${REF_NAME}"$'\n'"${BUILD_TYPE}"$'\n'"${CACHE_GENERATION}"$'\n'"${FINGERPRINT}"$'\n'"${GRADLE_FILES_HASH}"
412+ printf '%s\n' "${EXPECTED}" > "${CACHE_DIR}/.e2e-apk-cache-marker"
413+ echo "Namespace APK cache marker + APKs written to ${CACHE_DIR}."
414+
318415 - name : Repack APK with JS updates using @expo/repack-app
319416 if : ${{ steps.gate.outputs.needs-native-build != 'true' }}
320417 run : |
0 commit comments