ci: remove GitHub-only workflows (deploy via .gitea nix workflow)
Build & Deploy (Nix) / build-and-deploy (api) (push) Successful in 1m47s
Build & Deploy (Nix) / build-and-deploy (ml-service) (push) Successful in 3m18s
Build & Deploy (Nix) / build-and-deploy (web) (push) Successful in 1m12s
Build & Deploy (Nix) / build-and-deploy (api) (push) Successful in 1m47s
Build & Deploy (Nix) / build-and-deploy (ml-service) (push) Successful in 3m18s
Build & Deploy (Nix) / build-and-deploy (web) (push) Successful in 1m12s
This commit is contained in:
@@ -1,203 +0,0 @@
|
|||||||
name: Build Android APK
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- 'apps/web/**'
|
|
||||||
- 'apps/tauri/**'
|
|
||||||
- 'packages/shared/**'
|
|
||||||
- '.github/workflows/android.yml'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'apps/web/**'
|
|
||||||
- 'apps/tauri/**'
|
|
||||||
- 'packages/shared/**'
|
|
||||||
- '.github/workflows/android.yml'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
VITE_API_BASE_URL: ${{ vars.VITE_API_BASE_URL || 'https://zeavisedu.asepharyana.my.id' }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-apk:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 45
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.version.outputs.version }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
fetch-tags: true
|
|
||||||
|
|
||||||
- name: Setup Bun
|
|
||||||
uses: oven-sh/setup-bun@v2
|
|
||||||
with:
|
|
||||||
bun-version: latest
|
|
||||||
|
|
||||||
- name: Cache Bun dependencies
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.bun/install/cache
|
|
||||||
node_modules
|
|
||||||
apps/*/node_modules
|
|
||||||
packages/*/node_modules
|
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', '**/package.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-bun-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: bun install
|
|
||||||
|
|
||||||
- name: Setup Java 21
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: temurin
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Setup Android SDK
|
|
||||||
uses: android-actions/setup-android@v3
|
|
||||||
with:
|
|
||||||
packages: 'platforms;android-36 build-tools;36.0.0'
|
|
||||||
- name: Pin NDK version
|
|
||||||
run: echo "ANDROID_NDK_HOME=${ANDROID_SDK_ROOT}/ndk/$(ls ${ANDROID_SDK_ROOT}/ndk | sort -V | head -1)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup Rust with Android targets
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
targets: >-
|
|
||||||
aarch64-linux-android,
|
|
||||||
armv7-linux-androideabi,
|
|
||||||
i686-linux-android,
|
|
||||||
x86_64-linux-android
|
|
||||||
|
|
||||||
- name: Cache Cargo
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
apps/tauri/target
|
|
||||||
key: ${{ runner.os }}-cargo-android-${{ hashFiles('apps/tauri/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-cargo-android-
|
|
||||||
|
|
||||||
- name: Cache Gradle
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-android-${{ hashFiles('apps/tauri/Cargo.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-android-
|
|
||||||
|
|
||||||
- name: Install Tauri CLI
|
|
||||||
run: cd apps/tauri && bun install
|
|
||||||
|
|
||||||
- name: Compute version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
# Get latest git tag, default to v0.1.0
|
|
||||||
LATEST_TAG=$(git tag --list 'v*' --sort=-v:refname | head -1)
|
|
||||||
if [ -z "$LATEST_TAG" ]; then
|
|
||||||
NEW_VERSION="0.1.0"
|
|
||||||
else
|
|
||||||
# Strip 'v' prefix, bump patch
|
|
||||||
BASE="${LATEST_TAG#v}"
|
|
||||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE"
|
|
||||||
PATCH=$((PATCH + 1))
|
|
||||||
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
|
|
||||||
fi
|
|
||||||
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
|
|
||||||
echo "New version: ${NEW_VERSION}"
|
|
||||||
|
|
||||||
# Update tauri.conf.json
|
|
||||||
jq --arg v "${NEW_VERSION}" '.version = $v' apps/tauri/tauri.conf.json > /tmp/tauri.conf.json && mv /tmp/tauri.conf.json apps/tauri/tauri.conf.json
|
|
||||||
|
|
||||||
# Update Cargo.toml
|
|
||||||
sed -i "s/^version = \".*\"/version = \"${NEW_VERSION}\"/" apps/tauri/Cargo.toml
|
|
||||||
|
|
||||||
echo "Updated tauri.conf.json and Cargo.toml to ${NEW_VERSION}"
|
|
||||||
|
|
||||||
- name: Init Tauri Android project
|
|
||||||
working-directory: apps/tauri
|
|
||||||
env:
|
|
||||||
JAVA_HOME: ${{ env.JAVA_HOME_21_X64 }}
|
|
||||||
ANDROID_HOME: ${{ env.ANDROID_SDK_ROOT }}
|
|
||||||
NDK_HOME: ${{ env.ANDROID_NDK_HOME }}
|
|
||||||
run: |
|
|
||||||
rm -rf gen/android
|
|
||||||
bun tauri android init
|
|
||||||
|
|
||||||
- name: Generate Android launcher icons from SVG logo
|
|
||||||
working-directory: apps/tauri
|
|
||||||
run: bun run scripts/generate-icons.js
|
|
||||||
|
|
||||||
- name: Patch AndroidManifest (CAMERA permission + deep link)
|
|
||||||
working-directory: apps/tauri
|
|
||||||
run: bash scripts/patch-android-manifest.sh
|
|
||||||
|
|
||||||
- name: Build Tauri Android APK
|
|
||||||
working-directory: apps/tauri
|
|
||||||
env:
|
|
||||||
JAVA_HOME: ${{ env.JAVA_HOME_21_X64 }}
|
|
||||||
ANDROID_HOME: ${{ env.ANDROID_SDK_ROOT }}
|
|
||||||
NDK_HOME: ${{ env.ANDROID_NDK_HOME }}
|
|
||||||
run: bun tauri android build --apk
|
|
||||||
|
|
||||||
- name: Decode keystore
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
env:
|
|
||||||
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
|
||||||
run: |
|
|
||||||
echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > apps/tauri/zeavis.keystore
|
|
||||||
|
|
||||||
- name: Sign APK
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
env:
|
|
||||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
|
||||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
|
||||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
APK_UNSIGNED=$(find apps/tauri/gen/android/app/build/outputs/apk -name '*.apk' ! -name '*-signed*' | head -1)
|
|
||||||
APK_SIGNED="apps/tauri/gen/android/app/build/outputs/apk/universal/release/zeavis-edu-v${{ steps.version.outputs.version }}.apk"
|
|
||||||
$ANDROID_SDK_ROOT/build-tools/36.0.0/apksigner sign \
|
|
||||||
--ks apps/tauri/zeavis.keystore \
|
|
||||||
--ks-pass "pass:${ANDROID_KEYSTORE_PASSWORD}" \
|
|
||||||
--ks-key-alias "${ANDROID_KEY_ALIAS}" \
|
|
||||||
--key-pass "pass:${ANDROID_KEY_PASSWORD}" \
|
|
||||||
--out "$APK_SIGNED" \
|
|
||||||
"$APK_UNSIGNED"
|
|
||||||
echo "signed_apk=${APK_SIGNED}" >> $GITHUB_ENV
|
|
||||||
echo "Signed APK: $APK_SIGNED"
|
|
||||||
ls -lh "$APK_SIGNED"
|
|
||||||
|
|
||||||
- name: Create Release and upload APK
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
id: release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
tag_name: v${{ steps.version.outputs.version }}
|
|
||||||
name: v${{ steps.version.outputs.version }}
|
|
||||||
body: |
|
|
||||||
ZeaVis Edu Android APK v${{ steps.version.outputs.version }}
|
|
||||||
|
|
||||||
📦 Built from ${{ github.sha }}
|
|
||||||
🔗 Triggered by ${{ github.actor }}
|
|
||||||
|
|
||||||
### Install
|
|
||||||
Download the APK below and install on your Android device.
|
|
||||||
|
|
||||||
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
||||||
files: |
|
|
||||||
${{ env.signed_apk }}
|
|
||||||
apps/tauri/gen/android/app/build/outputs/bundle/**/*.aab
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
name: Build and Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
VITE_API_BASE_URL: ${{ vars.VITE_API_BASE_URL || '' }}
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
service:
|
|
||||||
- name: web
|
|
||||||
dockerfile: apps/web/Dockerfile
|
|
||||||
- name: api
|
|
||||||
dockerfile: apps/api/Dockerfile
|
|
||||||
- name: ml
|
|
||||||
dockerfile: apps/ml-service/Dockerfile
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set image prefix
|
|
||||||
run: echo "IMAGE_PREFIX=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Set up Python for model download & export
|
|
||||||
if: matrix.service.name == 'ml'
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.11'
|
|
||||||
|
|
||||||
- name: Download ONNX model from Hugging Face
|
|
||||||
if: matrix.service.name == 'ml'
|
|
||||||
working-directory: Machine_Learning
|
|
||||||
env:
|
|
||||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -eu
|
|
||||||
echo "::group::Install huggingface_hub"
|
|
||||||
python -m pip install --upgrade pip -q
|
|
||||||
python -m pip install huggingface_hub -q
|
|
||||||
echo "::endgroup::"
|
|
||||||
|
|
||||||
echo "::group::Check HF_TOKEN"
|
|
||||||
if [ -z "${HF_TOKEN:-}" ]; then
|
|
||||||
echo "ERROR: HF_TOKEN secret is not set."
|
|
||||||
echo "Add it: https://github.com/ATLAS-PJK-GM007/ZeaVis-Edu/settings/secrets/actions"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "HF_TOKEN is set (length: ${#HF_TOKEN})"
|
|
||||||
echo "::endgroup::"
|
|
||||||
|
|
||||||
echo "::group::Download model files from Hugging Face"
|
|
||||||
python -c "
|
|
||||||
from huggingface_hub import hf_hub_download
|
|
||||||
import os, shutil
|
|
||||||
repo = 'MythEclipse2737/zeavis-edu-corn-leaf-classifier'
|
|
||||||
token = os.environ['HF_TOKEN']
|
|
||||||
base = os.path.abspath('.')
|
|
||||||
|
|
||||||
# Files sit at root of HF repo → copy to correct subdirs
|
|
||||||
# model.onnx goes to model/ for Docker COPY
|
|
||||||
os.makedirs(os.path.join(base, 'model'), exist_ok=True)
|
|
||||||
os.makedirs(os.path.join(base, 'best_model'), exist_ok=True)
|
|
||||||
|
|
||||||
# ONNX → model/model.onnx (Docker expects this path)
|
|
||||||
p = hf_hub_download(repo_id=repo, filename='model.onnx', token=token)
|
|
||||||
shutil.copy2(p, os.path.join(base, 'model', 'model.onnx'))
|
|
||||||
print('model/model.onnx OK')
|
|
||||||
|
|
||||||
# TFLite (optional, for edge)
|
|
||||||
p = hf_hub_download(repo_id=repo, filename='model.tflite', token=token)
|
|
||||||
shutil.copy2(p, os.path.join(base, 'model', 'model.tflite'))
|
|
||||||
print('model/model.tflite OK')
|
|
||||||
|
|
||||||
# Labels
|
|
||||||
p = hf_hub_download(repo_id=repo, filename='labels.json', token=token)
|
|
||||||
shutil.copy2(p, os.path.join(base, 'model', 'labels.json'))
|
|
||||||
print('model/labels.json OK')
|
|
||||||
|
|
||||||
# Keras model + calibration for re-export
|
|
||||||
p = hf_hub_download(repo_id=repo, filename='best_model.keras', token=token)
|
|
||||||
shutil.copy2(p, os.path.join(base, 'best_model', 'best_model.keras'))
|
|
||||||
print('best_model/best_model.keras OK')
|
|
||||||
|
|
||||||
p = hf_hub_download(repo_id=repo, filename='calibration.json', token=token)
|
|
||||||
shutil.copy2(p, os.path.join(base, 'best_model', 'calibration.json'))
|
|
||||||
print('best_model/calibration.json OK')
|
|
||||||
"
|
|
||||||
ls -lh model/model.onnx model/model.tflite model/labels.json best_model/best_model.keras 2>/dev/null
|
|
||||||
echo "::endgroup::"
|
|
||||||
|
|
||||||
- name: Log in to GHCR
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ${{ env.IMAGE_PREFIX }}/${{ matrix.service.name }}
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=sha
|
|
||||||
|
|
||||||
- name: Build and push image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ${{ matrix.service.dockerfile }}
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
VITE_API_BASE_URL=${{ env.VITE_API_BASE_URL }}
|
|
||||||
cache-from: type=gha,scope=${{ matrix.service.name }}
|
|
||||||
cache-to: type=gha,mode=max,scope=${{ matrix.service.name }}
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.ref == 'refs/heads/main'
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: read
|
|
||||||
steps:
|
|
||||||
- name: Validate deploy secrets
|
|
||||||
env:
|
|
||||||
VPS_HOST: ${{ secrets.VPS_HOST }}
|
|
||||||
VPS_USER: ${{ secrets.VPS_USER }}
|
|
||||||
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
|
|
||||||
run: |
|
|
||||||
if [ -z "$VPS_HOST" ] || [ -z "$VPS_USER" ] || [ -z "$VPS_SSH_KEY" ]; then
|
|
||||||
echo "Missing deploy secrets: VPS_HOST, VPS_USER, VPS_SSH_KEY." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Deploy to VPS
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.VPS_HOST }}
|
|
||||||
username: ${{ secrets.VPS_USER }}
|
|
||||||
key: ${{ secrets.VPS_SSH_KEY }}
|
|
||||||
passphrase: ${{ secrets.VPS_SSH_PASSPHRASE }}
|
|
||||||
port: ${{ secrets.VPS_PORT || 22 }}
|
|
||||||
script: |
|
|
||||||
set -e
|
|
||||||
DEPLOY_PATH="${DEPLOY_PATH:-/opt/ZeaVis-Edu}"
|
|
||||||
REPO_SLUG="$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')"
|
|
||||||
|
|
||||||
if [ ! -d "$DEPLOY_PATH/.git" ]; then
|
|
||||||
mkdir -p "$DEPLOY_PATH"
|
|
||||||
git clone https://github.com/${{ github.repository }}.git "$DEPLOY_PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$DEPLOY_PATH"
|
|
||||||
git fetch origin main
|
|
||||||
git reset --hard origin/main
|
|
||||||
|
|
||||||
{
|
|
||||||
printf 'GITHUB_REPOSITORY=%s\n' "$REPO_SLUG"
|
|
||||||
cat << 'ENVEOF'
|
|
||||||
DATABASE_URL=${{ secrets.DATABASE_URL }}
|
|
||||||
SESSION_SECRET=${{ secrets.SESSION_SECRET }}
|
|
||||||
WEB_APP_URL=https://zeavisedu.asepharyana.my.id
|
|
||||||
ML_SERVICE_URL=http://zeavis-ml:8000
|
|
||||||
TS_IP=${{ vars.TS_IP || '100.96.248.86' }}
|
|
||||||
GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}
|
|
||||||
GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}
|
|
||||||
GOOGLE_REDIRECT_URI=https://zeavisedu.asepharyana.my.id/api/v1/auth/google/callback
|
|
||||||
ENVEOF
|
|
||||||
} > .env
|
|
||||||
|
|
||||||
docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
docker network create app-shared-net 2>/dev/null || true
|
|
||||||
docker network create telemetry-net 2>/dev/null || true
|
|
||||||
docker compose down --remove-orphans || true
|
|
||||||
docker rm -f zeavis-web zeavis-api zeavis-ml zeavis-node-exporter 2>/dev/null || true
|
|
||||||
docker compose pull
|
|
||||||
docker compose up -d
|
|
||||||
# Wait for containers to be healthy (up to 60s)
|
|
||||||
wait_container() {
|
|
||||||
local name=$1
|
|
||||||
for i in $(seq 1 30); do
|
|
||||||
docker compose ps | grep -q "${name}.*Up" && return 0
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
wait_container zeavis-web || exit 1
|
|
||||||
wait_container zeavis-api || exit 1
|
|
||||||
wait_container zeavis-ml || exit 1
|
|
||||||
wait_container zeavis-node-exporter || echo "⚠️ node_exporter not running (non-fatal)"
|
|
||||||
docker compose ps
|
|
||||||
Reference in New Issue
Block a user