build: remove expo.dev/EAS dependency, add local Android build pipeline
- Delete eas.json and strip extra.eas.projectId from app.json - Add plugins/withAndroidReleaseSigning.js — Expo config plugin that injects release signingConfig into the generated build.gradle during expo prebuild - Add build-apk.sh — self-contained build script (expo prebuild + Gradle) Reads keystore credentials from ~/.config/postiz-mobile/signing.env Outputs APK/AAB to dist/, wipes credentials from gradle.properties after build - Add install-android-sdk.sh — one-time Android SDK cmdline-tools bootstrap - Remove unused expo-task-manager dependency - Update .gitignore: android/, ios/, static-build/ excluded (generated) Build workflow: 1. eas credentials --platform android # export keystore once 2. ./install-android-sdk.sh # first time only 3. ./build-apk.sh # → dist/postiz-mobile-YYYYMMDD-HHMM.apk Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,16 +9,20 @@ dist/
|
|||||||
web-build/
|
web-build/
|
||||||
expo-env.d.ts
|
expo-env.d.ts
|
||||||
|
|
||||||
# Native
|
# Native — generated by expo prebuild, never committed
|
||||||
ios/
|
ios/
|
||||||
android/
|
android/
|
||||||
*.orig.*
|
*.orig.*
|
||||||
*.jks
|
*.jks
|
||||||
|
*.keystore
|
||||||
*.p8
|
*.p8
|
||||||
*.p12
|
*.p12
|
||||||
*.key
|
*.key
|
||||||
*.mobileprovision
|
*.mobileprovision
|
||||||
|
|
||||||
|
# Local build output
|
||||||
|
static-build/
|
||||||
|
|
||||||
# Metro
|
# Metro
|
||||||
.metro-health-check*
|
.metro-health-check*
|
||||||
|
|
||||||
|
|||||||
@@ -47,16 +47,12 @@
|
|||||||
"color": "#6366F1",
|
"color": "#6366F1",
|
||||||
"sounds": []
|
"sounds": []
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"./plugins/withAndroidReleaseSigning"
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true,
|
"typedRoutes": true,
|
||||||
"reactCompiler": true
|
"reactCompiler": true
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"eas": {
|
|
||||||
"projectId": "aeaaa2bd-3a27-4771-8e39-f2e14fe0e030"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+115
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build-apk.sh — Build a signed release APK locally without expo.dev / EAS.
|
||||||
|
#
|
||||||
|
# Prerequisites (first time only):
|
||||||
|
# 1. Export EAS keystore:
|
||||||
|
# eas credentials --platform android
|
||||||
|
# → "Download existing keystore" → save to ~/.config/postiz-mobile/postiz-mobile.jks
|
||||||
|
# 2. Fill in signing credentials:
|
||||||
|
# cp ~/.config/postiz-mobile/signing.env.example ~/.config/postiz-mobile/signing.env
|
||||||
|
# editor ~/.config/postiz-mobile/signing.env
|
||||||
|
# 3. Install Android SDK (if not already):
|
||||||
|
# ./install-android-sdk.sh
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./build-apk.sh # APK goes to dist/
|
||||||
|
# ./build-apk.sh --aab # AAB (Play Store) instead of APK
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SIGNING_ENV="$HOME/.config/postiz-mobile/signing.env"
|
||||||
|
DIST_DIR="$SCRIPT_DIR/dist"
|
||||||
|
BUILD_TYPE="${1:-}"
|
||||||
|
|
||||||
|
# ─── Colours ───────────────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[build]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
|
||||||
|
abort() { echo -e "${RED}[error]${NC} $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# ─── 1. Signing credentials ────────────────────────────────────────────────
|
||||||
|
if [ ! -f "$SIGNING_ENV" ]; then
|
||||||
|
abort "Missing signing.env.\n\n cp ~/.config/postiz-mobile/signing.env.example ~/.config/postiz-mobile/signing.env\n # then fill in your keystore path and passwords"
|
||||||
|
fi
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$SIGNING_ENV"
|
||||||
|
|
||||||
|
[ -z "${KEYSTORE_PATH:-}" ] && abort "KEYSTORE_PATH not set in signing.env"
|
||||||
|
[ -z "${KEYSTORE_ALIAS:-}" ] && abort "KEYSTORE_ALIAS not set in signing.env"
|
||||||
|
[ -z "${KEYSTORE_STORE_PASSWORD:-}" ] && abort "KEYSTORE_STORE_PASSWORD not set in signing.env"
|
||||||
|
[ -z "${KEYSTORE_KEY_PASSWORD:-}" ] && abort "KEYSTORE_KEY_PASSWORD not set in signing.env"
|
||||||
|
|
||||||
|
KEYSTORE_PATH_EXPANDED="${KEYSTORE_PATH/#\$HOME/$HOME}"
|
||||||
|
KEYSTORE_PATH_EXPANDED="${KEYSTORE_PATH_EXPANDED/#~/$HOME}"
|
||||||
|
|
||||||
|
[ ! -f "$KEYSTORE_PATH_EXPANDED" ] && abort "Keystore not found: $KEYSTORE_PATH_EXPANDED\n\nExport it from EAS:\n eas credentials --platform android\n → Download existing keystore → save to $KEYSTORE_PATH_EXPANDED"
|
||||||
|
|
||||||
|
# ─── 2. Android SDK ────────────────────────────────────────────────────────
|
||||||
|
if [ -z "${ANDROID_HOME:-}" ]; then
|
||||||
|
# Try common locations
|
||||||
|
for candidate in "$HOME/android-sdk" "$HOME/Android/Sdk" "/opt/android-sdk"; do
|
||||||
|
if [ -d "$candidate/platform-tools" ]; then
|
||||||
|
export ANDROID_HOME="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${ANDROID_HOME:-}" ]; then
|
||||||
|
abort "Android SDK not found.\n\nRun: ./install-android-sdk.sh\nOr set ANDROID_HOME manually in your shell."
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PATH="$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin"
|
||||||
|
info "Android SDK: $ANDROID_HOME"
|
||||||
|
|
||||||
|
# ─── 3. expo prebuild ──────────────────────────────────────────────────────
|
||||||
|
info "Running expo prebuild (Android)…"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
pnpm exec expo prebuild --platform android --clean --no-install
|
||||||
|
|
||||||
|
# ─── 4. Inject signing into gradle.properties ──────────────────────────────
|
||||||
|
info "Injecting signing credentials into gradle.properties…"
|
||||||
|
GRADLE_PROPS="$SCRIPT_DIR/android/gradle.properties"
|
||||||
|
|
||||||
|
# Remove any previous signing block written by this script
|
||||||
|
sed -i '/^# --- postiz-mobile release signing/,/^# --- end signing/d' "$GRADLE_PROPS" 2>/dev/null || true
|
||||||
|
|
||||||
|
cat >> "$GRADLE_PROPS" << EOF
|
||||||
|
|
||||||
|
# --- postiz-mobile release signing (injected by build-apk.sh)
|
||||||
|
MYAPP_UPLOAD_STORE_FILE=$KEYSTORE_PATH_EXPANDED
|
||||||
|
MYAPP_UPLOAD_STORE_PASSWORD=$KEYSTORE_STORE_PASSWORD
|
||||||
|
MYAPP_UPLOAD_KEY_ALIAS=$KEYSTORE_ALIAS
|
||||||
|
MYAPP_UPLOAD_KEY_PASSWORD=$KEYSTORE_KEY_PASSWORD
|
||||||
|
# --- end signing
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# ─── 5. Gradle build ───────────────────────────────────────────────────────
|
||||||
|
cd "$SCRIPT_DIR/android"
|
||||||
|
|
||||||
|
if [ "$BUILD_TYPE" = "--aab" ]; then
|
||||||
|
info "Building AAB (release)…"
|
||||||
|
./gradlew bundleRelease
|
||||||
|
ARTIFACT="app/build/outputs/bundle/release/app-release.aab"
|
||||||
|
EXT="aab"
|
||||||
|
else
|
||||||
|
info "Building APK (release)…"
|
||||||
|
./gradlew assembleRelease
|
||||||
|
ARTIFACT="app/build/outputs/apk/release/app-release.apk"
|
||||||
|
EXT="apk"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── 6. Copy to dist/ ──────────────────────────────────────────────────────
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
TIMESTAMP="$(date +%Y%m%d-%H%M)"
|
||||||
|
OUTPUT="$DIST_DIR/postiz-mobile-$TIMESTAMP.$EXT"
|
||||||
|
cp "$ARTIFACT" "$OUTPUT"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Build complete!"
|
||||||
|
echo -e " ${GREEN}→ $OUTPUT${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Wipe signing credentials from gradle.properties for safety
|
||||||
|
sed -i '/^# --- postiz-mobile release signing/,/^# --- end signing/d' "$GRADLE_PROPS"
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"cli": {
|
|
||||||
"version": ">= 16.0.0"
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"preview": {
|
|
||||||
"distribution": "internal",
|
|
||||||
"android": {
|
|
||||||
"buildType": "apk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"production": {
|
|
||||||
"android": {
|
|
||||||
"buildType": "app-bundle"
|
|
||||||
},
|
|
||||||
"ios": {
|
|
||||||
"resourceClass": "m-medium"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"submit": {
|
|
||||||
"production": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install-android-sdk.sh — Install Android SDK command-line tools only (no Android Studio).
|
||||||
|
# Installs to ~/android-sdk. Run once; takes ~1 GB of disk space.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SDK_DIR="$HOME/android-sdk"
|
||||||
|
CMDLINE_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-12266719_latest.zip"
|
||||||
|
CMDLINE_ZIP="/tmp/android-cmdline-tools.zip"
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[sdk-install]${NC} $*"; }
|
||||||
|
|
||||||
|
if [ -d "$SDK_DIR/cmdline-tools/latest/bin" ]; then
|
||||||
|
info "Android SDK already installed at $SDK_DIR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Downloading Android command-line tools…"
|
||||||
|
wget -q --show-progress -O "$CMDLINE_ZIP" "$CMDLINE_TOOLS_URL"
|
||||||
|
|
||||||
|
info "Extracting…"
|
||||||
|
mkdir -p "$SDK_DIR/cmdline-tools"
|
||||||
|
unzip -q "$CMDLINE_ZIP" -d "$SDK_DIR/cmdline-tools"
|
||||||
|
mv "$SDK_DIR/cmdline-tools/cmdline-tools" "$SDK_DIR/cmdline-tools/latest"
|
||||||
|
rm "$CMDLINE_ZIP"
|
||||||
|
|
||||||
|
export ANDROID_HOME="$SDK_DIR"
|
||||||
|
export PATH="$PATH:$SDK_DIR/cmdline-tools/latest/bin:$SDK_DIR/platform-tools"
|
||||||
|
|
||||||
|
info "Accepting licenses…"
|
||||||
|
yes | sdkmanager --licenses > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
info "Installing SDK components (platform-tools, build-tools 35, NDK 28)…"
|
||||||
|
sdkmanager \
|
||||||
|
"platform-tools" \
|
||||||
|
"platforms;android-35" \
|
||||||
|
"build-tools;35.0.0" \
|
||||||
|
"ndk;28.0.12433566"
|
||||||
|
|
||||||
|
info "Done. Add this to your ~/.bashrc or ~/.zshrc:"
|
||||||
|
echo ""
|
||||||
|
echo ' export ANDROID_HOME="$HOME/android-sdk"'
|
||||||
|
echo ' export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools"'
|
||||||
|
echo ""
|
||||||
|
info "Then reload your shell: source ~/.bashrc"
|
||||||
@@ -61,7 +61,6 @@
|
|||||||
"expo-clipboard": "~8.0.8",
|
"expo-clipboard": "~8.0.8",
|
||||||
"expo-notifications": "~0.32.17",
|
"expo-notifications": "~0.32.17",
|
||||||
"expo-secure-store": "~15.0.8",
|
"expo-secure-store": "~15.0.8",
|
||||||
"expo-task-manager": "~14.0.9",
|
|
||||||
"react-native-calendars": "^1.1314.0"
|
"react-native-calendars": "^1.1314.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
const { withAppBuildGradle } = require("@expo/config-plugins");
|
||||||
|
|
||||||
|
// Injects a proper release signingConfig into the generated build.gradle.
|
||||||
|
// Reads credentials from gradle.properties (populated by build-apk.sh at build time).
|
||||||
|
// This plugin runs during `expo prebuild` so android/ doesn't need to be committed.
|
||||||
|
module.exports = function withAndroidReleaseSigning(config) {
|
||||||
|
return withAppBuildGradle(config, (mod) => {
|
||||||
|
let contents = mod.modResults.contents;
|
||||||
|
|
||||||
|
if (contents.includes("MYAPP_UPLOAD_STORE_FILE")) {
|
||||||
|
return mod; // already patched
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseBlock = ` release {
|
||||||
|
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
||||||
|
storeFile file(MYAPP_UPLOAD_STORE_FILE)
|
||||||
|
storePassword MYAPP_UPLOAD_STORE_PASSWORD
|
||||||
|
keyAlias MYAPP_UPLOAD_KEY_ALIAS
|
||||||
|
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
// Insert release signingConfig after the closing brace of the debug block
|
||||||
|
contents = contents.replace(
|
||||||
|
/(signingConfigs\s*\{[\s\S]*?debug\s*\{[\s\S]*?\})/,
|
||||||
|
`$1\n${releaseBlock}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Switch the release buildType from debug signing to release signing
|
||||||
|
contents = contents.replace(
|
||||||
|
/(buildTypes[\s\S]*?release\s*\{[\s\S]*?)signingConfig\s+signingConfigs\.debug/,
|
||||||
|
"$1signingConfig signingConfigs.release"
|
||||||
|
);
|
||||||
|
|
||||||
|
mod.modResults.contents = contents;
|
||||||
|
return mod;
|
||||||
|
});
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user