Add Deno workflow for linting and testing - #3
Draft
zyntromedia wants to merge 1 commit into
Draft
Conversation
This workflow installs Deno and runs linting and tests.
This comment was marked as abuse.
This comment was marked as abuse.
|
📊 PR Size: size/S
|
Member
Author
Mobile App Build Guide: Schema, Prototype & Automations
Table of Contents
1. Architecture Overview2. Database Schema2.1 Relational Schema (PostgreSQL)-- Users & Authentication
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
display_name VARCHAR(100),
avatar_url TEXT,
phone VARCHAR(20),
email_verified BOOLEAN DEFAULT FALSE,
status VARCHAR(20) DEFAULT 'active', -- active, suspended, deleted
role VARCHAR(20) DEFAULT 'user', -- user, admin, moderator
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
last_login_at TIMESTAMPTZ
);
CREATE TABLE user_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
refresh_token TEXT NOT NULL,
device_info JSONB,
ip_address INET,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Core App Data (Example: Content/Posts)
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
content TEXT,
status VARCHAR(20) DEFAULT 'draft', -- draft, published, archived
visibility VARCHAR(20) DEFAULT 'public', -- public, private, followers
metadata JSONB DEFAULT '{}',
view_count INTEGER DEFAULT 0,
like_count INTEGER DEFAULT 0,
comment_count INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
published_at TIMESTAMPTZ
);
CREATE TABLE comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
parent_id UUID REFERENCES comments(id) ON DELETE CASCADE, -- nested replies
content TEXT NOT NULL,
like_count INTEGER DEFAULT 0,
is_edited BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Social Features
CREATE TABLE follows (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
follower_id UUID REFERENCES users(id) ON DELETE CASCADE,
following_id UUID REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(follower_id, following_id)
);
CREATE TABLE likes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
target_type VARCHAR(20) NOT NULL, -- post, comment
target_id UUID NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, target_type, target_id)
);
-- Notifications
CREATE TABLE notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL, -- like, comment, follow, mention
title VARCHAR(255) NOT NULL,
body TEXT,
data JSONB DEFAULT '{}',
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Media/Assets
CREATE TABLE media (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
url TEXT NOT NULL,
thumbnail_url TEXT,
mime_type VARCHAR(100),
size_bytes BIGINT,
width INTEGER,
height INTEGER,
duration INTEGER, -- for video/audio in seconds
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_status_created ON posts(status, created_at DESC);
CREATE INDEX idx_comments_post_id ON comments(post_id);
CREATE INDEX idx_notifications_user_read ON notifications(user_id, is_read);
CREATE INDEX idx_follows_follower ON follows(follower_id);
CREATE INDEX idx_follows_following ON follows(following_id);2.2 NoSQL Schema (Firestore)// users/{userId}
{
"id": "uuid",
"email": "user@example.com",
"displayName": "John Doe",
"avatarUrl": "https://...",
"phone": "+1234567890",
"role": "user",
"status": "active",
"stats": {
"followersCount": 150,
"followingCount": 89,
"postsCount": 42
},
"settings": {
"notifications": true,
"theme": "system",
"language": "en"
},
"createdAt": "timestamp",
"updatedAt": "timestamp"
}
// posts/{postId}
{
"id": "uuid",
"userId": "ref:users/uuid",
"author": {
"displayName": "John Doe",
"avatarUrl": "https://..."
},
"title": "Post Title",
"content": "Post content...",
"status": "published",
"visibility": "public",
"media": [
{ "url": "https://...", "type": "image", "width": 1920, "height": 1080 }
],
"stats": {
"viewCount": 1250,
"likeCount": 89,
"commentCount": 12,
"shareCount": 5
},
"tags": ["tech", "mobile"],
"createdAt": "timestamp",
"updatedAt": "timestamp",
"publishedAt": "timestamp"
}
// notifications/{notificationId}
{
"userId": "ref:users/uuid",
"type": "like",
"title": "New Like",
"body": "John liked your post",
"actor": {
"id": "uuid",
"displayName": "John",
"avatarUrl": "https://..."
},
"target": {
"type": "post",
"id": "post-uuid"
},
"isRead": false,
"createdAt": "timestamp"
}3. API Schema3.1 REST API Endpoints# Authentication
POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/auth/refresh
POST /api/v1/auth/logout
POST /api/v1/auth/forgot-password
POST /api/v1/auth/reset-password
POST /api/v1/auth/verify-email
# User
GET /api/v1/users/me
PUT /api/v1/users/me
PUT /api/v1/users/me/avatar
DELETE /api/v1/users/me
GET /api/v1/users/{id}
GET /api/v1/users/{id}/followers
GET /api/v1/users/{id}/following
POST /api/v1/users/{id}/follow
DELETE /api/v1/users/{id}/follow
# Posts
GET /api/v1/posts # ?page=1&limit=20&sort=latest&filter=public
POST /api/v1/posts
GET /api/v1/posts/{id}
PUT /api/v1/posts/{id}
DELETE /api/v1/posts/{id}
POST /api/v1/posts/{id}/like
DELETE /api/v1/posts/{id}/like
POST /api/v1/posts/{id}/share
# Comments
GET /api/v1/posts/{postId}/comments # ?page=1&limit=50
POST /api/v1/posts/{postId}/comments
GET /api/v1/comments/{id}
PUT /api/v1/comments/{id}
DELETE /api/v1/comments/{id}
POST /api/v1/comments/{id}/like
# Notifications
GET /api/v1/notifications # ?unread_only=true&page=1
PUT /api/v1/notifications/{id}/read
PUT /api/v1/notifications/read-all
DELETE /api/v1/notifications/{id}
# Upload
POST /api/v1/upload/presigned # Get presigned URL for direct S3 upload
POST /api/v1/upload/complete # Confirm upload completion3.2 Request/Response Examples// POST /api/v1/auth/register
// Request
{
"email": "user@example.com",
"password": "SecurePass123!",
"displayName": "John Doe"
}
// Response 201
{
"success": true,
"data": {
"user": {
"id": "uuid",
"email": "user@example.com",
"displayName": "John Doe",
"avatarUrl": null,
"createdAt": "2026-08-08T01:25:00Z"
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2g...",
"expiresIn": 3600
}
}
}
// GET /api/v1/posts?page=1&limit=20
// Response 200
{
"success": true,
"data": {
"items": [
{
"id": "post-uuid",
"author": {
"id": "user-uuid",
"displayName": "Jane Smith",
"avatarUrl": "https://cdn.example.com/avatars/jane.jpg"
},
"title": "Building Scalable Mobile Apps",
"content": "In this post, we'll explore...",
"media": [],
"stats": { "viewCount": 1250, "likeCount": 89, "commentCount": 12 },
"createdAt": "2026-08-07T10:00:00Z",
"isLikedByMe": false
}
],
"pagination": {
"page": 1,
"limit": 20,
"totalItems": 1542,
"totalPages": 78,
"hasNextPage": true,
"hasPrevPage": false
}
}
}4. UI/UX Prototype Specs4.1 Screen Inventory
4.2 Design System Tokens/* Colors */
--color-primary: #6366F1; /* Indigo 500 */
--color-primary-dark: #4F46E5; /* Indigo 600 */
--color-primary-light: #818CF8; /* Indigo 400 */
--color-secondary: #EC4899; /* Pink 500 */
--color-success: #22C55E; /* Green 500 */
--color-warning: #F59E0B; /* Amber 500 */
--color-error: #EF4444; /* Red 500 */
--color-background: #FFFFFF;
--color-surface: #F8FAFC;
--color-text-primary: #0F172A;
--color-text-secondary: #64748B;
--color-text-muted: #94A3B8;
--color-border: #E2E8F0;
/* Typography */
--font-family: 'Inter', -apple-system, sans-serif;
--text-xs: 12px / 16px;
--text-sm: 14px / 20px;
--text-base: 16px / 24px;
--text-lg: 18px / 28px;
--text-xl: 20px / 28px;
--text-2xl: 24px / 32px;
--font-regular: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
/* Border Radius */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-xl: 24px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
/* Animation */
--duration-fast: 150ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
--ease-default: cubic-bezier(0.4, 0, 0.2, 1);
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);4.3 Navigation Structure5. Automation Pipelines5.1 CI/CD Pipeline (GitHub Actions)# .github/workflows/mobile-ci.yml
name: Mobile CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
NODE_VERSION: '20'
JAVA_VERSION: '17'
RUBY_VERSION: '3.2'
jobs:
# ─── Stage 1: Code Quality ─────────────────────────────
lint-and-test:
name: Lint & Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run TypeScript check
run: npm run typecheck
- name: Run unit tests
run: npm run test:unit -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
# ─── Stage 2: Build Verification ─────────────────────────
build-android:
name: Build Android
runs-on: ubuntu-latest
needs: lint-and-test
steps:
- uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build Android APK
run: cd android && ./gradlew assembleRelease
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: android-release-apk
path: android/app/build/outputs/apk/release/*.apk
build-ios:
name: Build iOS
runs-on: macos-latest
needs: lint-and-test
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.RUBY_VERSION }}
bundler-cache: true
- name: Install CocoaPods
run: cd ios && bundle exec pod install
- name: Build iOS
run: |
cd ios
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -destination 'platform=iOS Simulator,name=iPhone 15' clean build
# ─── Stage 3: E2E Tests ────────────────────────────────
e2e-tests:
name: E2E Tests (Maestro)
runs-on: macos-latest
needs: [build-android, build-ios]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Maestro
run: curl -Ls "https://get.maestro.mobile.dev" | bash
- name: Run E2E tests
run: maestro test e2e/
# ─── Stage 4: Deployment ───────────────────────────────
deploy-beta:
name: Deploy to Beta
runs-on: ubuntu-latest
needs: [build-android, build-ios, e2e-tests]
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: npm ci
- name: Deploy Android to Play Console (Internal Testing)
run: |
cd android
bundle exec fastlane deploy_internal
env:
PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_JSON_KEY }}
- name: Deploy iOS to TestFlight
run: |
cd ios
bundle exec fastlane beta
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [build-android, build-ios, e2e-tests]
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy Android to Play Store
run: cd android && bundle exec fastlane deploy_production
env:
PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_JSON_KEY }}
- name: Deploy iOS to App Store
run: cd ios && bundle exec fastlane release
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}5.2 Fastlane Configuration# fastlane/Fastfile
default_platform(:android)
platform :android do
desc "Run unit tests"
lane :test do
gradle(task: "test")
end
desc "Build release APK"
lane :build do
gradle(task: "assembleRelease")
end
desc "Deploy to Internal Testing"
lane :deploy_internal do
gradle(task: "bundleRelease")
upload_to_play_store(
track: 'internal',
release_status: 'draft',
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true
)
end
desc "Deploy to Production"
lane :deploy_production do
gradle(task: "bundleRelease")
upload_to_play_store(
track: 'production',
rollout: '0.1' # 10% staged rollout
)
end
end
# ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Run tests"
lane :test do
scan(scheme: "MyApp")
end
desc "Build app"
lane :build do
gym(scheme: "MyApp", export_method: "app-store")
end
desc "Deploy to TestFlight"
lane :beta do
increment_build_number(xcodeproj: "MyApp.xcodeproj")
match(type: "appstore")
gym(scheme: "MyApp")
upload_to_testflight(
skip_waiting_for_build_processing: true,
notify_external_testers: false
)
end
desc "Deploy to App Store"
lane :release do
increment_build_number(xcodeproj: "MyApp.xcodeproj")
match(type: "appstore")
gym(scheme: "MyApp")
upload_to_app_store(
force: true,
skip_metadata: false,
skip_screenshots: false,
submit_for_review: true,
automatic_release: false
)
end
end5.3 Automation Scripts#!/bin/bash
# scripts/setup.sh - One-command project setup
set -e
echo "🚀 Setting up mobile app development environment..."
# Check prerequisites
echo "📋 Checking prerequisites..."
command -v node >/dev/null 2>&1 || { echo "❌ Node.js is required"; exit 1; }
command -v java >/dev/null 2>&1 || { echo "❌ Java is required"; exit 1; }
# Install dependencies
echo "📦 Installing dependencies..."
npm ci
# Setup iOS
echo "🍎 Setting up iOS..."
cd ios
bundle install
bundle exec pod install
cd ..
# Setup Android
echo "🤖 Setting up Android..."
cd android
chmod +x gradlew
./gradlew clean
cd ..
# Setup git hooks
echo "🪝 Setting up git hooks..."
npx husky install
# Setup environment
echo "🔧 Setting up environment..."
if [ ! -f .env ]; then
cp .env.example .env
echo "⚠️ Please configure .env file"
fi
echo "✅ Setup complete! Run 'npm run ios' or 'npm run android' to start."// package.json - Scripts section
{
"scripts": {
"setup": "bash scripts/setup.sh",
"start": "react-native start",
"ios": "react-native run-ios",
"android": "react-native run-android",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"typecheck": "tsc --noEmit",
"test": "jest",
"test:unit": "jest --testPathPattern='__tests__/unit'",
"test:integration": "jest --testPathPattern='__tests__/integration'",
"test:e2e": "maestro test e2e/",
"test:coverage": "jest --coverage",
"format": "prettier --write "src/**/*.{ts,tsx,js,jsx,json}"",
"format:check": "prettier --check "src/**/*.{ts,tsx,js,jsx,json}"",
"build:android": "cd android && ./gradlew assembleRelease",
"build:ios": "cd ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release",
"release:android": "cd android && bundle exec fastlane deploy_production",
"release:ios": "cd ios && bundle exec fastlane release",
"bump:patch": "npm version patch && git push && git push --tags",
"bump:minor": "npm version minor && git push && git push --tags",
"bump:major": "npm version major && git push && git push --tags"
}
}5.4 Pre-commit Hooks (Husky)// .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
// .lintstagedrc
{
"*.{ts,tsx,js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
]
}6. Folder Structure7. Tech Stack RecommendationsCross-Platform (Recommended for speed)
Native (Recommended for performance)
Backend
Generated: August 2026 | For educational and development reference. |
zyntromedia
marked this pull request as draft
August 21, 2026 18:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This workflow installs Deno and runs linting and tests.
Summary
Details
Related Issues
How to Validate
Pre-Merge Checklist