diff --git a/.github/workflows/expo-build.yml b/.github/workflows/expo-build.yml index 91be810..2cd54e6 100644 --- a/.github/workflows/expo-build.yml +++ b/.github/workflows/expo-build.yml @@ -69,3 +69,38 @@ jobs: run: pnpm install --frozen-lockfile - name: TypeScript type check run: npx tsc --noEmit + + build: + runs-on: ubuntu-latest + needs: [lint, format, typecheck] + env: + EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 10.12.1 + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Ensure EAS CLI available + run: pnpm dlx eas-cli@latest --version + # No explicit login needed, Expo CLI uses EXPO_TOKEN env automatically + - name: Build Android APK + if: env.EXPO_TOKEN != '' + run: pnpm dlx eas-cli@latest build --platform android --non-interactive --profile preview --clear-cache --wait + - name: Download Android APK + if: env.EXPO_TOKEN != '' + run: pnpm dlx eas-cli@latest build:download --platform android --profile preview --latest --path ./app.apk + # ...existing code... + - name: Upload Android artifact + if: env.EXPO_TOKEN != '' + uses: actions/upload-artifact@v4 + with: + name: android-apk + path: ./app.apk diff --git a/app.json b/app.json index d81440b..c4cbf7b 100644 --- a/app.json +++ b/app.json @@ -51,7 +51,8 @@ "resizeMode": "contain", "backgroundColor": "#ffffff" } - ] + ], + "./app.plugin.js" ], "experiments": { "typedRoutes": true diff --git a/app.plugin.js b/app.plugin.js new file mode 100644 index 0000000..a2118cc --- /dev/null +++ b/app.plugin.js @@ -0,0 +1,64 @@ +// Inject core-splashscreen dependency to fix Android resource linking errors for Theme.SplashScreen +// This runs during EAS prebuild and modifies android/app/build.gradle +const { + withAppBuildGradle, + createRunOncePlugin, + WarningAggregator, +} = require("@expo/config-plugins"); + +const DEP_LINE = 'implementation("androidx.core:core-splashscreen:1.0.1")'; +const EXPO_AGG_LINE = 'implementation(project(":expo"))'; + +function ensureDependency(contents) { + // If gradle file isn't available during introspection, just return as-is. + if (typeof contents !== "string") return contents; + const hasDepsBlock = /dependencies\s*\{/m.test(contents); + if (!hasDepsBlock) return contents; + + let updated = contents; + // Ensure the :expo aggregator project is present so expo.* classes resolve + if (!updated.includes(EXPO_AGG_LINE)) { + updated = updated.replace( + /dependencies\s*\{/m, + (match) => `${match}\n ${EXPO_AGG_LINE}`, + ); + } + // Ensure the splashscreen dependency is present + if (!updated.includes(DEP_LINE)) { + updated = updated.replace( + /dependencies\s*\{/m, + (match) => `${match}\n ${DEP_LINE}`, + ); + } + + return updated; +} + +const withCoreSplashscreenDependency = (config) => + withAppBuildGradle(config, (config) => { + try { + const updated = ensureDependency(config.modResults?.contents); + if (typeof updated === "string") { + config.modResults.contents = updated; + } else { + // During introspection (e.g., EAS Read app config) the file may not exist yet. + WarningAggregator.addWarningAndroid( + "core-splashscreen", + "Skipped adding androidx.core:core-splashscreen; app/build.gradle not available during introspection.", + ); + } + } catch (e) { + WarningAggregator.addWarningAndroid( + "core-splashscreen", + `Failed to add androidx.core:core-splashscreen dependency: ${e?.message ?? e}`, + ); + } + return config; + }); + +// module exp +module.exports = createRunOncePlugin( + withCoreSplashscreenDependency, + "with-core-splashscreen-dependency", + "1.0.0", +); diff --git a/package.json b/package.json index 49e1367..8f70358 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ }, "devDependencies": { "@babel/core": "^7.25.2", + "@expo/config-plugins": "^7.9.2", "@types/jest": "^29.5.12", "@types/react": "~19.0.14", "@types/react-test-renderer": "^18.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37ac851..0b78e9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,6 +183,9 @@ importers: '@babel/core': specifier: ^7.25.2 version: 7.27.4 + '@expo/config-plugins': + specifier: ^7.9.2 + version: 7.9.2 '@types/jest': specifier: ^29.5.12 version: 29.5.14 @@ -1225,6 +1228,12 @@ packages: '@expo/config-plugins@10.1.2': resolution: {integrity: sha512-IMYCxBOcnuFStuK0Ay+FzEIBKrwW8OVUMc65+v0+i7YFIIe8aL342l7T4F8lR4oCfhXn7d6M5QPgXvjtc/gAcw==} + '@expo/config-plugins@7.9.2': + resolution: {integrity: sha512-sRU/OAp7kJxrCUiCTUZqvPMKPdiN1oTmNfnbkG4oPdfWQTpid3jyCH7ZxJEN5SI6jrY/ZsK5B/JPgjDUhuWLBQ==} + + '@expo/config-types@50.0.1': + resolution: {integrity: sha512-EZHMgzkWRB9SMHO1e9m8s+OMahf92XYTnsCFjxhSfcDrcEoSdFPyJWDJVloHZPMGhxns7Fi2+A+bEVN/hD4NKA==} + '@expo/config-types@53.0.5': resolution: {integrity: sha512-kqZ0w44E+HEGBjy+Lpyn0BVL5UANg/tmNixxaRMLS6nf37YsDrLk2VMAmeKMMk5CKG0NmOdVv3ngeUjRQMsy9g==} @@ -1241,9 +1250,16 @@ packages: resolution: {integrity: sha512-MYfPYBTMfrrNr07DALuLhG6EaLVNVrY/PXjEzsjWdWE4ZFn0yqI0IdHNkJG7t1gePT8iztHc7qnsx+oo/rDo6w==} hasBin: true + '@expo/fingerprint@0.6.1': + resolution: {integrity: sha512-ggLn6unI6qowlA1FihdQwPpLn16VJulYkvYAEL50gaqVahfNEglRQMSH2giZzjD0d6xq2/EQuUdFyHaJfyJwOQ==} + hasBin: true + '@expo/image-utils@0.7.6': resolution: {integrity: sha512-GKnMqC79+mo/1AFrmAcUcGfbsXXTRqOMNS1umebuevl3aaw+ztsYEFEiuNhHZW7PQ3Xs3URNT513ZxKhznDscw==} + '@expo/json-file@8.3.3': + resolution: {integrity: sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==} + '@expo/json-file@9.1.5': resolution: {integrity: sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==} @@ -1262,6 +1278,9 @@ packages: '@expo/package-manager@1.8.6': resolution: {integrity: sha512-gcdICLuL+nHKZagPIDC5tX8UoDDB8vNA5/+SaQEqz8D+T2C4KrEJc2Vi1gPAlDnKif834QS6YluHWyxjk0yZlQ==} + '@expo/plist@0.1.3': + resolution: {integrity: sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg==} + '@expo/plist@0.3.5': resolution: {integrity: sha512-9RYVU1iGyCJ7vWfg3e7c/NVyMFs8wbl+dMWZphtFtsqyN9zppGREU3ctlD3i8KUE0sCUTVnLjCWr+VeUIDep2g==} @@ -1565,6 +1584,9 @@ packages: resolution: {integrity: sha512-a2wsFlIhvd9ZqCD5KPRsbCQmbZi6KxhRN++jrqG0FUTEV5vY7MvjjUqDILwJd2ZBZsf7uiDuClCcKqA+EEdbvw==} engines: {node: '>=18'} + '@react-native/normalize-color@2.1.0': + resolution: {integrity: sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==} + '@react-native/normalize-colors@0.74.89': resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} @@ -1999,6 +2021,11 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@xmldom/xmldom@0.7.13': + resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} + engines: {node: '>=10.0.0'} + deprecated: this version is no longer supported, please update to at least 0.8.* + '@xmldom/xmldom@0.8.10': resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} @@ -3463,6 +3490,10 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + getenv@1.0.0: + resolution: {integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==} + engines: {node: '>=6'} + getenv@2.0.0: resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==} engines: {node: '>=6'} @@ -3482,6 +3513,10 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -5941,6 +5976,9 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@2.4.3: + resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} + write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -5996,6 +6034,10 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} + xmlbuilder@14.0.0: + resolution: {integrity: sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==} + engines: {node: '>=8.0'} + xmlbuilder@15.1.1: resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} engines: {node: '>=8.0'} @@ -7148,6 +7190,30 @@ snapshots: transitivePeerDependencies: - supports-color + '@expo/config-plugins@7.9.2': + dependencies: + '@expo/config-types': 50.0.1 + '@expo/fingerprint': 0.6.1 + '@expo/json-file': 8.3.3 + '@expo/plist': 0.1.3 + '@expo/sdk-runtime-versions': 1.0.0 + '@react-native/normalize-color': 2.1.0 + chalk: 4.1.2 + debug: 4.4.1 + find-up: 5.0.0 + getenv: 1.0.0 + glob: 7.1.6 + resolve-from: 5.0.0 + semver: 7.7.2 + slash: 3.0.0 + slugify: 1.6.6 + xcode: 3.0.1 + xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + + '@expo/config-types@50.0.1': {} + '@expo/config-types@53.0.5': {} '@expo/config@11.0.13': @@ -7203,6 +7269,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@expo/fingerprint@0.6.1': + dependencies: + '@expo/spawn-async': 1.7.2 + chalk: 4.1.2 + debug: 4.4.1 + find-up: 5.0.0 + minimatch: 3.1.2 + p-limit: 3.1.0 + resolve-from: 5.0.0 + transitivePeerDependencies: + - supports-color + '@expo/image-utils@0.7.6': dependencies: '@expo/spawn-async': 1.7.2 @@ -7215,6 +7293,12 @@ snapshots: temp-dir: 2.0.0 unique-string: 2.0.0 + '@expo/json-file@8.3.3': + dependencies: + '@babel/code-frame': 7.10.4 + json5: 2.2.3 + write-file-atomic: 2.4.3 + '@expo/json-file@9.1.5': dependencies: '@babel/code-frame': 7.10.4 @@ -7262,6 +7346,12 @@ snapshots: ora: 3.4.0 resolve-workspace-root: 2.0.0 + '@expo/plist@0.1.3': + dependencies: + '@xmldom/xmldom': 0.7.13 + base64-js: 1.5.1 + xmlbuilder: 14.0.0 + '@expo/plist@0.3.5': dependencies: '@xmldom/xmldom': 0.8.10 @@ -7742,6 +7832,8 @@ snapshots: '@react-native/js-polyfills@0.79.5': {} + '@react-native/normalize-color@2.1.0': {} + '@react-native/normalize-colors@0.74.89': {} '@react-native/normalize-colors@0.79.5': {} @@ -8222,6 +8314,8 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 + '@xmldom/xmldom@0.7.13': {} + '@xmldom/xmldom@0.8.10': {} '@xtuc/ieee754@1.2.0': {} @@ -9910,6 +10004,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + getenv@1.0.0: {} + getenv@2.0.0: {} glob-parent@5.1.2: @@ -9931,6 +10027,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@7.1.6: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -12760,6 +12865,12 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@2.4.3: + dependencies: + graceful-fs: 4.2.11 + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + write-file-atomic@4.0.2: dependencies: imurmurhash: 0.1.4 @@ -12787,6 +12898,8 @@ snapshots: xmlbuilder@11.0.1: {} + xmlbuilder@14.0.0: {} + xmlbuilder@15.1.1: {} xmlchars@2.2.0: {}