ci: 增加前端构建发布流水线
新增 Gitea CI,覆盖 Android OTA 发布与校验、Android Release APK 打包,以及 Web Docker 镜像构建推送。 Made-with: Cursor
This commit is contained in:
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
.expo
|
||||||
|
dist
|
||||||
|
dist-web
|
||||||
|
dist-android
|
||||||
|
dist-android-update.zip
|
||||||
|
android
|
||||||
|
screenshots
|
||||||
|
*.log
|
||||||
196
.gitea/workflows/build.yml
Normal file
196
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
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
|
||||||
|
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: Set up Android SDK
|
||||||
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
|
- name: Install Android SDK packages
|
||||||
|
run: |
|
||||||
|
yes | sdkmanager --licenses
|
||||||
|
sdkmanager "platform-tools" "platforms;android-35" "build-tools;35.0.0"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build Android release APK
|
||||||
|
run: |
|
||||||
|
cd android
|
||||||
|
chmod +x gradlew
|
||||||
|
./gradlew clean :app:assembleRelease --no-daemon
|
||||||
|
|
||||||
|
- 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.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- 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"
|
||||||
15
Dockerfile.web
Normal file
15
Dockerfile.web
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npx expo export --platform web --output-dir dist-web
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
COPY --from=builder /app/dist-web /usr/share/nginx/html
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
Reference in New Issue
Block a user