Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/deploy-pages-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Deploys the example app to Cloudflare Pages PREVIEW on every push to develop.
# Always-fresh URL for testing the latest features before a tagged release.
#
# PR previews are handled automatically by CF Pages' native GitHub integration
# (or this same action on pull_request events) — each PR gets its own URL.
#
# Setup (one-time): same repo secrets as production:
# CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID
name: Deploy (Preview — develop)

on:
push:
branches: [develop]
# PR previews: each PR deploys to its own <branch>.coflui-preview.pages.dev
pull_request:
branches: [develop]

permissions:
contents: read
pull-requests: write # so the bot can comment the preview URL on the PR

jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- name: Build example web
# --no-tree-shake-icons required: IconResolver creates IconData at
# runtime from JSON, which breaks the icon font tree-shaker.
run: flutter pub get && flutter build web --release --no-tree-shake-icons
working-directory: example

- name: Capture branch name (for PR preview alias)
id: branch
run: echo "name=${GITHUB_HEAD_REF:-develop}" >> "$GITHUB_OUTPUT"

- name: Ensure Pages project exists (idempotent)
# Creates the project on first run; subsequent runs no-op (it exists).
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages project create coflui --production-branch=main
continue-on-error: true # already exists on subsequent runs

- name: Deploy to Cloudflare Pages (preview)
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy example/build/web --project-name=coflui --branch=${{ steps.branch.outputs.name }}

- name: Comment preview URL on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const url = '${{ steps.deploy.outputs.deployment-url }}';
if (!url) return;
const body = `🌐 **Preview deployed:** ${url}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.startsWith('🌐 **Preview deployed:**'));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
54 changes: 54 additions & 0 deletions .github/workflows/deploy-pages-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Deploys the example app to Cloudflare Pages PRODUCTION — but ONLY on a
# version tag push (v0.3.0, v0.3.1, …). Stable URL shared externally.
#
# Setup (one-time):
# CF Account Token + Account ID as repo secrets:
# CLOUDFLARE_API_TOKEN — Pages:Edit permission
# CLOUDFLARE_ACCOUNT_ID
#
# Trigger: push a tag → production deploy to coflui.pages.dev
# git tag v0.3.0 && git push origin v0.3.0
name: Deploy (Production — tag)

on:
push:
tags:
- 'v*'

permissions:
contents: read
deployments: write

jobs:
deploy-production:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- name: Build example web
# --no-tree-shake-icons is required: IconResolver creates IconData at
# runtime from JSON, which breaks Flutter's icon font tree-shaker.
run: flutter pub get && flutter build web --release --no-tree-shake-icons
working-directory: example

- name: Ensure Pages project exists (idempotent)
# Creates the project on first run; subsequent runs no-op (it exists).
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages project create coflui --production-branch=main
continue-on-error: true # already exists on subsequent runs

- name: Deploy to Cloudflare Pages (production)
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy example/build/web --project-name=coflui --branch=main
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ app.*.map.json

# Cora code-review local cache (review history, etc)
.cora/

# iOS generated (not part of web/package build)
example/ios/Podfile
example/ios/Pods/
89 changes: 89 additions & 0 deletions doc/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Deploying the Example App to Cloudflare Pages

The coflui example app is a static Flutter web build — a perfect fit for
Cloudflare Pages (free, global CDN, HTTPS automatically).

This repo ships **two GitHub Actions workflows** for a 3-tier deploy strategy:

| Trigger | Target | Purpose |
|---------|--------|---------|
| `git push tag vX.Y.Z` | `coflui.pages.dev` | **Production** — stable URL for sharing |
| `git push origin develop` | `coflui-preview.pages.dev` | **Preview** — always latest |
| Open a PR → develop | `<branch>.coflui-preview.pages.dev` | **PR preview** — per-branch |

---

## One-time setup

### 1. Create the API token

1. Go to **dash.cloudflare.com → My Profile → API Tokens → Create Token**
2. Use the **"Edit Cloudflare Workers"** template, or a custom token with:
- Account → Cloudflare Pages → **Edit**
3. Copy the token value.

### 2. Add repository secrets

In **GitHub → repo → Settings → Secrets and variables → Actions → New secret**:

| Secret name | Value |
|-------------|-------|
| `CLOUDFLARE_API_TOKEN` | (token from step 1) |
| `CLOUDFLARE_ACCOUNT_ID` | Your CF account ID (find it in the CF dashboard URL or Pages project) |

> ℹ️ The Pages project (`coflui`) is **created automatically** on the first
> workflow run — no need to run `wrangler pages project create` manually.

---

## Usage

### Production release (stable URL)

```bash
# After merging develop → main (via PR), tag a release:
git checkout develop
git tag v0.3.0
git push origin v0.3.0
# → workflow deploys to https://coflui.pages.dev
```

### Preview (auto, every push to develop)

Just push to develop — the preview workflow runs automatically:

```bash
git push origin develop
# → workflow deploys to https://coflui-preview.pages.dev
```

### PR previews

Open any PR targeting develop — the bot comments the preview URL on the PR.

---

## ⚠️ The `--no-tree-shake-icons` requirement

Flutter web's icon tree-shaker needs every `IconData` to be a compile-time
constant. Coflui's `IconResolver` resolves icon names to `IconData` at **runtime**
(from JSON), which breaks the shaker. Every web build MUST use:

```bash
flutter build web --release --no-tree-shake-icons
```

The `tool/build_web.sh` helper and both workflows already include this flag.
If you see `This application cannot tree shake icons fonts…`, you forgot it.

---

## Local build + serve

```bash
# Build only
./tool/build_web.sh

# Build + serve on LAN (http://<your-ip>:5678)
./tool/build_web.sh --serve
```
34 changes: 34 additions & 0 deletions tool/build_web.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
#
# Build the coflui example app for web (release mode).
#
# Why --no-tree-shake-icons?
# IconResolver (lib/src/dynamic/resolvers/icon_resolver.dart) creates IconData
# instances at RUNTIME from JSON strings. Flutter's web tree-shaker requires
# all IconData references to be compile-time constants — so it fails without
# this flag. This is an inherent trade-off of dynamic icon resolution.
#
# Usage:
# ./tool/build_web.sh # build only → example/build/web
# ./tool/build_web.sh --serve # build + serve locally on :5678 (LAN-accessible)
set -euo pipefail

cd "$(dirname "$0")/.."

EXAMPLE_DIR="example"
BUILD_DIR="$EXAMPLE_DIR/build/web"

echo "🔨 Building coflui example (web release)…"
cd "$EXAMPLE_DIR"
flutter build web --release --no-tree-shake-icons
cd - >/dev/null

echo "✅ Build complete → $BUILD_DIR"

if [[ "${1:-}" == "--serve" ]]; then
PORT="${2:-5678}"
echo "🌐 Serving on http://0.0.0.0:$PORT (LAN-accessible)"
echo " Local: http://localhost:$PORT"
cd "$BUILD_DIR"
python3 -m http.server "$PORT" --bind 0.0.0.0
fi
Loading