From 0e7c4d56f1c50f6640fa82c7ec37c9f255dcd1e9 Mon Sep 17 00:00:00 2001 From: Jiahao Zhang Date: Thu, 10 Jul 2025 22:47:39 -0700 Subject: [PATCH] Fix Railway deployment: Add nixpacks.toml and runtime.txt for Python app detection --- APP_STORE_CHECKLIST.md | 196 ++++++++++++++++++ .../UserInterfaceState.xcuserstate | Bin 66854 -> 66788 bytes .../Configuration/AppConfig.swift | 5 +- backend/DEPLOYMENT_GUIDE.md | 32 +++ backend/Procfile | 2 +- backend/railway.json | 7 +- nixpacks.toml | 24 +++ requirements.txt | 32 +++ runtime.txt | 1 + 9 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 APP_STORE_CHECKLIST.md create mode 100644 nixpacks.toml create mode 100644 requirements.txt create mode 100644 runtime.txt diff --git a/APP_STORE_CHECKLIST.md b/APP_STORE_CHECKLIST.md new file mode 100644 index 00000000..d2dae604 --- /dev/null +++ b/APP_STORE_CHECKLIST.md @@ -0,0 +1,196 @@ +# 🍎 TradingAgents App Store Submission Checklist + +## 📋 Pre-Submission Checklist + +### **Backend API Deployment** ✅ +- [ ] Railway account created and project deployed +- [ ] Environment variables configured in Railway dashboard +- [ ] API accessible via HTTPS (required by Apple) +- [ ] All endpoints tested and working +- [ ] Production URL documented + +### **iOS App Configuration** 📱 +- [ ] AppConfig.swift updated with production Railway URL +- [ ] App tested with production API (not localhost) +- [ ] Network calls working with HTTPS +- [ ] Error handling tested (network timeouts, API errors) +- [ ] App works without debugging console + +### **App Store Requirements** 🏪 +- [ ] App version and build number incremented +- [ ] App bundle identifier is unique +- [ ] App icons in all required sizes (1024x1024 for App Store) +- [ ] Launch screen configured +- [ ] App category selected (Finance) +- [ ] Age rating appropriate (17+ for financial content) + +### **Privacy and Legal** 🔒 +- [ ] Privacy policy created and accessible +- [ ] Terms of service created +- [ ] App privacy report filled out in App Store Connect +- [ ] Data collection practices documented +- [ ] Third-party API usage disclosed (OpenAI, Finnhub, etc.) + +### **Content and Marketing** 📝 +- [ ] App name decided and available +- [ ] App description written (under 4000 characters) +- [ ] Keywords selected (under 100 characters) +- [ ] Screenshots taken for all device sizes +- [ ] App preview video created (optional but recommended) + +## 🚀 Deployment Steps Summary + +### **Phase 1: Railway Deployment** +```bash +# In backend directory +./deploy-to-railway.sh +``` + +1. **Deploy to Railway:** + - Go to https://railway.app + - Connect GitHub repository + - Add environment variables + - Get production URL + +2. **Update iOS App:** + ```swift + // In AppConfig.swift, update production URL: + return "https://your-actual-railway-url.up.railway.app" + ``` + +3. **Test Everything:** + - API health check: `https://your-app.railway.app/health` + - iOS app with production API + - Full analysis flow end-to-end + +### **Phase 2: App Store Submission** + +1. **Prepare in Xcode:** + - Archive for App Store distribution + - Upload to App Store Connect + - Fill out app information + +2. **Submit for Review:** + - Complete metadata + - Upload screenshots + - Submit for review + +## 📊 Expected Timelines + +| Task | Duration | Notes | +|------|----------|-------| +| Railway Deployment | 5-10 minutes | Automatic build and deploy | +| iOS App Updates | 10-15 minutes | URL change and testing | +| App Store Metadata | 1-2 hours | Screenshots, descriptions, etc. | +| App Review Process | 1-7 days | Apple's review timeline | + +## 🔧 Technical Requirements + +### **API Requirements** +- ✅ HTTPS mandatory (Railway provides automatically) +- ✅ Stable uptime (Railway handles auto-restart) +- ✅ Response time < 30 seconds for health checks +- ✅ Proper error handling and status codes + +### **iOS App Requirements** +- ✅ iOS 15.0+ minimum deployment target +- ✅ Swift 5.5+ with SwiftUI +- ✅ Proper network security (HTTPS only) +- ✅ Privacy compliance (data handling disclosure) + +## 🚨 Common Rejection Reasons (and How to Avoid) + +### **Network/API Issues** +- ❌ **App doesn't work**: Ensure production API is stable +- ❌ **Network errors**: Test with poor network conditions +- ❌ **HTTPS required**: Use Railway (provides HTTPS automatically) + +### **Content Issues** +- ❌ **Financial advice disclaimer**: Add disclaimer about not being financial advice +- ❌ **Data accuracy**: Mention data is for informational purposes only +- ❌ **Real-time data**: Clarify data may be delayed + +### **Privacy Issues** +- ❌ **Missing privacy policy**: Create and link privacy policy +- ❌ **Data collection not disclosed**: Document all API data usage +- ❌ **Third-party services**: Disclose OpenAI, Finnhub usage + +## 📱 Testing Checklist + +### **Functional Testing** +- [ ] App launches successfully +- [ ] Can enter ticker symbols +- [ ] Analysis starts and completes +- [ ] Results display correctly +- [ ] History saves and loads +- [ ] Error states handled gracefully + +### **Network Testing** +- [ ] Works on WiFi +- [ ] Works on cellular data +- [ ] Handles network timeouts +- [ ] Handles server errors (500, 503, etc.) +- [ ] Handles invalid ticker symbols + +### **Device Testing** +- [ ] iPhone (various sizes) +- [ ] iPad (if universal app) +- [ ] Different iOS versions +- [ ] Light and dark mode +- [ ] Accessibility features + +## 📄 Required Legal Documents + +### **Privacy Policy Template** +``` +This app collects the following data: +- Stock ticker symbols you search +- Analysis results for your reference +- App usage analytics (if implemented) + +Data is processed by: +- OpenAI (for AI analysis) +- Finnhub (for market data) +- [Your Railway server] (for processing) + +Data is not sold or shared with third parties for marketing. +``` + +### **Terms of Service Key Points** +- App provides educational/informational content only +- Not financial advice +- User responsible for investment decisions +- Data accuracy not guaranteed +- Service availability not guaranteed + +## ✅ Final Pre-Submission Check + +- [ ] **Production API is live and stable** +- [ ] **iOS app works with production API** +- [ ] **All screenshots and metadata ready** +- [ ] **Privacy policy and terms accessible** +- [ ] **App tested on real device (not just simulator)** +- [ ] **No debugging code or console logs in release build** +- [ ] **App Store Connect information complete** + +## 🎉 Post-Approval Tasks + +### **After App Store Approval:** +- [ ] Monitor Railway usage and costs +- [ ] Set up monitoring/alerts for API downtime +- [ ] Plan for scaling if app becomes popular +- [ ] Consider migration to VPS for cost optimization + +### **Marketing and Growth:** +- [ ] Social media announcement +- [ ] Product Hunt launch (optional) +- [ ] User feedback collection +- [ ] App Store optimization (ASO) + +--- + +**🚀 Good luck with your App Store submission!** + +For technical support during deployment, see: +- `backend/DEPLOYMENT_GUIDE.md` - Detailed deployment instructions +- `backend/deploy-to-railway.sh` - Automated deployment script \ No newline at end of file diff --git a/TradingDummy/TradingDummy.xcodeproj/project.xcworkspace/xcuserdata/bytedance.xcuserdatad/UserInterfaceState.xcuserstate b/TradingDummy/TradingDummy.xcodeproj/project.xcworkspace/xcuserdata/bytedance.xcuserdatad/UserInterfaceState.xcuserstate index 20c991afb12676563cfadf24a173427fb68b5a7c..d4ff625d213ee32e571acacd03f5d70b1df15067 100644 GIT binary patch delta 16165 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 W*MAEkEnD{cO{@C-{_|XP`+oo?Te>d* delta 16275 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 NwCZ1 **Note**: The repository includes special configuration files (`nixpacks.toml`, `runtime.txt`) to help Railway detect the Python app in the `backend/` directory. + ### **Step 3: Configure Environment Variables** In the Railway dashboard: @@ -65,6 +67,35 @@ curl -X POST https://your-app.railway.app/analyze \ -d '{"ticker": "AAPL"}' ``` +## 🔧 **Troubleshooting Railway Deployment** + +### **Build Detection Issues** + +If Railway says "Nixpacks was unable to generate a build plan": + +1. **Check root directory files**: Ensure these files exist in your root directory: + - `nixpacks.toml` ✅ (helps Railway detect Python app) + - `runtime.txt` ✅ (specifies Python version) + - `requirements.txt` ✅ (copied from backend/) + +2. **Force redeploy**: In Railway dashboard, click "Redeploy" + +3. **Check logs**: Look at build logs for specific error messages + +### **Build Failures** + +If the build fails: +- Check that all dependencies in `requirements.txt` are valid +- Ensure Python version compatibility (we use Python 3.11) +- Check Railway build logs for specific error messages + +### **Runtime Errors** + +If the app builds but doesn't start: +- Verify environment variables are set correctly +- Check Railway deployment logs +- Ensure the start command is correct: `cd backend && uvicorn api:app --host 0.0.0.0 --port $PORT` + ## 📱 Update iOS App for Production ### **Step 1: Update API Configuration** @@ -123,6 +154,7 @@ Railway automatically handles: 1. **Build failures:** - Check requirements.txt is valid - Ensure Python version compatibility + - Look for missing root-level configuration files 2. **Runtime errors:** - Verify all environment variables are set diff --git a/backend/Procfile b/backend/Procfile index 75831aa1..f951acd0 100644 --- a/backend/Procfile +++ b/backend/Procfile @@ -1 +1 @@ -web: uvicorn api:app --host 0.0.0.0 --port $PORT --workers 1 \ No newline at end of file +web: cd backend && uvicorn api:app --host 0.0.0.0 --port $PORT --workers 1 \ No newline at end of file diff --git a/backend/railway.json b/backend/railway.json index f3bb01f5..46367229 100644 --- a/backend/railway.json +++ b/backend/railway.json @@ -2,17 +2,18 @@ "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "nixpacks", - "buildCommand": "pip install -r requirements.txt" + "buildCommand": "cd backend && pip install -r requirements.txt", + "watchPatterns": ["backend/**"] }, "deploy": { - "startCommand": "uvicorn api:app --host 0.0.0.0 --port $PORT", + "startCommand": "cd backend && uvicorn api:app --host 0.0.0.0 --port $PORT", "restartPolicyType": "on_failure", "restartPolicyMaxRetries": 3 }, "environments": { "production": { "variables": { - "PYTHONPATH": "/app", + "PYTHONPATH": "/app/backend", "TRADINGAGENTS_API_HOST": "0.0.0.0", "TRADINGAGENTS_API_PORT": "$PORT" } diff --git a/nixpacks.toml b/nixpacks.toml new file mode 100644 index 00000000..4f2688bf --- /dev/null +++ b/nixpacks.toml @@ -0,0 +1,24 @@ +[options] +# Tell Nixpacks this is a Python app +providers = ["python"] + +[variables] +# Set working directory to backend +NIXPACKS_BUILD_CMD = "cd backend && pip install -r requirements.txt" +NIXPACKS_START_CMD = "cd backend && uvicorn api:app --host 0.0.0.0 --port $PORT" + +[phases.setup] +dependsOn = [] + +[phases.install] +dependsOn = ["setup"] +cmds = [ + "cd backend", + "pip install -r requirements.txt" +] + +[phases.build] +dependsOn = ["install"] + +[phases.start] +cmd = "cd backend && uvicorn api:app --host 0.0.0.0 --port $PORT" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..885544d9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,32 @@ +typing-extensions +langchain-openai +langchain-experimental +pandas +yfinance +praw +feedparser +stockstats +eodhd +langgraph +chromadb +setuptools +backtrader +akshare +tushare +finnhub-python +parsel +requests +tqdm +pytz +redis +chainlit +rich +questionary +langchain_anthropic +langchain-google-genai +fastapi +pydantic +uvicorn[standard] +python-dotenv +google-search-results +serpapi diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 00000000..2000ca37 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.11 \ No newline at end of file