CI: move workflow to .forgejo/workflows; self-contained Forgejo release; bump 0.1.1
Some checks failed
build-apk / build (push) Failing after 10m45s

- .forgejo/workflows/build-apk.yml (Forgejo's location): on v* tag, build signed
  APK using the Actions secrets and publish via the Forgejo API (curl + auto token),
  no external action. Best-effort NDK install for the RN version.
- Remove the old .gitea workflow to avoid double runs.
- Bump app version to 0.1.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hank 2026-07-07 14:18:06 -07:00
parent 02e2c750ea
commit 3d1505d2e7
4 changed files with 83 additions and 64 deletions

View file

@ -0,0 +1,72 @@
# Build a signed release APK and publish it as a Forgejo release on every v* tag.
# Obtainium then tracks releases and offers the update.
#
# Requires:
# - A Forgejo Actions RUNNER registered to this repo/instance (see docs/DISTRIBUTION.md),
# with a label this job's `runs-on` matches (adjust below to your runner's labels).
# - Repo Actions secrets: ANDROID_KEYSTORE_B64, ANDROID_KEYSTORE_PASSWORD,
# ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD (already set).
#
# Release: bump `expo.version` in app/app.json, then: git tag v0.1.1 && git push origin v0.1.1
name: build-apk
on:
push:
tags:
- 'v*'
jobs:
build:
# Match this to a label your runner registered with (commonly "docker" or "ubuntu-latest").
runs-on: docker
# A purpose-built RN Android image (Android SDK + NDK + Node). Runner needs Docker.
container:
image: reactnativecommunity/react-native-android:latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm install
- name: Build the API client
run: npm run build --workspace parksmarter-client
# RN 0.79 wants a specific NDK; install it if the image doesn't ship it (best-effort).
- name: Ensure NDK
run: |
yes | sdkmanager --licenses >/dev/null 2>&1 || true
sdkmanager "ndk;27.1.12297006" >/dev/null 2>&1 || true
- name: Restore signing keystore
run: echo "${{ secrets.ANDROID_KEYSTORE_B64 }}" | base64 -d > "$GITHUB_WORKSPACE/app/release.keystore"
- name: Expo prebuild (generate android/)
working-directory: app
run: CI=1 npx expo prebuild --platform android --no-install
- name: Assemble signed release
working-directory: app/android
env:
BBP_UPLOAD_STORE_FILE: ${{ github.workspace }}/app/release.keystore
BBP_UPLOAD_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
BBP_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
BBP_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: ./gradlew :app:assembleRelease -PreactNativeArchitectures=arm64-v8a --no-daemon
# Self-contained publish via the Forgejo API (no external action to fetch).
- name: Publish release + upload APK
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
TAG="${GITHUB_REF_NAME}"
APK="$(ls app/android/app/build/outputs/apk/release/*.apk | head -1)"
ASSET="BigBrainParking-${TAG}.apk"
API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
REL="$(curl -sf -X POST "$API/releases" -H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"$TAG\",\"target_commitish\":\"main\",\"name\":\"BigBrainParking $TAG\",\"body\":\"Install/update via Obtainium.\"}")"
ID="$(printf '%s' "$REL" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>console.log(JSON.parse(s).id))")"
curl -sf -X POST "$API/releases/$ID/assets?name=$ASSET" \
-H "Authorization: token $TOKEN" -F "attachment=@$APK"
echo "Published $TAG"

View file

@ -1,62 +0,0 @@
# Build a signed release APK and publish it as a Gitea/Forgejo release.
# Obtainium then tracks this repo's releases and offers updates.
#
# Triggers on any tag like v0.1.0. Requires a self-hosted Actions runner that has
# (or a container image that provides) the Android SDK + JDK 17 + Node 20.
#
# Required repo secrets (Settings -> Actions -> Secrets):
# ANDROID_KEYSTORE_B64 base64 of your release keystore (keep this key forever —
# Obtainium/F-Droid pin the signer; a new key = users must reinstall)
# ANDROID_KEYSTORE_PASSWORD
# ANDROID_KEY_ALIAS
# ANDROID_KEY_PASSWORD
#
# Generate the keystore once, locally:
# keytool -genkeypair -v -keystore bigbrainparking.keystore \
# -alias bigbrainparking -keyalg RSA -keysize 2048 -validity 10000
# base64 -w0 bigbrainparking.keystore # -> ANDROID_KEYSTORE_B64
name: build-apk
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
# A container with Android SDK + Node preinstalled keeps the runner simple:
container:
image: reactnativecommunity/react-native-android:latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm install
- name: Build the API client
run: npm run build --workspace parksmarter-client
- name: Restore signing keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_B64 }}" | base64 -d > "$PWD/app/release.keystore"
- name: Expo prebuild (generate android/)
working-directory: app
run: npx expo prebuild --platform android --no-install
- name: Assemble signed release
working-directory: app/android
env:
BBP_UPLOAD_STORE_FILE: ../release.keystore
BBP_UPLOAD_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
BBP_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
BBP_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: ./gradlew assembleRelease --no-daemon
- name: Publish release with APK
uses: akkuman/gitea-release-action@v1
with:
files: app/android/app/build/outputs/apk/release/*.apk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,11 +1,20 @@
import React from 'react'; import React, { useEffect } from 'react';
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import { SafeAreaProvider } from 'react-native-safe-area-context'; import { SafeAreaProvider } from 'react-native-safe-area-context';
import { AuthProvider } from '@/auth/AuthContext'; import { AuthProvider } from '@/auth/AuthContext';
import { ThemeProvider } from '@/theme/ThemeContext'; import { ThemeProvider } from '@/theme/ThemeContext';
import { RootNavigator } from '@/navigation/RootNavigator'; import { RootNavigator } from '@/navigation/RootNavigator';
import { ps } from '@/api/client';
import { initFileLogging } from '@/features/diagnostics/fileLogger';
export default function App() { export default function App() {
// Restore the file-logging preference; if on, also turn on request logging.
useEffect(() => {
void initFileLogging().then((on) => {
if (on) ps.setLogRequests(true);
});
}, []);
return ( return (
<SafeAreaProvider> <SafeAreaProvider>
<ThemeProvider> <ThemeProvider>

View file

@ -3,7 +3,7 @@
"name": "BigBrainParking", "name": "BigBrainParking",
"slug": "bigbrainparking", "slug": "bigbrainparking",
"scheme": "bigbrainparking", "scheme": "bigbrainparking",
"version": "0.1.0", "version": "0.1.1",
"orientation": "portrait", "orientation": "portrait",
"userInterfaceStyle": "automatic", "userInterfaceStyle": "automatic",
"newArchEnabled": true, "newArchEnabled": true,