mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
Started tasks to migrate runtime to React 19. Added phase 3 projects
This commit is contained in:
@@ -0,0 +1,579 @@
|
||||
# DEPLOY-001: One-Click Deploy Integrations
|
||||
|
||||
## Overview
|
||||
|
||||
Add one-click deployment to popular hosting platforms (Netlify, Vercel, GitHub Pages). Users can deploy their frontend directly from the editor without manual file handling or CLI tools.
|
||||
|
||||
## Context
|
||||
|
||||
Currently, deployment requires:
|
||||
1. Deploy to local folder
|
||||
2. Manually upload to hosting platform
|
||||
3. Configure hosting settings separately
|
||||
4. Repeat for every deployment
|
||||
|
||||
This friction discourages frequent deployments and makes it harder for non-technical users to share their work.
|
||||
|
||||
### Existing Infrastructure
|
||||
|
||||
From `deployer.ts`:
|
||||
```typescript
|
||||
export async function deployToFolder({
|
||||
project,
|
||||
direntry,
|
||||
environment,
|
||||
baseUrl,
|
||||
envVariables,
|
||||
runtimeType = 'deploy'
|
||||
}: DeployToFolderOptions)
|
||||
```
|
||||
|
||||
From `compilation.ts`:
|
||||
```typescript
|
||||
class Compilation {
|
||||
deployToFolder(direntry, options): Promise<void>;
|
||||
// Build scripts for pre/post deploy
|
||||
}
|
||||
```
|
||||
|
||||
From `DeployToFolderTab.tsx`:
|
||||
- Current UI for folder selection
|
||||
- Environment selection dropdown
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
1. **Platform Integrations**
|
||||
- Netlify (OAuth + API)
|
||||
- Vercel (OAuth + API)
|
||||
- GitHub Pages (via GitHub API)
|
||||
- Cloudflare Pages (OAuth + API)
|
||||
|
||||
2. **Deploy Flow**
|
||||
- One-click deploy from editor
|
||||
- Platform selection dropdown
|
||||
- Site/project selection or creation
|
||||
- Environment variables configuration
|
||||
- Deploy progress indication
|
||||
|
||||
3. **Site Management**
|
||||
- List user's sites on each platform
|
||||
- Create new site from editor
|
||||
- Link project to existing site
|
||||
- View deployment history
|
||||
|
||||
4. **Configuration**
|
||||
- Environment variables per platform
|
||||
- Custom domain display
|
||||
- Build settings (if needed)
|
||||
- Deploy hooks
|
||||
|
||||
5. **Status & History**
|
||||
- Deploy status in editor
|
||||
- Link to live site
|
||||
- Deployment history
|
||||
- Rollback option (if supported)
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
- Deploy completes in < 2 minutes
|
||||
- Works with existing deploy-to-folder logic
|
||||
- Secure token storage
|
||||
- Clear error messages
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### 1. Deploy Service Architecture
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/DeployService.ts
|
||||
|
||||
interface DeployTarget {
|
||||
id: string;
|
||||
name: string;
|
||||
platform: DeployPlatform;
|
||||
siteId: string;
|
||||
siteName: string;
|
||||
url: string;
|
||||
customDomain?: string;
|
||||
lastDeployedAt?: string;
|
||||
envVariables?: Record<string, string>;
|
||||
}
|
||||
|
||||
enum DeployPlatform {
|
||||
NETLIFY = 'netlify',
|
||||
VERCEL = 'vercel',
|
||||
GITHUB_PAGES = 'github_pages',
|
||||
CLOUDFLARE = 'cloudflare',
|
||||
LOCAL_FOLDER = 'local_folder'
|
||||
}
|
||||
|
||||
interface DeployResult {
|
||||
success: boolean;
|
||||
deployId: string;
|
||||
url: string;
|
||||
buildTime: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
class DeployService {
|
||||
private static instance: DeployService;
|
||||
private providers: Map<DeployPlatform, DeployProvider> = new Map();
|
||||
|
||||
// Provider management
|
||||
registerProvider(provider: DeployProvider): void;
|
||||
getProvider(platform: DeployPlatform): DeployProvider;
|
||||
|
||||
// Authentication
|
||||
async authenticate(platform: DeployPlatform): Promise<void>;
|
||||
async disconnect(platform: DeployPlatform): Promise<void>;
|
||||
isAuthenticated(platform: DeployPlatform): boolean;
|
||||
|
||||
// Site management
|
||||
async listSites(platform: DeployPlatform): Promise<Site[]>;
|
||||
async createSite(platform: DeployPlatform, name: string): Promise<Site>;
|
||||
async linkSite(project: ProjectModel, target: DeployTarget): Promise<void>;
|
||||
|
||||
// Deployment
|
||||
async deploy(project: ProjectModel, target: DeployTarget): Promise<DeployResult>;
|
||||
async getDeployStatus(deployId: string): Promise<DeployStatus>;
|
||||
async getDeployHistory(target: DeployTarget): Promise<Deployment[]>;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Deploy Provider Interface
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/deploy/DeployProvider.ts
|
||||
|
||||
interface DeployProvider {
|
||||
readonly platform: DeployPlatform;
|
||||
readonly name: string;
|
||||
readonly icon: string;
|
||||
|
||||
// Authentication
|
||||
authenticate(): Promise<AuthResult>;
|
||||
disconnect(): Promise<void>;
|
||||
isAuthenticated(): boolean;
|
||||
getUser(): Promise<User | null>;
|
||||
|
||||
// Sites
|
||||
listSites(): Promise<Site[]>;
|
||||
createSite(name: string, options?: CreateSiteOptions): Promise<Site>;
|
||||
deleteSite(siteId: string): Promise<void>;
|
||||
|
||||
// Deployment
|
||||
deploy(siteId: string, files: DeployFiles): Promise<DeployResult>;
|
||||
getDeployStatus(deployId: string): Promise<DeployStatus>;
|
||||
getDeployHistory(siteId: string): Promise<Deployment[]>;
|
||||
cancelDeploy(deployId: string): Promise<void>;
|
||||
|
||||
// Configuration
|
||||
getEnvVariables(siteId: string): Promise<Record<string, string>>;
|
||||
setEnvVariables(siteId: string, vars: Record<string, string>): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Netlify Provider
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/deploy/providers/NetlifyProvider.ts
|
||||
|
||||
class NetlifyProvider implements DeployProvider {
|
||||
platform = DeployPlatform.NETLIFY;
|
||||
name = 'Netlify';
|
||||
icon = 'netlify-icon.svg';
|
||||
|
||||
private clientId = 'YOUR_NETLIFY_CLIENT_ID';
|
||||
private redirectUri = 'noodl://netlify-callback';
|
||||
private token: string | null = null;
|
||||
|
||||
async authenticate(): Promise<AuthResult> {
|
||||
// OAuth flow
|
||||
const authUrl = `https://app.netlify.com/authorize?` +
|
||||
`client_id=${this.clientId}&` +
|
||||
`response_type=token&` +
|
||||
`redirect_uri=${encodeURIComponent(this.redirectUri)}`;
|
||||
|
||||
// Open in browser, handle callback via deep link
|
||||
const token = await this.handleOAuthCallback(authUrl);
|
||||
this.token = token;
|
||||
|
||||
await this.storeToken(token);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async listSites(): Promise<Site[]> {
|
||||
const response = await fetch('https://api.netlify.com/api/v1/sites', {
|
||||
headers: { Authorization: `Bearer ${this.token}` }
|
||||
});
|
||||
|
||||
const sites = await response.json();
|
||||
return sites.map(s => ({
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
url: s.ssl_url || s.url,
|
||||
customDomain: s.custom_domain,
|
||||
updatedAt: s.updated_at
|
||||
}));
|
||||
}
|
||||
|
||||
async createSite(name: string): Promise<Site> {
|
||||
const response = await fetch('https://api.netlify.com/api/v1/sites', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ name })
|
||||
});
|
||||
|
||||
const site = await response.json();
|
||||
return {
|
||||
id: site.id,
|
||||
name: site.name,
|
||||
url: site.ssl_url || site.url
|
||||
};
|
||||
}
|
||||
|
||||
async deploy(siteId: string, files: DeployFiles): Promise<DeployResult> {
|
||||
// Create deploy
|
||||
const deploy = await this.createDeploy(siteId);
|
||||
|
||||
// Upload files using Netlify's digest-based upload
|
||||
const fileHashes = await this.calculateHashes(files);
|
||||
const required = await this.getRequiredFiles(deploy.id, fileHashes);
|
||||
|
||||
for (const file of required) {
|
||||
await this.uploadFile(deploy.id, file);
|
||||
}
|
||||
|
||||
// Finalize deploy
|
||||
return await this.finalizeDeploy(deploy.id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Vercel Provider
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/deploy/providers/VercelProvider.ts
|
||||
|
||||
class VercelProvider implements DeployProvider {
|
||||
platform = DeployPlatform.VERCEL;
|
||||
name = 'Vercel';
|
||||
icon = 'vercel-icon.svg';
|
||||
|
||||
private clientId = 'YOUR_VERCEL_CLIENT_ID';
|
||||
private token: string | null = null;
|
||||
|
||||
async authenticate(): Promise<AuthResult> {
|
||||
// Vercel uses OAuth 2.0
|
||||
const state = this.generateState();
|
||||
const authUrl = `https://vercel.com/integrations/noodl/new?` +
|
||||
`state=${state}`;
|
||||
|
||||
const token = await this.handleOAuthCallback(authUrl);
|
||||
this.token = token;
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async deploy(projectId: string, files: DeployFiles): Promise<DeployResult> {
|
||||
// Vercel deployment API
|
||||
const deployment = await fetch('https://api.vercel.com/v13/deployments', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: projectId,
|
||||
files: await this.prepareFiles(files),
|
||||
projectSettings: {
|
||||
framework: null // Static site
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const result = await deployment.json();
|
||||
return {
|
||||
success: true,
|
||||
deployId: result.id,
|
||||
url: `https://${result.url}`,
|
||||
buildTime: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. GitHub Pages Provider
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/deploy/providers/GitHubPagesProvider.ts
|
||||
|
||||
class GitHubPagesProvider implements DeployProvider {
|
||||
platform = DeployPlatform.GITHUB_PAGES;
|
||||
name = 'GitHub Pages';
|
||||
icon = 'github-icon.svg';
|
||||
|
||||
async authenticate(): Promise<AuthResult> {
|
||||
// Reuse GitHub OAuth from GIT-001
|
||||
const githubService = GitHubOAuthService.instance;
|
||||
if (!githubService.isAuthenticated()) {
|
||||
await githubService.authenticate();
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async listSites(): Promise<Site[]> {
|
||||
// List repos with GitHub Pages enabled
|
||||
const repos = await this.githubApi.listRepos();
|
||||
const pagesRepos = repos.filter(r => r.has_pages);
|
||||
|
||||
return pagesRepos.map(r => ({
|
||||
id: r.full_name,
|
||||
name: r.name,
|
||||
url: `https://${r.owner.login}.github.io/${r.name}`,
|
||||
repo: r.full_name
|
||||
}));
|
||||
}
|
||||
|
||||
async deploy(repoFullName: string, files: DeployFiles): Promise<DeployResult> {
|
||||
const [owner, repo] = repoFullName.split('/');
|
||||
|
||||
// Create/update gh-pages branch
|
||||
const branch = 'gh-pages';
|
||||
|
||||
// Get current tree (if exists)
|
||||
let baseTree: string | null = null;
|
||||
try {
|
||||
const ref = await this.githubApi.getRef(owner, repo, `heads/${branch}`);
|
||||
const commit = await this.githubApi.getCommit(owner, repo, ref.object.sha);
|
||||
baseTree = commit.tree.sha;
|
||||
} catch {
|
||||
// Branch doesn't exist yet
|
||||
}
|
||||
|
||||
// Create blobs for all files
|
||||
const tree = await this.createTree(owner, repo, files, baseTree);
|
||||
|
||||
// Create commit
|
||||
const commit = await this.githubApi.createCommit(owner, repo, {
|
||||
message: 'Deploy from Noodl',
|
||||
tree: tree.sha,
|
||||
parents: baseTree ? [baseTree] : []
|
||||
});
|
||||
|
||||
// Update branch reference
|
||||
await this.githubApi.updateRef(owner, repo, `heads/${branch}`, commit.sha);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
deployId: commit.sha,
|
||||
url: `https://${owner}.github.io/${repo}`,
|
||||
buildTime: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. UI Components
|
||||
|
||||
#### Deploy Panel
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Deploy [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ DEPLOY TARGET │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 🌐 Netlify [▾] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ SITE │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ my-noodl-app [▾] │ │
|
||||
│ │ https://my-noodl-app.netlify.app │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ [+ Create New Site] │
|
||||
│ │
|
||||
│ ENVIRONMENT │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Production [▾] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Last deployed: 2 hours ago │ │
|
||||
│ │ Deploy time: 45 seconds │ │
|
||||
│ │ [View Site ↗] [View Deploy History] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [🚀 Deploy Now] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Deploy Progress
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Deploying to Netlify... │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 35% │
|
||||
│ │
|
||||
│ ✓ Building project │
|
||||
│ ✓ Exporting files (127 files) │
|
||||
│ ◐ Uploading to Netlify... │
|
||||
│ ○ Finalizing deploy │
|
||||
│ │
|
||||
│ [Cancel] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Files to Create
|
||||
|
||||
1. `packages/noodl-editor/src/editor/src/services/DeployService.ts`
|
||||
2. `packages/noodl-editor/src/editor/src/services/deploy/DeployProvider.ts`
|
||||
3. `packages/noodl-editor/src/editor/src/services/deploy/providers/NetlifyProvider.ts`
|
||||
4. `packages/noodl-editor/src/editor/src/services/deploy/providers/VercelProvider.ts`
|
||||
5. `packages/noodl-editor/src/editor/src/services/deploy/providers/GitHubPagesProvider.ts`
|
||||
6. `packages/noodl-editor/src/editor/src/services/deploy/providers/CloudflareProvider.ts`
|
||||
7. `packages/noodl-core-ui/src/components/deploy/DeployPanel/DeployPanel.tsx`
|
||||
8. `packages/noodl-core-ui/src/components/deploy/DeployProgress/DeployProgress.tsx`
|
||||
9. `packages/noodl-core-ui/src/components/deploy/SiteSelector/SiteSelector.tsx`
|
||||
10. `packages/noodl-core-ui/src/components/deploy/PlatformSelector/PlatformSelector.tsx`
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. `packages/noodl-editor/src/editor/src/views/DeployPopup/DeployPopup.tsx`
|
||||
- Add platform tabs
|
||||
- Integrate new deploy flow
|
||||
|
||||
2. `packages/noodl-editor/src/editor/src/utils/compilation/compilation.ts`
|
||||
- Add deploy to platform method
|
||||
- Hook into build scripts
|
||||
|
||||
3. `packages/noodl-editor/src/main/src/main.js`
|
||||
- Add deep link handlers for OAuth callbacks
|
||||
|
||||
4. `packages/noodl-editor/src/editor/src/models/projectmodel.ts`
|
||||
- Store deploy target configuration
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Service Architecture
|
||||
1. Create DeployService
|
||||
2. Define DeployProvider interface
|
||||
3. Implement provider registration
|
||||
4. Set up token storage
|
||||
|
||||
### Phase 2: Netlify Integration
|
||||
1. Implement NetlifyProvider
|
||||
2. Add OAuth flow
|
||||
3. Implement site listing
|
||||
4. Implement deployment
|
||||
|
||||
### Phase 3: Vercel Integration
|
||||
1. Implement VercelProvider
|
||||
2. Add OAuth flow
|
||||
3. Implement deployment
|
||||
|
||||
### Phase 4: GitHub Pages Integration
|
||||
1. Implement GitHubPagesProvider
|
||||
2. Reuse GitHub OAuth
|
||||
3. Implement gh-pages deployment
|
||||
|
||||
### Phase 5: UI Components
|
||||
1. Create DeployPanel
|
||||
2. Create platform/site selectors
|
||||
3. Create progress indicator
|
||||
4. Integrate with existing popup
|
||||
|
||||
### Phase 6: Testing & Polish
|
||||
1. Test each provider
|
||||
2. Error handling
|
||||
3. Progress accuracy
|
||||
4. Deploy history
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Netlify OAuth works
|
||||
- [ ] Netlify site listing works
|
||||
- [ ] Netlify deployment succeeds
|
||||
- [ ] Vercel OAuth works
|
||||
- [ ] Vercel deployment succeeds
|
||||
- [ ] GitHub Pages deployment works
|
||||
- [ ] Progress indicator accurate
|
||||
- [ ] Error messages helpful
|
||||
- [ ] Deploy history shows
|
||||
- [ ] Site links work
|
||||
- [ ] Token storage secure
|
||||
- [ ] Disconnect works
|
||||
|
||||
## Dependencies
|
||||
|
||||
- GIT-001 (GitHub OAuth) - for GitHub Pages
|
||||
|
||||
## Blocked By
|
||||
|
||||
- None (can start immediately)
|
||||
|
||||
## Blocks
|
||||
|
||||
- DEPLOY-002 (Preview Deployments)
|
||||
- DEPLOY-003 (Deploy Settings)
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
- Service architecture: 4-5 hours
|
||||
- Netlify provider: 5-6 hours
|
||||
- Vercel provider: 4-5 hours
|
||||
- GitHub Pages provider: 4-5 hours
|
||||
- Cloudflare provider: 4-5 hours
|
||||
- UI components: 5-6 hours
|
||||
- Testing & polish: 4-5 hours
|
||||
- **Total: 30-37 hours**
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. One-click deploy to Netlify works
|
||||
2. One-click deploy to Vercel works
|
||||
3. One-click deploy to GitHub Pages works
|
||||
4. Site creation from editor works
|
||||
5. Deploy progress visible
|
||||
6. Deploy history accessible
|
||||
|
||||
## Platform-Specific Notes
|
||||
|
||||
### Netlify
|
||||
- Uses digest-based uploads (efficient)
|
||||
- Supports deploy previews (branch deploys)
|
||||
- Has good API documentation
|
||||
- Free tier: 100GB bandwidth/month
|
||||
|
||||
### Vercel
|
||||
- File-based deployment API
|
||||
- Automatic HTTPS
|
||||
- Edge functions support
|
||||
- Free tier: 100GB bandwidth/month
|
||||
|
||||
### GitHub Pages
|
||||
- No OAuth app needed (reuse GitHub)
|
||||
- Limited to public repos on free tier
|
||||
- Jekyll processing (can disable with .nojekyll)
|
||||
- Free for public repos
|
||||
|
||||
### Cloudflare Pages
|
||||
- Similar to Netlify/Vercel
|
||||
- Global CDN
|
||||
- Free tier generous
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- AWS S3 + CloudFront
|
||||
- Firebase Hosting
|
||||
- Surge.sh
|
||||
- Custom server deployment (SFTP/SSH)
|
||||
- Docker container deployment
|
||||
@@ -0,0 +1,510 @@
|
||||
# DEPLOY-002: Preview Deployments
|
||||
|
||||
## Overview
|
||||
|
||||
Enable automatic preview deployments for each git branch or commit. When users push changes, a preview URL is automatically generated so stakeholders can review before merging to production.
|
||||
|
||||
## Context
|
||||
|
||||
Currently, sharing work-in-progress requires:
|
||||
1. Manual deploy to a staging site
|
||||
2. Share URL with stakeholders
|
||||
3. Remember which deploy corresponds to which version
|
||||
4. Manually clean up old deploys
|
||||
|
||||
Preview deployments provide:
|
||||
- Automatic URL per branch/PR
|
||||
- Easy sharing with stakeholders
|
||||
- Visual history of changes
|
||||
- Automatic cleanup
|
||||
|
||||
This is especially valuable for:
|
||||
- Design reviews
|
||||
- QA testing
|
||||
- Client approvals
|
||||
- Team collaboration
|
||||
|
||||
### Integration with GIT Series
|
||||
|
||||
From GIT-002:
|
||||
- Git status tracking per project
|
||||
- Branch awareness
|
||||
- Commit detection
|
||||
|
||||
This task leverages that infrastructure to trigger preview deploys.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
1. **Automatic Previews**
|
||||
- Deploy preview on branch push
|
||||
- Unique URL per branch
|
||||
- Update preview on new commits
|
||||
- Delete preview on branch delete
|
||||
|
||||
2. **Manual Previews**
|
||||
- "Deploy Preview" button in editor
|
||||
- Generate shareable URL
|
||||
- Named previews (optional)
|
||||
- Expiration settings
|
||||
|
||||
3. **Preview Management**
|
||||
- List all active previews
|
||||
- View preview URL
|
||||
- Delete individual previews
|
||||
- Set auto-cleanup rules
|
||||
|
||||
4. **Sharing**
|
||||
- Copy preview URL
|
||||
- QR code for mobile
|
||||
- Optional password protection
|
||||
- Expiration timer
|
||||
|
||||
5. **Integration with PRs**
|
||||
- Comment preview URL on PR
|
||||
- Update comment on new commits
|
||||
- Status check integration
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
- Preview available within 2 minutes
|
||||
- Support 10+ concurrent previews
|
||||
- Auto-cleanup after configurable period
|
||||
- Works with all deploy providers
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### 1. Preview Service
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts
|
||||
|
||||
interface PreviewDeployment {
|
||||
id: string;
|
||||
projectId: string;
|
||||
branch: string;
|
||||
commitSha: string;
|
||||
url: string;
|
||||
platform: DeployPlatform;
|
||||
siteId: string;
|
||||
status: PreviewStatus;
|
||||
createdAt: string;
|
||||
expiresAt?: string;
|
||||
password?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
enum PreviewStatus {
|
||||
PENDING = 'pending',
|
||||
BUILDING = 'building',
|
||||
READY = 'ready',
|
||||
FAILED = 'failed',
|
||||
EXPIRED = 'expired'
|
||||
}
|
||||
|
||||
interface PreviewConfig {
|
||||
enabled: boolean;
|
||||
autoDeployBranches: boolean;
|
||||
excludeBranches: string[]; // e.g., ['main', 'master']
|
||||
expirationDays: number;
|
||||
maxPreviews: number;
|
||||
passwordProtect: boolean;
|
||||
commentOnPR: boolean;
|
||||
}
|
||||
|
||||
class PreviewDeployService {
|
||||
private static instance: PreviewDeployService;
|
||||
|
||||
// Preview management
|
||||
async createPreview(options: CreatePreviewOptions): Promise<PreviewDeployment>;
|
||||
async updatePreview(previewId: string): Promise<PreviewDeployment>;
|
||||
async deletePreview(previewId: string): Promise<void>;
|
||||
async listPreviews(projectId: string): Promise<PreviewDeployment[]>;
|
||||
|
||||
// Auto-deployment
|
||||
async onBranchPush(projectId: string, branch: string, commitSha: string): Promise<void>;
|
||||
async onBranchDelete(projectId: string, branch: string): Promise<void>;
|
||||
|
||||
// PR integration
|
||||
async commentOnPR(preview: PreviewDeployment): Promise<void>;
|
||||
async updatePRComment(preview: PreviewDeployment): Promise<void>;
|
||||
|
||||
// Cleanup
|
||||
async cleanupExpiredPreviews(): Promise<void>;
|
||||
async enforceMaxPreviews(projectId: string): Promise<void>;
|
||||
|
||||
// Configuration
|
||||
getConfig(projectId: string): PreviewConfig;
|
||||
setConfig(projectId: string, config: Partial<PreviewConfig>): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Branch-Based Preview Naming
|
||||
|
||||
```typescript
|
||||
// Generate preview URLs based on branch
|
||||
function generatePreviewUrl(platform: DeployPlatform, branch: string, projectName: string): string {
|
||||
const sanitizedBranch = sanitizeBranchName(branch);
|
||||
|
||||
switch (platform) {
|
||||
case DeployPlatform.NETLIFY:
|
||||
// Netlify: branch--sitename.netlify.app
|
||||
return `https://${sanitizedBranch}--${projectName}.netlify.app`;
|
||||
|
||||
case DeployPlatform.VERCEL:
|
||||
// Vercel: project-branch-hash.vercel.app
|
||||
return `https://${projectName}-${sanitizedBranch}.vercel.app`;
|
||||
|
||||
case DeployPlatform.GITHUB_PAGES:
|
||||
// GitHub Pages: use subdirectory or separate branch
|
||||
return `https://${owner}.github.io/${repo}/preview/${sanitizedBranch}`;
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeBranchName(branch: string): string {
|
||||
return branch
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-]/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.substring(0, 50);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Git Integration Hook
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts
|
||||
|
||||
// Hook into git operations
|
||||
class PreviewDeployService {
|
||||
constructor() {
|
||||
// Listen for git events
|
||||
EventDispatcher.instance.on('git.push.success', this.handlePush.bind(this));
|
||||
EventDispatcher.instance.on('git.branch.delete', this.handleBranchDelete.bind(this));
|
||||
}
|
||||
|
||||
private async handlePush(event: GitPushEvent): Promise<void> {
|
||||
const config = this.getConfig(event.projectId);
|
||||
|
||||
if (!config.enabled || !config.autoDeployBranches) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if branch is excluded
|
||||
if (config.excludeBranches.includes(event.branch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we already have a preview for this branch
|
||||
const existing = await this.findPreviewByBranch(event.projectId, event.branch);
|
||||
|
||||
if (existing) {
|
||||
// Update existing preview
|
||||
await this.updatePreview(existing.id);
|
||||
} else {
|
||||
// Create new preview
|
||||
await this.createPreview({
|
||||
projectId: event.projectId,
|
||||
branch: event.branch,
|
||||
commitSha: event.commitSha
|
||||
});
|
||||
}
|
||||
|
||||
// Comment on PR if enabled
|
||||
if (config.commentOnPR) {
|
||||
const pr = await this.findPRForBranch(event.projectId, event.branch);
|
||||
if (pr) {
|
||||
await this.commentOnPR(existing || await this.getLatestPreview(event.projectId, event.branch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async handleBranchDelete(event: GitBranchDeleteEvent): Promise<void> {
|
||||
const preview = await this.findPreviewByBranch(event.projectId, event.branch);
|
||||
if (preview) {
|
||||
await this.deletePreview(preview.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. PR Comment Integration
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts
|
||||
|
||||
async commentOnPR(preview: PreviewDeployment): Promise<void> {
|
||||
const github = GitHubApiClient.instance;
|
||||
const project = ProjectModel.instance;
|
||||
const remote = project.getRemoteUrl();
|
||||
|
||||
if (!remote || !remote.includes('github.com')) {
|
||||
return; // Only GitHub PRs supported
|
||||
}
|
||||
|
||||
const { owner, repo } = parseGitHubUrl(remote);
|
||||
const pr = await github.findPRByBranch(owner, repo, preview.branch);
|
||||
|
||||
if (!pr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commentBody = this.generatePRComment(preview);
|
||||
|
||||
// Check for existing Noodl comment
|
||||
const existingComment = await github.findComment(owner, repo, pr.number, '<!-- noodl-preview -->');
|
||||
|
||||
if (existingComment) {
|
||||
await github.updateComment(owner, repo, existingComment.id, commentBody);
|
||||
} else {
|
||||
await github.createComment(owner, repo, pr.number, commentBody);
|
||||
}
|
||||
}
|
||||
|
||||
private generatePRComment(preview: PreviewDeployment): string {
|
||||
return `<!-- noodl-preview -->
|
||||
## 🚀 Noodl Preview Deployment
|
||||
|
||||
| Status | URL |
|
||||
|--------|-----|
|
||||
| ${this.getStatusEmoji(preview.status)} ${preview.status} | [${preview.url}](${preview.url}) |
|
||||
|
||||
**Branch:** \`${preview.branch}\`
|
||||
**Commit:** \`${preview.commitSha.substring(0, 7)}\`
|
||||
**Updated:** ${new Date(preview.createdAt).toLocaleString()}
|
||||
|
||||
---
|
||||
<sub>Deployed automatically by Noodl</sub>`;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. UI Components
|
||||
|
||||
#### Preview Manager Panel
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Preview Deployments [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ACTIVE PREVIEWS (3) │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 🌿 feature/new-dashboard [Copy URL] │ │
|
||||
│ │ https://feature-new-dashboard--myapp.netlify.app │ │
|
||||
│ │ Updated 10 minutes ago • Commit abc1234 │ │
|
||||
│ │ [Open ↗] [QR Code] [Delete] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ 🌿 feature/login-redesign [Copy URL] │ │
|
||||
│ │ https://feature-login-redesign--myapp.netlify.app │ │
|
||||
│ │ Updated 2 hours ago • Commit def5678 │ │
|
||||
│ │ [Open ↗] [QR Code] [Delete] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ 🌿 bugfix/form-validation [Copy URL] │ │
|
||||
│ │ https://bugfix-form-validation--myapp.netlify.app │ │
|
||||
│ │ Updated yesterday • Commit ghi9012 │ │
|
||||
│ │ [Open ↗] [QR Code] [Delete] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ SETTINGS │
|
||||
│ ☑ Auto-deploy branches │
|
||||
│ ☑ Comment preview URL on PRs │
|
||||
│ Exclude branches: main, master │
|
||||
│ Auto-delete after: [7 days ▾] │
|
||||
│ │
|
||||
│ [+ Create Manual Preview] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### QR Code Modal
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Share Preview [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄│ │
|
||||
│ │ █ █ █ █│ │
|
||||
│ │ █ ███ █ █ ███ █│ Scan to open │
|
||||
│ │ █ █ █ █│ on mobile │
|
||||
│ │ ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀│ │
|
||||
│ └─────────────────┘ │
|
||||
│ │
|
||||
│ feature/new-dashboard │
|
||||
│ https://feature-new-dashboard--myapp.netlify.app │
|
||||
│ │
|
||||
│ [Copy URL] [Download QR] [Close] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Create Manual Preview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Create Preview [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Preview Name (optional): │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ client-review-dec-15 │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Expires in: │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 7 days [▾] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ☐ Password protect │
|
||||
│ Password: [•••••••• ] │
|
||||
│ │
|
||||
│ Deploy from: │
|
||||
│ ○ Current state (uncommitted changes included) │
|
||||
│ ● Current branch (feature/new-dashboard) │
|
||||
│ ○ Specific commit: [abc1234 ▾] │
|
||||
│ │
|
||||
│ [Cancel] [Create Preview] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Files to Create
|
||||
|
||||
1. `packages/noodl-editor/src/editor/src/services/PreviewDeployService.ts`
|
||||
2. `packages/noodl-core-ui/src/components/deploy/PreviewManager/PreviewManager.tsx`
|
||||
3. `packages/noodl-core-ui/src/components/deploy/PreviewCard/PreviewCard.tsx`
|
||||
4. `packages/noodl-core-ui/src/components/deploy/CreatePreviewModal/CreatePreviewModal.tsx`
|
||||
5. `packages/noodl-core-ui/src/components/deploy/QRCodeModal/QRCodeModal.tsx`
|
||||
6. `packages/noodl-core-ui/src/components/deploy/PreviewSettings/PreviewSettings.tsx`
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. `packages/noodl-editor/src/editor/src/services/DeployService.ts`
|
||||
- Add preview deployment methods
|
||||
- Integrate with deploy providers
|
||||
|
||||
2. `packages/noodl-editor/src/editor/src/views/DeployPopup/DeployPopup.tsx`
|
||||
- Add "Previews" tab
|
||||
- Integrate PreviewManager
|
||||
|
||||
3. `packages/noodl-editor/src/editor/src/models/projectmodel.ts`
|
||||
- Store preview configuration
|
||||
- Track active previews
|
||||
|
||||
4. `packages/noodl-editor/src/editor/src/services/GitHubApiClient.ts`
|
||||
- Add PR comment methods
|
||||
- Add PR lookup by branch
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Preview Service
|
||||
1. Create PreviewDeployService
|
||||
2. Implement preview creation
|
||||
3. Implement preview deletion
|
||||
4. Add configuration storage
|
||||
|
||||
### Phase 2: Git Integration
|
||||
1. Hook into push events
|
||||
2. Hook into branch delete events
|
||||
3. Implement auto-deployment
|
||||
4. Test with branches
|
||||
|
||||
### Phase 3: PR Integration
|
||||
1. Implement PR comment creation
|
||||
2. Implement comment updating
|
||||
3. Add status emoji handling
|
||||
4. Test with GitHub PRs
|
||||
|
||||
### Phase 4: UI - Preview Manager
|
||||
1. Create PreviewManager component
|
||||
2. Create PreviewCard component
|
||||
3. Add copy/share functionality
|
||||
4. Implement delete action
|
||||
|
||||
### Phase 5: UI - Create Preview
|
||||
1. Create CreatePreviewModal
|
||||
2. Add expiration options
|
||||
3. Add password protection
|
||||
4. Add source selection
|
||||
|
||||
### Phase 6: UI - QR & Sharing
|
||||
1. Create QRCodeModal
|
||||
2. Add QR code generation
|
||||
3. Add download option
|
||||
4. Polish sharing UX
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Auto-preview on push works
|
||||
- [ ] Preview URL is correct
|
||||
- [ ] PR comment created
|
||||
- [ ] PR comment updated on new commit
|
||||
- [ ] Manual preview creation works
|
||||
- [ ] Preview deletion works
|
||||
- [ ] Auto-cleanup works
|
||||
- [ ] QR code generates correctly
|
||||
- [ ] Password protection works
|
||||
- [ ] Expiration works
|
||||
- [ ] Multiple previews supported
|
||||
|
||||
## Dependencies
|
||||
|
||||
- DEPLOY-001 (One-Click Deploy) - for deploy providers
|
||||
- GIT-001 (GitHub OAuth) - for PR comments
|
||||
- GIT-002 (Git Status) - for branch awareness
|
||||
|
||||
## Blocked By
|
||||
|
||||
- DEPLOY-001
|
||||
|
||||
## Blocks
|
||||
|
||||
- None
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
- Preview service: 5-6 hours
|
||||
- Git integration: 4-5 hours
|
||||
- PR integration: 3-4 hours
|
||||
- UI preview manager: 4-5 hours
|
||||
- UI create preview: 3-4 hours
|
||||
- UI QR/sharing: 2-3 hours
|
||||
- Testing & polish: 3-4 hours
|
||||
- **Total: 24-31 hours**
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. Auto-preview deploys on branch push
|
||||
2. Preview URL unique per branch
|
||||
3. PR comments posted automatically
|
||||
4. Manual previews can be created
|
||||
5. QR codes work for mobile testing
|
||||
6. Expired previews auto-cleaned
|
||||
|
||||
## Platform-Specific Implementation
|
||||
|
||||
### Netlify
|
||||
- Branch deploys built-in
|
||||
- URL pattern: `branch--site.netlify.app`
|
||||
- Easy configuration
|
||||
|
||||
### Vercel
|
||||
- Preview deployments automatic
|
||||
- URL pattern: `project-branch-hash.vercel.app`
|
||||
- Good GitHub integration
|
||||
|
||||
### GitHub Pages
|
||||
- Need separate approach (subdirectory or deploy to different branch)
|
||||
- Less native support for branch previews
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Visual regression testing
|
||||
- Screenshot comparison
|
||||
- Performance metrics per preview
|
||||
- A/B testing setup
|
||||
- Preview environments (staging, QA)
|
||||
- Slack/Teams notifications
|
||||
@@ -0,0 +1,533 @@
|
||||
# DEPLOY-003: Deploy Settings & Environment Variables
|
||||
|
||||
## Overview
|
||||
|
||||
Provide comprehensive deployment configuration including environment variables, build settings, custom domains, and deployment rules. Users can manage different environments (development, staging, production) with different configurations.
|
||||
|
||||
## Context
|
||||
|
||||
Currently:
|
||||
- Environment variables set per deploy manually
|
||||
- No persistent environment configuration
|
||||
- No distinction between environments
|
||||
- Custom domain setup requires external configuration
|
||||
|
||||
This task adds:
|
||||
- Persistent environment variable management
|
||||
- Multiple environment profiles
|
||||
- Custom domain configuration
|
||||
- Build optimization settings
|
||||
- Deploy rules and triggers
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
1. **Environment Variables**
|
||||
- Add/edit/delete variables
|
||||
- Sensitive variable masking
|
||||
- Import from .env file
|
||||
- Export to .env file
|
||||
- Variable validation
|
||||
|
||||
2. **Environment Profiles**
|
||||
- Development, Staging, Production presets
|
||||
- Custom profiles
|
||||
- Variables per profile
|
||||
- Easy switching
|
||||
|
||||
3. **Custom Domains**
|
||||
- View current domains
|
||||
- Add custom domain
|
||||
- SSL certificate status
|
||||
- DNS configuration help
|
||||
|
||||
4. **Build Settings**
|
||||
- Output directory
|
||||
- Base URL configuration
|
||||
- Asset optimization
|
||||
- Source maps (dev only)
|
||||
|
||||
5. **Deploy Rules**
|
||||
- Auto-deploy on push
|
||||
- Branch-based rules
|
||||
- Deploy schedule
|
||||
- Deploy hooks/webhooks
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
- Variables encrypted at rest
|
||||
- Sensitive values never logged
|
||||
- Sync with platform settings
|
||||
- Works offline (cached)
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### 1. Environment Configuration Service
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/services/EnvironmentConfigService.ts
|
||||
|
||||
interface EnvironmentVariable {
|
||||
key: string;
|
||||
value: string;
|
||||
sensitive: boolean; // Masked in UI
|
||||
scope: VariableScope;
|
||||
}
|
||||
|
||||
enum VariableScope {
|
||||
BUILD = 'build', // Available during build
|
||||
RUNTIME = 'runtime', // Injected into app
|
||||
BOTH = 'both'
|
||||
}
|
||||
|
||||
interface EnvironmentProfile {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
variables: EnvironmentVariable[];
|
||||
isDefault: boolean;
|
||||
platform?: DeployPlatform; // If linked to a platform
|
||||
}
|
||||
|
||||
interface DeploySettings {
|
||||
outputDirectory: string;
|
||||
baseUrl: string;
|
||||
assetOptimization: boolean;
|
||||
sourceMaps: boolean;
|
||||
cleanUrls: boolean;
|
||||
trailingSlash: boolean;
|
||||
}
|
||||
|
||||
class EnvironmentConfigService {
|
||||
private static instance: EnvironmentConfigService;
|
||||
|
||||
// Profiles
|
||||
async getProfiles(projectId: string): Promise<EnvironmentProfile[]>;
|
||||
async createProfile(projectId: string, profile: Omit<EnvironmentProfile, 'id'>): Promise<EnvironmentProfile>;
|
||||
async updateProfile(projectId: string, profileId: string, updates: Partial<EnvironmentProfile>): Promise<void>;
|
||||
async deleteProfile(projectId: string, profileId: string): Promise<void>;
|
||||
|
||||
// Variables
|
||||
async getVariables(projectId: string, profileId: string): Promise<EnvironmentVariable[]>;
|
||||
async setVariable(projectId: string, profileId: string, variable: EnvironmentVariable): Promise<void>;
|
||||
async deleteVariable(projectId: string, profileId: string, key: string): Promise<void>;
|
||||
async importFromEnvFile(projectId: string, profileId: string, content: string): Promise<void>;
|
||||
async exportToEnvFile(projectId: string, profileId: string): Promise<string>;
|
||||
|
||||
// Build settings
|
||||
async getDeploySettings(projectId: string): Promise<DeploySettings>;
|
||||
async updateDeploySettings(projectId: string, settings: Partial<DeploySettings>): Promise<void>;
|
||||
|
||||
// Platform sync
|
||||
async syncWithPlatform(projectId: string, profileId: string, platform: DeployPlatform): Promise<void>;
|
||||
async pullFromPlatform(projectId: string, platform: DeployPlatform): Promise<EnvironmentVariable[]>;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Environment Storage
|
||||
|
||||
```typescript
|
||||
// Store in project metadata, encrypted
|
||||
interface ProjectDeployConfig {
|
||||
profiles: EnvironmentProfile[];
|
||||
activeProfileId: string;
|
||||
deploySettings: DeploySettings;
|
||||
domains: CustomDomain[];
|
||||
deployRules: DeployRule[];
|
||||
}
|
||||
|
||||
// Encryption for sensitive values
|
||||
class SecureStorage {
|
||||
async encrypt(value: string): Promise<string>;
|
||||
async decrypt(value: string): Promise<string>;
|
||||
}
|
||||
|
||||
// Store encrypted in project.json
|
||||
{
|
||||
"metadata": {
|
||||
"deployConfig": {
|
||||
"profiles": [
|
||||
{
|
||||
"id": "prod",
|
||||
"name": "Production",
|
||||
"variables": [
|
||||
{
|
||||
"key": "API_URL",
|
||||
"value": "https://api.example.com", // Plain text
|
||||
"sensitive": false
|
||||
},
|
||||
{
|
||||
"key": "API_KEY",
|
||||
"value": "encrypted:abc123...", // Encrypted
|
||||
"sensitive": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Domain Configuration
|
||||
|
||||
```typescript
|
||||
interface CustomDomain {
|
||||
domain: string;
|
||||
platform: DeployPlatform;
|
||||
siteId: string;
|
||||
status: DomainStatus;
|
||||
sslStatus: SSLStatus;
|
||||
dnsRecords?: DNSRecord[];
|
||||
}
|
||||
|
||||
enum DomainStatus {
|
||||
PENDING = 'pending',
|
||||
ACTIVE = 'active',
|
||||
FAILED = 'failed'
|
||||
}
|
||||
|
||||
enum SSLStatus {
|
||||
PENDING = 'pending',
|
||||
ACTIVE = 'active',
|
||||
EXPIRED = 'expired',
|
||||
FAILED = 'failed'
|
||||
}
|
||||
|
||||
interface DNSRecord {
|
||||
type: 'A' | 'CNAME' | 'TXT';
|
||||
name: string;
|
||||
value: string;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
class DomainService {
|
||||
async addDomain(siteId: string, domain: string): Promise<CustomDomain>;
|
||||
async verifyDomain(domainId: string): Promise<DomainStatus>;
|
||||
async getDNSInstructions(domain: string): Promise<DNSRecord[]>;
|
||||
async checkSSL(domainId: string): Promise<SSLStatus>;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Deploy Rules
|
||||
|
||||
```typescript
|
||||
interface DeployRule {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
trigger: DeployTrigger;
|
||||
conditions: DeployCondition[];
|
||||
actions: DeployAction[];
|
||||
}
|
||||
|
||||
interface DeployTrigger {
|
||||
type: 'push' | 'schedule' | 'manual' | 'webhook';
|
||||
config: PushConfig | ScheduleConfig | WebhookConfig;
|
||||
}
|
||||
|
||||
interface PushConfig {
|
||||
branches: string[]; // Glob patterns
|
||||
paths?: string[]; // Only deploy if these paths changed
|
||||
}
|
||||
|
||||
interface ScheduleConfig {
|
||||
cron: string; // Cron expression
|
||||
timezone: string;
|
||||
}
|
||||
|
||||
interface DeployCondition {
|
||||
type: 'branch' | 'tag' | 'path' | 'message';
|
||||
operator: 'equals' | 'contains' | 'matches';
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface DeployAction {
|
||||
type: 'deploy' | 'notify' | 'webhook';
|
||||
config: DeployActionConfig | NotifyConfig | WebhookConfig;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. UI Components
|
||||
|
||||
#### Environment Variables Panel
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Environment Variables [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Profile: [Production ▾] [+ New Profile] │
|
||||
│ │
|
||||
│ VARIABLES [Import .env] │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Key Value Scope [⋯] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ API_URL https://api.example.com Runtime [✎🗑] │ │
|
||||
│ │ API_KEY •••••••••••••••••••••• Runtime [✎🗑] │ │
|
||||
│ │ ANALYTICS_ID UA-12345678-1 Runtime [✎🗑] │ │
|
||||
│ │ DEBUG false Build [✎🗑] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [+ Add Variable] [Export .env] │
|
||||
│ │
|
||||
│ ☑ Sync with Netlify │
|
||||
│ Last synced: 5 minutes ago [Sync Now] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Build Settings Panel
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Build Settings [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ OUTPUT │
|
||||
│ Output Directory: [dist ] │
|
||||
│ Base URL: [/ ] │
|
||||
│ │
|
||||
│ OPTIMIZATION │
|
||||
│ ☑ Optimize assets (minify JS/CSS) │
|
||||
│ ☐ Generate source maps (increases build size) │
|
||||
│ ☑ Clean URLs (remove .html extension) │
|
||||
│ ☐ Trailing slash on URLs │
|
||||
│ │
|
||||
│ ADVANCED │
|
||||
│ Build Command: [npm run build ] │
|
||||
│ Publish Directory: [build ] │
|
||||
│ │
|
||||
│ NODE VERSION │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 18 (LTS) [▾] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Save Settings] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Custom Domains Panel
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Custom Domains [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ CONNECTED DOMAINS │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 🌐 myapp.com │ │
|
||||
│ │ ✓ DNS Configured ✓ SSL Active │ │
|
||||
│ │ Primary domain [Remove] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ 🌐 www.myapp.com │ │
|
||||
│ │ ✓ DNS Configured ✓ SSL Active │ │
|
||||
│ │ Redirects to myapp.com [Remove] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [+ Add Custom Domain] │
|
||||
│ │
|
||||
│ DEFAULT DOMAIN │
|
||||
│ https://myapp.netlify.app [Copy] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Add Domain Modal
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Add Custom Domain [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Domain: │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ app.example.com │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ DNS CONFIGURATION REQUIRED │
|
||||
│ │
|
||||
│ Add these records to your DNS provider: │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Type Name Value [Copy] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ CNAME app myapp.netlify.app [Copy] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ⚠️ DNS changes can take up to 48 hours to propagate │
|
||||
│ │
|
||||
│ Status: ⏳ Waiting for DNS verification... │
|
||||
│ │
|
||||
│ [Cancel] [Verify Domain] [Done] │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Deploy Rules Panel
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Deploy Rules [×] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ RULES [+ Add Rule] │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ ☑ Auto-deploy production │ │
|
||||
│ │ When: Push to main │ │
|
||||
│ │ Deploy to: Production [Edit 🗑] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ ☑ Preview branches │ │
|
||||
│ │ When: Push to feature/* │ │
|
||||
│ │ Deploy to: Preview [Edit 🗑] │ │
|
||||
│ ├─────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ ☐ Scheduled deploy │ │
|
||||
│ │ When: Daily at 2:00 AM UTC │ │
|
||||
│ │ Deploy to: Production [Edit 🗑] │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ WEBHOOKS [+ Add Webhook] │
|
||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Build hook URL: │ │
|
||||
│ │ https://api.netlify.com/build_hooks/abc123 [Copy] [🔄] │ │
|
||||
│ │ Trigger: POST request to this URL │ │
|
||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Files to Create
|
||||
|
||||
1. `packages/noodl-editor/src/editor/src/services/EnvironmentConfigService.ts`
|
||||
2. `packages/noodl-editor/src/editor/src/services/DomainService.ts`
|
||||
3. `packages/noodl-editor/src/editor/src/services/DeployRulesService.ts`
|
||||
4. `packages/noodl-core-ui/src/components/deploy/EnvironmentVariables/EnvironmentVariables.tsx`
|
||||
5. `packages/noodl-core-ui/src/components/deploy/EnvironmentVariables/VariableRow.tsx`
|
||||
6. `packages/noodl-core-ui/src/components/deploy/EnvironmentVariables/ProfileSelector.tsx`
|
||||
7. `packages/noodl-core-ui/src/components/deploy/BuildSettings/BuildSettings.tsx`
|
||||
8. `packages/noodl-core-ui/src/components/deploy/CustomDomains/CustomDomains.tsx`
|
||||
9. `packages/noodl-core-ui/src/components/deploy/CustomDomains/AddDomainModal.tsx`
|
||||
10. `packages/noodl-core-ui/src/components/deploy/DeployRules/DeployRules.tsx`
|
||||
11. `packages/noodl-core-ui/src/components/deploy/DeployRules/RuleEditor.tsx`
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. `packages/noodl-editor/src/editor/src/views/DeployPopup/DeployPopup.tsx`
|
||||
- Add settings tabs
|
||||
- Integrate new panels
|
||||
|
||||
2. `packages/noodl-editor/src/editor/src/utils/compilation/compilation.ts`
|
||||
- Use environment variables from config
|
||||
- Apply build settings
|
||||
|
||||
3. `packages/noodl-editor/src/editor/src/models/projectmodel.ts`
|
||||
- Store deploy configuration
|
||||
- Load/save config
|
||||
|
||||
4. `packages/noodl-editor/src/editor/src/services/DeployService.ts`
|
||||
- Apply environment variables to deploy
|
||||
- Handle domain configuration
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Environment Variables
|
||||
1. Create EnvironmentConfigService
|
||||
2. Implement variable storage
|
||||
3. Implement encryption for sensitive values
|
||||
4. Add import/export .env
|
||||
|
||||
### Phase 2: Environment Profiles
|
||||
1. Add profile management
|
||||
2. Implement profile switching
|
||||
3. Add default profiles (dev/staging/prod)
|
||||
4. UI for profile management
|
||||
|
||||
### Phase 3: UI - Variables Panel
|
||||
1. Create EnvironmentVariables component
|
||||
2. Create VariableRow component
|
||||
3. Add add/edit/delete functionality
|
||||
4. Add import/export buttons
|
||||
|
||||
### Phase 4: Build Settings
|
||||
1. Create build settings storage
|
||||
2. Create BuildSettings component
|
||||
3. Integrate with compilation
|
||||
4. Test with deployments
|
||||
|
||||
### Phase 5: Custom Domains
|
||||
1. Create DomainService
|
||||
2. Implement platform-specific domain APIs
|
||||
3. Create CustomDomains component
|
||||
4. Create AddDomainModal
|
||||
|
||||
### Phase 6: Deploy Rules
|
||||
1. Create DeployRulesService
|
||||
2. Implement rule evaluation
|
||||
3. Create DeployRules component
|
||||
4. Create RuleEditor
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Variables saved correctly
|
||||
- [ ] Sensitive values encrypted
|
||||
- [ ] Variables applied to deploy
|
||||
- [ ] Import .env works
|
||||
- [ ] Export .env works
|
||||
- [ ] Profile switching works
|
||||
- [ ] Build settings applied
|
||||
- [ ] Custom domain setup works
|
||||
- [ ] DNS verification works
|
||||
- [ ] Deploy rules trigger correctly
|
||||
- [ ] Webhooks work
|
||||
- [ ] Platform sync works
|
||||
|
||||
## Dependencies
|
||||
|
||||
- DEPLOY-001 (One-Click Deploy) - for platform integration
|
||||
|
||||
## Blocked By
|
||||
|
||||
- DEPLOY-001
|
||||
|
||||
## Blocks
|
||||
|
||||
- None (final DEPLOY task)
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
- Environment config service: 4-5 hours
|
||||
- Variable storage/encryption: 3-4 hours
|
||||
- Environment profiles: 3-4 hours
|
||||
- UI variables panel: 4-5 hours
|
||||
- Build settings: 3-4 hours
|
||||
- Custom domains: 4-5 hours
|
||||
- Deploy rules: 4-5 hours
|
||||
- Testing & polish: 3-4 hours
|
||||
- **Total: 28-36 hours**
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. Environment variables persist across deploys
|
||||
2. Sensitive values properly secured
|
||||
3. Multiple profiles supported
|
||||
4. Import/export .env works
|
||||
5. Custom domains configurable
|
||||
6. Deploy rules automate deployments
|
||||
7. Settings sync with platforms
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Sensitive values encrypted at rest
|
||||
- Never log sensitive values
|
||||
- Use platform-native secret storage where available
|
||||
- Clear memory after use
|
||||
- Validate input to prevent injection
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Environment variable inheritance
|
||||
- Secret rotation reminders
|
||||
- Integration with secret managers (Vault, AWS Secrets)
|
||||
- A/B testing configuration
|
||||
- Feature flags integration
|
||||
- Monitoring/alerting integration
|
||||
@@ -0,0 +1,385 @@
|
||||
# DEPLOY Series: Deployment Automation
|
||||
|
||||
## Overview
|
||||
|
||||
The DEPLOY series transforms Noodl's deployment from manual folder exports into a modern, automated deployment pipeline. Users can deploy to popular hosting platforms with one click, get automatic preview URLs for each branch, and manage environment variables and domains directly from the editor.
|
||||
|
||||
## Target Environment
|
||||
|
||||
- **Editor**: React 19 version only
|
||||
- **Platforms**: Netlify, Vercel, GitHub Pages, Cloudflare Pages
|
||||
- **Backwards Compatibility**: Existing deploy-to-folder continues to work
|
||||
|
||||
## Task Dependency Graph
|
||||
|
||||
```
|
||||
DEPLOY-001 (One-Click Deploy)
|
||||
│
|
||||
├─────────────────────┐
|
||||
↓ ↓
|
||||
DEPLOY-002 (Previews) DEPLOY-003 (Settings)
|
||||
```
|
||||
|
||||
## Task Summary
|
||||
|
||||
| Task ID | Name | Est. Hours | Priority |
|
||||
|---------|------|------------|----------|
|
||||
| DEPLOY-001 | One-Click Deploy Integrations | 30-37 | Critical |
|
||||
| DEPLOY-002 | Preview Deployments | 24-31 | High |
|
||||
| DEPLOY-003 | Deploy Settings & Environment Variables | 28-36 | High |
|
||||
|
||||
**Total Estimated: 82-104 hours**
|
||||
|
||||
## Implementation Order
|
||||
|
||||
### Phase 1: Core Deployment (Weeks 1-2)
|
||||
1. **DEPLOY-001** - One-click deploy to platforms
|
||||
- Establishes provider architecture
|
||||
- OAuth flows for each platform
|
||||
- Core deployment functionality
|
||||
|
||||
### Phase 2: Preview & Settings (Weeks 3-4)
|
||||
2. **DEPLOY-002** - Preview deployments
|
||||
- Branch-based previews
|
||||
- PR integration
|
||||
- Sharing features
|
||||
|
||||
3. **DEPLOY-003** - Deploy settings
|
||||
- Environment variables
|
||||
- Custom domains
|
||||
- Deploy rules
|
||||
|
||||
## Existing Infrastructure
|
||||
|
||||
### Deployer
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/utils/compilation/build/deployer.ts
|
||||
export async function deployToFolder({
|
||||
project,
|
||||
direntry,
|
||||
environment,
|
||||
baseUrl,
|
||||
envVariables,
|
||||
runtimeType = 'deploy'
|
||||
}: DeployToFolderOptions)
|
||||
```
|
||||
|
||||
### Compilation
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/utils/compilation/compilation.ts
|
||||
class Compilation {
|
||||
deployToFolder(direntry, options): Promise<void>;
|
||||
// Build scripts system for pre/post deploy
|
||||
}
|
||||
```
|
||||
|
||||
### Deploy UI
|
||||
|
||||
```typescript
|
||||
// Current deploy popup with folder selection
|
||||
DeployToFolderTab.tsx
|
||||
DeployPopup.tsx
|
||||
```
|
||||
|
||||
### Cloud Services
|
||||
|
||||
```typescript
|
||||
// packages/noodl-editor/src/editor/src/models/CloudServices.ts
|
||||
interface Environment {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
appId: string;
|
||||
masterKey?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## New Architecture
|
||||
|
||||
### Service Layer
|
||||
|
||||
```
|
||||
packages/noodl-editor/src/editor/src/services/
|
||||
├── DeployService.ts # Central deployment service
|
||||
├── PreviewDeployService.ts # Preview management
|
||||
├── EnvironmentConfigService.ts # Env vars & profiles
|
||||
├── DomainService.ts # Custom domain management
|
||||
├── DeployRulesService.ts # Automation rules
|
||||
└── deploy/
|
||||
├── DeployProvider.ts # Provider interface
|
||||
└── providers/
|
||||
├── NetlifyProvider.ts
|
||||
├── VercelProvider.ts
|
||||
├── GitHubPagesProvider.ts
|
||||
└── CloudflareProvider.ts
|
||||
```
|
||||
|
||||
### Provider Interface
|
||||
|
||||
```typescript
|
||||
interface DeployProvider {
|
||||
readonly platform: DeployPlatform;
|
||||
readonly name: string;
|
||||
|
||||
// Authentication
|
||||
authenticate(): Promise<AuthResult>;
|
||||
isAuthenticated(): boolean;
|
||||
|
||||
// Sites
|
||||
listSites(): Promise<Site[]>;
|
||||
createSite(name: string): Promise<Site>;
|
||||
|
||||
// Deployment
|
||||
deploy(siteId: string, files: DeployFiles): Promise<DeployResult>;
|
||||
getDeployStatus(deployId: string): Promise<DeployStatus>;
|
||||
|
||||
// Configuration
|
||||
getEnvVariables(siteId: string): Promise<Record<string, string>>;
|
||||
setEnvVariables(siteId: string, vars: Record<string, string>): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Comparison
|
||||
|
||||
| Feature | Netlify | Vercel | GitHub Pages | Cloudflare |
|
||||
|---------|---------|--------|--------------|------------|
|
||||
| OAuth | ✓ | ✓ | Via GitHub | ✓ |
|
||||
| Preview Deploys | ✓ | ✓ | Manual | ✓ |
|
||||
| Custom Domains | ✓ | ✓ | ✓ | ✓ |
|
||||
| Env Variables | ✓ | ✓ | Secrets only | ✓ |
|
||||
| Deploy Hooks | ✓ | ✓ | Actions | ✓ |
|
||||
| Free Tier | 100GB/mo | 100GB/mo | Unlimited* | 100K/day |
|
||||
|
||||
*GitHub Pages: Free for public repos, requires Pro for private
|
||||
|
||||
## Key User Flows
|
||||
|
||||
### 1. First-Time Deploy
|
||||
|
||||
```
|
||||
User clicks "Deploy"
|
||||
↓
|
||||
Select platform (Netlify, Vercel, etc.)
|
||||
↓
|
||||
Authenticate with platform (OAuth)
|
||||
↓
|
||||
Create new site or select existing
|
||||
↓
|
||||
Configure environment variables
|
||||
↓
|
||||
Deploy → Get live URL
|
||||
```
|
||||
|
||||
### 2. Subsequent Deploys
|
||||
|
||||
```
|
||||
User clicks "Deploy"
|
||||
↓
|
||||
Site already linked
|
||||
↓
|
||||
One-click deploy
|
||||
↓
|
||||
Progress indicator
|
||||
↓
|
||||
Success → Link to site
|
||||
```
|
||||
|
||||
### 3. Preview Workflow
|
||||
|
||||
```
|
||||
User pushes feature branch
|
||||
↓
|
||||
Auto-deploy preview
|
||||
↓
|
||||
Comment on PR with preview URL
|
||||
↓
|
||||
Stakeholder reviews
|
||||
↓
|
||||
Merge → Production deploy
|
||||
↓
|
||||
Preview auto-cleaned
|
||||
```
|
||||
|
||||
## UI Components to Create
|
||||
|
||||
| Component | Package | Purpose |
|
||||
|-----------|---------|---------|
|
||||
| DeployPanel | noodl-core-ui | Main deploy interface |
|
||||
| PlatformSelector | noodl-core-ui | Platform choice |
|
||||
| SiteSelector | noodl-core-ui | Site choice |
|
||||
| DeployProgress | noodl-core-ui | Progress indicator |
|
||||
| PreviewManager | noodl-core-ui | Preview list |
|
||||
| EnvironmentVariables | noodl-core-ui | Var management |
|
||||
| CustomDomains | noodl-core-ui | Domain setup |
|
||||
| DeployRules | noodl-core-ui | Automation rules |
|
||||
|
||||
## Data Models
|
||||
|
||||
### Deploy Target
|
||||
|
||||
```typescript
|
||||
interface DeployTarget {
|
||||
id: string;
|
||||
name: string;
|
||||
platform: DeployPlatform;
|
||||
siteId: string;
|
||||
siteName: string;
|
||||
url: string;
|
||||
customDomain?: string;
|
||||
lastDeployedAt?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Preview Deployment
|
||||
|
||||
```typescript
|
||||
interface PreviewDeployment {
|
||||
id: string;
|
||||
projectId: string;
|
||||
branch: string;
|
||||
commitSha: string;
|
||||
url: string;
|
||||
status: PreviewStatus;
|
||||
createdAt: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Profile
|
||||
|
||||
```typescript
|
||||
interface EnvironmentProfile {
|
||||
id: string;
|
||||
name: string;
|
||||
variables: EnvironmentVariable[];
|
||||
isDefault: boolean;
|
||||
}
|
||||
|
||||
interface EnvironmentVariable {
|
||||
key: string;
|
||||
value: string;
|
||||
sensitive: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Token Storage
|
||||
- OAuth tokens stored in electron safeStorage
|
||||
- Never logged or displayed
|
||||
- Cleared on disconnect
|
||||
|
||||
### Sensitive Variables
|
||||
- Encrypted at rest
|
||||
- Masked in UI (•••••)
|
||||
- Never exported to .env without warning
|
||||
|
||||
### Platform Security
|
||||
- Minimum OAuth scopes
|
||||
- Token refresh handling
|
||||
- Secure redirect handling
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Provider method isolation
|
||||
- Token handling
|
||||
- File preparation
|
||||
|
||||
### Integration Tests
|
||||
- OAuth flow mocking
|
||||
- Deploy API mocking
|
||||
- Full deploy cycle
|
||||
|
||||
### Manual Testing
|
||||
- Real deployments to each platform
|
||||
- Custom domain setup
|
||||
- Preview workflow
|
||||
|
||||
## Cline Usage Notes
|
||||
|
||||
### Before Starting Each Task
|
||||
|
||||
1. Review existing deployment infrastructure:
|
||||
- `deployer.ts`
|
||||
- `compilation.ts`
|
||||
- `DeployPopup/`
|
||||
|
||||
2. Understand build output:
|
||||
- How project.json is exported
|
||||
- How bundles are created
|
||||
- How index.html is generated
|
||||
|
||||
### Key Integration Points
|
||||
|
||||
1. **Compilation**: Use existing `deployToFolder` for file preparation
|
||||
2. **Cloud Services**: Existing environment model can inform design
|
||||
3. **Git Integration**: Leverage GIT series for branch awareness
|
||||
|
||||
### Platform API Notes
|
||||
|
||||
- **Netlify**: Uses digest-based upload (SHA1 hashes)
|
||||
- **Vercel**: File-based deployment API
|
||||
- **GitHub Pages**: Git-based via GitHub API
|
||||
- **Cloudflare**: Similar to Netlify/Vercel
|
||||
|
||||
## Success Criteria (Series Complete)
|
||||
|
||||
1. ✅ One-click deploy to 4 platforms
|
||||
2. ✅ OAuth authentication flow works
|
||||
3. ✅ Site creation from editor works
|
||||
4. ✅ Preview deploys auto-generated
|
||||
5. ✅ PR comments posted automatically
|
||||
6. ✅ Environment variables managed
|
||||
7. ✅ Custom domains configurable
|
||||
8. ✅ Deploy rules automate workflow
|
||||
|
||||
## Future Work (Post-DEPLOY)
|
||||
|
||||
The DEPLOY series enables:
|
||||
- **CI/CD Integration**: Connect to GitHub Actions, GitLab CI
|
||||
- **Performance Monitoring**: Lighthouse scores per deploy
|
||||
- **A/B Testing**: Deploy variants to subsets
|
||||
- **Rollback**: One-click rollback to previous deploy
|
||||
- **Multi-Region**: Deploy to multiple regions
|
||||
|
||||
## Files in This Series
|
||||
|
||||
- `DEPLOY-001-one-click-deploy.md`
|
||||
- `DEPLOY-002-preview-deployments.md`
|
||||
- `DEPLOY-003-deploy-settings.md`
|
||||
- `DEPLOY-OVERVIEW.md` (this file)
|
||||
|
||||
## External Dependencies
|
||||
|
||||
### Platform OAuth
|
||||
|
||||
| Platform | OAuth Type | Client ID Required |
|
||||
|----------|------------|-------------------|
|
||||
| Netlify | OAuth 2.0 | Yes |
|
||||
| Vercel | OAuth 2.0 | Yes |
|
||||
| GitHub | OAuth 2.0 | From GIT-001 |
|
||||
| Cloudflare | OAuth 2.0 | Yes |
|
||||
|
||||
### API Endpoints
|
||||
|
||||
- Netlify: `api.netlify.com/api/v1`
|
||||
- Vercel: `api.vercel.com/v13`
|
||||
- GitHub: `api.github.com`
|
||||
- Cloudflare: `api.cloudflare.com/client/v4`
|
||||
|
||||
## Complete Roadmap Summary
|
||||
|
||||
With the DEPLOY series complete, the full OpenNoodl modernization roadmap includes:
|
||||
|
||||
| Series | Tasks | Hours | Focus |
|
||||
|--------|-------|-------|-------|
|
||||
| DASH | 4 tasks | 43-63 | Dashboard UX |
|
||||
| GIT | 5 tasks | 68-96 | Git integration |
|
||||
| COMP | 6 tasks | 117-155 | Shared components |
|
||||
| AI | 4 tasks | 121-154 | AI assistance |
|
||||
| DEPLOY | 3 tasks | 82-104 | Deployment |
|
||||
|
||||
**Grand Total: 22 tasks, 431-572 hours**
|
||||
Reference in New Issue
Block a user