ci: merge build into ci workflow with needs gate; update README
This commit is contained in:
@@ -1,85 +0,0 @@
|
|||||||
# Branch push (dev): builds APK, uploads as artifact named fabledapp-dev-<sha>
|
|
||||||
# Branch push (main): builds APK, uploads as artifact named fabledapp-<sha>
|
|
||||||
# Tag push (v26.03.09): builds APK, uploads artifact + creates Forgejo Release
|
|
||||||
#
|
|
||||||
# To cut a release:
|
|
||||||
# git tag v26.03.09 && git push origin v26.03.09
|
|
||||||
#
|
|
||||||
# Required secrets (repo → Settings → Secrets → Actions):
|
|
||||||
# RELEASE_TOKEN — Forgejo PAT with write:repository scope
|
|
||||||
name: Build APK
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main, dev]
|
|
||||||
tags: ["v*"]
|
|
||||||
paths:
|
|
||||||
- "lib/**"
|
|
||||||
- "pubspec.yaml"
|
|
||||||
- "pubspec.lock"
|
|
||||||
- "android/**"
|
|
||||||
- "assets/**"
|
|
||||||
- ".forgejo/workflows/build.yml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build release APK
|
|
||||||
runs-on: py3.12-node22
|
|
||||||
container:
|
|
||||||
image: ghcr.io/cirruslabs/flutter:stable
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: flutter pub get
|
|
||||||
|
|
||||||
- name: Build release APK
|
|
||||||
run: flutter build apk --release
|
|
||||||
|
|
||||||
- name: Set artifact name
|
|
||||||
id: artifact
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
|
|
||||||
echo "name=fabledapp-dev-${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "name=fabledapp-${{ github.sha }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: ${{ steps.artifact.outputs.name }}
|
|
||||||
path: build/app/outputs/flutter-apk/app-release.apk
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Create Forgejo release
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
env:
|
|
||||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
TAG: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
echo "Creating release $TAG..."
|
|
||||||
|
|
||||||
RESPONSE=$(curl -s -X POST \
|
|
||||||
"https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledApp/releases" \
|
|
||||||
-H "Authorization: token $RELEASE_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"tag_name\": \"$TAG\", \"name\": \"$TAG\", \"body\": \"\"}")
|
|
||||||
|
|
||||||
RELEASE_ID=$(echo "$RESPONSE" | grep -oE '"id":[[:space:]]*[0-9]+' | head -1 | grep -oE '[0-9]+')
|
|
||||||
|
|
||||||
if [ -z "$RELEASE_ID" ]; then
|
|
||||||
echo "Failed to create release. API response:"
|
|
||||||
echo "$RESPONSE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Release created with ID $RELEASE_ID, uploading APK..."
|
|
||||||
|
|
||||||
curl -s -X POST \
|
|
||||||
"https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledApp/releases/$RELEASE_ID/assets" \
|
|
||||||
-H "Authorization: token $RELEASE_TOKEN" \
|
|
||||||
-F "attachment=@build/app/outputs/flutter-apk/app-release.apk"
|
|
||||||
|
|
||||||
echo "Done — release $TAG is live at:"
|
|
||||||
echo "https://git.fabledsword.com/bvandeusen/FabledApp/releases/tag/$TAG"
|
|
||||||
@@ -1,13 +1,29 @@
|
|||||||
name: CI
|
# CI runs first; build only proceeds if analyze and test pass.
|
||||||
|
#
|
||||||
|
# Push to dev: analyze + test → build APK → upload artifact (fabledapp-dev-<sha>)
|
||||||
|
# Push to main: analyze + test only (no build — wait for release tag)
|
||||||
|
# Tag v26.03.11: analyze + test → build APK → upload artifact + create Forgejo Release
|
||||||
|
# Pull request: analyze + test only
|
||||||
|
#
|
||||||
|
# To cut a release:
|
||||||
|
# git tag v26.03.11 && git push origin v26.03.11
|
||||||
|
#
|
||||||
|
# Required secrets (repo → Settings → Secrets → Actions):
|
||||||
|
# RELEASE_TOKEN — Forgejo PAT with write:repository scope
|
||||||
|
name: CI & Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches: [main, dev]
|
||||||
|
tags: ["v*"]
|
||||||
paths:
|
paths:
|
||||||
- "lib/**"
|
- "lib/**"
|
||||||
- "test/**"
|
- "test/**"
|
||||||
- "pubspec.yaml"
|
- "pubspec.yaml"
|
||||||
- "pubspec.lock"
|
- "pubspec.lock"
|
||||||
- "analysis_options.yaml"
|
- "analysis_options.yaml"
|
||||||
|
- "android/**"
|
||||||
|
- "assets/**"
|
||||||
- ".forgejo/workflows/ci.yml"
|
- ".forgejo/workflows/ci.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
@@ -35,3 +51,72 @@ jobs:
|
|||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: flutter test
|
run: flutter test
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build release APK
|
||||||
|
needs: [analyze]
|
||||||
|
# Build on dev branch pushes and version tag pushes only.
|
||||||
|
# main branch pushes run CI for safety but do not build —
|
||||||
|
# the release tag (v*) is the sole trigger for a production APK.
|
||||||
|
if: |
|
||||||
|
github.event_name == 'push' &&
|
||||||
|
(github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/'))
|
||||||
|
runs-on: py3.12-node22
|
||||||
|
container:
|
||||||
|
image: ghcr.io/cirruslabs/flutter:stable
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: flutter pub get
|
||||||
|
|
||||||
|
- name: Build release APK
|
||||||
|
run: flutter build apk --release
|
||||||
|
|
||||||
|
- name: Set artifact name
|
||||||
|
id: artifact
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
|
||||||
|
echo "name=fabledapp-dev-${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "name=fabledapp-${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: ${{ steps.artifact.outputs.name }}
|
||||||
|
path: build/app/outputs/flutter-apk/app-release.apk
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Create Forgejo release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
env:
|
||||||
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
echo "Creating release $TAG..."
|
||||||
|
|
||||||
|
RESPONSE=$(curl -s -X POST \
|
||||||
|
"https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledApp/releases" \
|
||||||
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"tag_name\": \"$TAG\", \"name\": \"$TAG\", \"body\": \"\"}")
|
||||||
|
|
||||||
|
RELEASE_ID=$(echo "$RESPONSE" | grep -oE '"id":[[:space:]]*[0-9]+' | head -1 | grep -oE '[0-9]+')
|
||||||
|
|
||||||
|
if [ -z "$RELEASE_ID" ]; then
|
||||||
|
echo "Failed to create release. API response:"
|
||||||
|
echo "$RESPONSE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Release created with ID $RELEASE_ID, uploading APK..."
|
||||||
|
|
||||||
|
curl -s -X POST \
|
||||||
|
"https://git.fabledsword.com/api/v1/repos/bvandeusen/FabledApp/releases/$RELEASE_ID/assets" \
|
||||||
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
|
-F "attachment=@build/app/outputs/flutter-apk/app-release.apk"
|
||||||
|
|
||||||
|
echo "Done — release $TAG is live at:"
|
||||||
|
echo "https://git.fabledsword.com/bvandeusen/FabledApp/releases/tag/$TAG"
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
# Fabled — Android App
|
# Fabled — Android App
|
||||||
|
|
||||||
Native Android client for [FabledAssistant](https://github.com/yourusername/fabledassistant), a self-hosted AI productivity assistant.
|
Native Android client for FabledAssistant, a self-hosted AI second-brain and productivity assistant.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Notes** — create, edit, and browse markdown notes
|
- **Daily Briefing** — the primary screen; opens on launch. Shows your AI-compiled morning digest (tasks, calendar, weather, RSS) with a full conversation you can reply to inline. Refresh manually or browse past briefings from the overflow menu.
|
||||||
- **Tasks** — manage tasks with status (To Do / In Progress / Done) and priority
|
- **Quick Capture** — always-visible input bar above all tabs. Type a note or task and submit; multiple captures queue sequentially so the input is never blocked. Falls back to offline persistence when the server is unreachable.
|
||||||
- **Chat** — streaming AI conversations with real-time SSE response display
|
- **Library** — unified browsable list of notes, tasks, and projects with filter pills (All · Notes · Tasks · Projects). Tasks have a secondary status sub-filter and a tappable status icon to cycle todo → in progress → done without opening the editor. Inline search filters results live.
|
||||||
- **Quick Capture** — FAB shortcut to create a note or task from anywhere
|
- **Chat** — streaming AI conversations with real-time SSE display. Tap + to start a new conversation or open an existing one.
|
||||||
- **OAuth / SSO** — authenticates via your server's configured OIDC provider; local username/password login also supported if enabled on the server
|
- **Note & task editing** — full Markdown editor for notes, task editor with due date, priority, project, and milestone assignment.
|
||||||
- **Session persistence** — stays logged in across app restarts via a persistent cookie jar
|
- **OAuth / SSO** — authenticates via your server's OIDC provider; local username/password login also supported if enabled server-side.
|
||||||
- **Home screen widget** — tap to open the chat screen directly from the Android launcher
|
- **Session persistence** — stays logged in across restarts via a persistent cookie jar.
|
||||||
|
- **Auto-update** — checks your Forgejo releases on launch and prompts to download and install new APKs in-app.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A running [FabledAssistant](https://github.com/yourusername/fabledassistant) server (self-hosted)
|
- A running FabledAssistant server (self-hosted)
|
||||||
- Android 5.0+ (API 21)
|
- Android 5.0+ (API 21)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
@@ -38,20 +39,33 @@ On first launch, enter your FabledAssistant server URL (e.g. `https://fabled.exa
|
|||||||
|
|
||||||
```
|
```
|
||||||
lib/
|
lib/
|
||||||
├── main.dart # Entry point; resolves async deps before runApp
|
├── main.dart # Entry point; resolves async deps before runApp
|
||||||
├── app.dart # GoRouter + auth redirect guards + shell nav
|
├── app.dart # GoRouter + auth redirect guards + 3-tab shell
|
||||||
├── core/
|
├── core/
|
||||||
│ ├── constants.dart # Route name constants
|
│ ├── constants.dart # Route name constants
|
||||||
│ └── exceptions.dart # AppException hierarchy
|
│ ├── exceptions.dart # AppException hierarchy
|
||||||
|
│ └── theme.dart # Custom slate-indigo ColorScheme + Fraunces typography
|
||||||
├── data/
|
├── data/
|
||||||
│ ├── api/ # Dio HTTP layer (one class per resource)
|
│ ├── api/ # Dio HTTP layer (one class per resource)
|
||||||
│ ├── models/ # Plain Dart models with fromJson/toJson
|
│ ├── models/ # Plain Dart models with fromJson/toJson
|
||||||
│ └── repositories/ # Thin wrappers over API classes
|
│ └── repositories/ # Thin wrappers over API classes
|
||||||
└── providers/ # Riverpod providers (state + dependency wiring)
|
├── providers/ # Riverpod providers (state + dependency wiring)
|
||||||
screens/ # Flutter UI screens
|
│ ├── briefing_provider.dart # Today's briefing — optimistic UI, SSE, polling
|
||||||
|
│ └── capture_work_queue_provider.dart # Sequential in-memory capture queue
|
||||||
|
├── screens/
|
||||||
|
│ ├── briefing/ # BriefingScreen + BriefingHistoryScreen
|
||||||
|
│ ├── library/ # LibraryScreen (unified notes/tasks/projects)
|
||||||
|
│ ├── chat/ # ConversationsTabScreen + ChatScreen
|
||||||
|
│ ├── notes/ # NoteDetailScreen + NoteEditScreen
|
||||||
|
│ ├── tasks/ # TaskEditScreen
|
||||||
|
│ └── settings/ auth/ setup/ splash/
|
||||||
|
└── widgets/
|
||||||
|
├── chat_message_bubble.dart # Shared bubble (used by Chat + Briefing)
|
||||||
|
├── briefing_digest_card.dart # Expandable first-message card
|
||||||
|
└── library_item_card.dart # Note / task / project row widgets
|
||||||
```
|
```
|
||||||
|
|
||||||
**Key packages:** `flutter_riverpod`, `go_router`, `dio` + `cookie_jar`, `flutter_inappwebview`, `flutter_markdown`
|
**Key packages:** `flutter_riverpod`, `go_router`, `dio` + `cookie_jar`, `google_fonts`, `flutter_markdown_plus`
|
||||||
|
|
||||||
## Building a Release APK
|
## Building a Release APK
|
||||||
|
|
||||||
@@ -59,4 +73,25 @@ lib/
|
|||||||
flutter build apk --release
|
flutter build apk --release
|
||||||
```
|
```
|
||||||
|
|
||||||
The signed APK will be at `build/app/outputs/flutter-apk/app-release.apk`.
|
The APK will be at `build/app/outputs/flutter-apk/app-release.apk`.
|
||||||
|
|
||||||
|
## CI / CD
|
||||||
|
|
||||||
|
CI and build are defined in a single workflow (`.forgejo/workflows/ci.yml`) running on the shared `py3.12-node22` act runner with the `ghcr.io/cirruslabs/flutter:stable` container.
|
||||||
|
|
||||||
|
| Trigger | Analyze + Test | Build APK | Release |
|
||||||
|
|---------|---------------|-----------|---------|
|
||||||
|
| PR | ✓ | — | — |
|
||||||
|
| Push `dev` | ✓ | ✓ (artifact `fabledapp-dev-<sha>`) | — |
|
||||||
|
| Push `main` | ✓ | — | — |
|
||||||
|
| Tag `v*` | ✓ | ✓ (artifact `fabledapp-<sha>`) | ✓ Forgejo Release + APK attached |
|
||||||
|
|
||||||
|
The build job has `needs: [analyze]` — a failed analyze or test blocks the APK build.
|
||||||
|
|
||||||
|
To cut a release:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag v26.03.11 && git push origin v26.03.11
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires a `RELEASE_TOKEN` secret (Forgejo PAT with `write:repository` scope) set in repo Settings → Secrets → Actions.
|
||||||
|
|||||||
Reference in New Issue
Block a user