Files
frontend/.gitea/workflows/build.yml
lan 9fb78bb5ee
Some checks failed
Frontend CI / ota-android (push) Successful in 11m19s
Frontend CI / build-android-apk (push) Failing after 19m34s
Frontend CI / build-and-push-web (push) Failing after 1m12s
fix(ci): 修复 Gradle 配置,使用完整的 settings.gradle 和 build.gradle 文件替换
2026-03-18 00:19:22 +08:00

319 lines
11 KiB
YAML

name: Frontend CI
on:
push:
branches:
- main
- master
- develop
- dev
pull_request:
branches:
- main
- master
workflow_dispatch:
inputs:
publish_ota:
description: 'Publish Android OTA'
required: false
default: 'true'
env:
REGISTRY: code.littlelan.cn
IMAGE_NAME: carrot_bbs/frontend-web
OTA_PLATFORM: android
OTA_PUBLISH_URL: https://updates.littlelan.cn/admin/publish
OTA_MANIFEST_URL: https://updates.littlelan.cn/api/manifest
jobs:
ota-android:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.event.inputs.publish_ota == 'true')
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '25.6.1'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Resolve runtime version
id: runtime
run: |
RUNTIME_VERSION="$(node -p "require('./app.json').expo.version")"
echo "runtime_version=${RUNTIME_VERSION}" >> "$GITHUB_OUTPUT"
echo "Resolved runtimeVersion: ${RUNTIME_VERSION}"
- name: Export Android update bundle
run: |
rm -rf dist-android dist-android-update.zip
npx expo export --platform android --output-dir dist-android
npx expo config --type public --json > dist-android/expoConfig.json
- name: Archive Android update bundle
run: |
python - <<'PY'
import os
import zipfile
with zipfile.ZipFile('dist-android-update.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
for root, _, files in os.walk('dist-android'):
for name in files:
src = os.path.join(root, name)
arc = os.path.relpath(src, 'dist-android')
zf.write(src, arc)
PY
- name: Publish OTA
env:
OTA_AUTH_TOKEN: ${{ secrets.OTA_AUTH_TOKEN }}
run: |
test -n "${OTA_AUTH_TOKEN}" || (echo "Missing secret OTA_AUTH_TOKEN" && exit 1)
curl -fSs -X POST "${OTA_PUBLISH_URL}?runtimeVersion=${{ steps.runtime.outputs.runtime_version }}&platform=${OTA_PLATFORM}" \
-H "Authorization: Bearer ${OTA_AUTH_TOKEN}" \
-H "Content-Type: application/zip" \
--data-binary @"dist-android-update.zip"
- name: Verify OTA manifest
run: |
REMOTE="$(curl -sS "${OTA_MANIFEST_URL}" \
-H "expo-platform: ${OTA_PLATFORM}" \
-H "expo-runtime-version: ${{ steps.runtime.outputs.runtime_version }}" \
-H "expo-protocol-version: 1" \
| python -c "import sys, json, re; s=sys.stdin.read(); m=re.search(r'\\{\\\"id\\\":.*\\\"extra\\\":\\{.*\\}\\}', s); j=json.loads(m.group(0)); print(j['launchAsset']['url'].split('/')[-1].split('&')[0])")"
LOCAL="$(python -c "import json; m=json.load(open('dist-android/metadata.json')); print(m['fileMetadata']['android']['bundle'].split('/')[-1])")"
echo "Remote bundle: ${REMOTE}"
echo "Local bundle: ${LOCAL}"
test "${REMOTE}" = "${LOCAL}"
build-android-apk:
runs-on: ubuntu-latest
container:
image: reactnativecommunity/react-native-android:latest
env:
GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.parallel=false -Dorg.gradle.workers.max=1 -Xmx4g -XX:MaxMetaspaceSize=1g"
_JAVA_OPTIONS: "-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC"
NODE_ENV: "production"
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '25.6.1'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Generate Android native project
run: npx expo prebuild --platform android
- name: Configure Gradle with Aliyun Maven mirror
run: |
cd android
# Update settings.gradle with Aliyun Maven mirror
cat > settings.gradle << 'SETTINGS_EOF'
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}
def reactNativeGradlePlugin = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })")
}.standardOutput.asText.get().trim()
).getParentFile().absolutePath
includeBuild(reactNativeGradlePlugin)
def expoPluginsPath = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })")
}.standardOutput.asText.get().trim(),
"../android/expo-gradle-plugin"
).absolutePath
includeBuild(expoPluginsPath)
}
plugins {
id("com.facebook.react.settings")
id("expo-autolinking-settings")
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
}
}
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
ex.autolinkLibrariesFromCommand()
} else {
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
}
}
expoAutolinking.useExpoModules()
rootProject.name = '萝卜社区'
expoAutolinking.useExpoVersionCatalog()
include ':app'
includeBuild(expoAutolinking.reactNativeGradlePlugin)
SETTINGS_EOF
# Update build.gradle with Aliyun Maven mirror
cat > build.gradle << 'BUILD_EOF'
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}
dependencies {
classpath('com.android.tools.build:gradle')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
}
}
apply plugin: "expo-root-project"
apply plugin: "com.facebook.react.rootproject"
BUILD_EOF
# Update gradle.properties
cat > gradle.properties << 'PROPS_EOF'
org.gradle.daemon=false
org.gradle.parallel=false
org.gradle.configureondemand=false
org.gradle.workers.max=1
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError
android.enableJetifier=false
android.useAndroidX=true
hermesEnabled=true
reactNativeArchitectures=arm64-v8a
newArchEnabled=true
edgeToEdgeEnabled=true
expo.gif.enabled=true
expo.webp.enabled=true
expo.webp.animated=false
expo.useLegacyPackaging=false
PROPS_EOF
- name: Build Android release APK (arm64 only)
run: |
cd android
chmod +x gradlew
./gradlew :app:assembleRelease -PreactNativeArchitectures=arm64-v8a --max-workers=1
- name: Upload APK artifact
uses: actions/upload-artifact@v3
with:
name: carrot-bbs-android-release-apk
path: android/app/build/outputs/apk/release/app-release.apk
build-and-push-web:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to Gitea Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.GIT_USERNAME }}
password: ${{ secrets.GIT_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.web
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
provenance: false
- name: Show image tags
run: |
echo "Built image tags:"
echo "${{ steps.meta.outputs.tags }}"
echo "Digest: ${{ steps.meta.outputs.digest }}"
- name: Summary
if: always()
run: |
echo "## Frontend CI Summary" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "**Web image:** ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> "$GITHUB_STEP_SUMMARY"
echo "**Tags:**" >> "$GITHUB_STEP_SUMMARY"
echo "${{ steps.meta.outputs.tags }}" >> "$GITHUB_STEP_SUMMARY"
echo "**Digest:** ${{ steps.meta.outputs.digest }}" >> "$GITHUB_STEP_SUMMARY"