Skip to content

Commit b0dcc16

Browse files
committed
feat(project): add automatic project detection and gh sync bugfix, aswell as bugfix of .env selection in project creaiton
1 parent 8bb5589 commit b0dcc16

File tree

6 files changed

+991
-377
lines changed

6 files changed

+991
-377
lines changed

app/routers/project.py

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,73 @@
1-
from fastapi import APIRouter, Depends, Request, Query
1+
import logging
2+
import os
3+
from datetime import datetime, timedelta, timezone
4+
from typing import Any
5+
from urllib.parse import parse_qs, urlparse
6+
27
import httpx
3-
from fastapi.responses import Response, RedirectResponse
8+
from arq.connections import ArqRedis
9+
from fastapi import APIRouter, Depends, Query, Request
10+
from fastapi.responses import RedirectResponse, Response
11+
from redis.asyncio import Redis
412
from sqlalchemy import select, update
513
from sqlalchemy.ext.asyncio import AsyncSession
614
from sqlalchemy.orm import selectinload
7-
from datetime import datetime, timedelta, timezone
8-
from redis.asyncio import Redis
9-
from arq.connections import ArqRedis
10-
from urllib.parse import urlparse, parse_qs
11-
import logging
12-
import os
13-
from typing import Any
1415

16+
from config import Settings, get_settings
17+
from db import get_db
1518
from dependencies import (
19+
RedirectResponseX,
20+
TemplateResponse,
21+
flash,
22+
get_access,
1623
get_current_user,
17-
get_project_by_name,
1824
get_deployment_by_id,
19-
get_team_by_slug,
25+
get_github_installation_service,
2026
get_github_service,
21-
get_redis_client,
2227
get_job_queue,
23-
flash,
24-
get_translation as _,
25-
TemplateResponse,
26-
RedirectResponseX,
28+
get_project_by_name,
29+
get_redis_client,
2730
get_role,
28-
get_access,
29-
get_github_installation_service,
31+
get_team_by_slug,
3032
)
31-
from models import (
32-
Project,
33-
Deployment,
34-
Domain,
35-
User,
36-
Team,
37-
TeamMember,
38-
utc_now,
33+
from dependencies import (
34+
get_translation as _,
3935
)
4036
from forms.project import (
4137
NewProjectForm,
42-
ProjectDeployForm,
43-
ProjectDeleteForm,
44-
ProjectGeneralForm,
45-
ProjectEnvVarsForm,
46-
ProjectEnvironmentForm,
47-
ProjectDeleteEnvironmentForm,
4838
ProjectBuildAndProjectDeployForm,
4939
ProjectCancelDeploymentForm,
50-
ProjectRollbackDeploymentForm,
40+
ProjectDeleteEnvironmentForm,
41+
ProjectDeleteForm,
42+
ProjectDeployForm,
5143
ProjectDomainForm,
44+
ProjectEnvironmentForm,
45+
ProjectEnvVarsForm,
46+
ProjectGeneralForm,
5247
ProjectRemoveDomainForm,
53-
ProjectVerifyDomainForm,
5448
ProjectResourcesForm,
49+
ProjectRollbackDeploymentForm,
50+
ProjectVerifyDomainForm,
51+
)
52+
from models import (
53+
Deployment,
54+
Domain,
55+
Project,
56+
Team,
57+
TeamMember,
58+
User,
59+
utc_now,
5560
)
56-
from config import get_settings, Settings
57-
from db import get_db
58-
from services.github import GitHubService
59-
from services.github_installation import GitHubInstallationService
6061
from services.deployment import DeploymentService
6162
from services.domain import DomainService
62-
from utils.project import get_latest_projects, get_latest_deployments
63-
from utils.team import get_latest_teams
64-
from utils.pagination import paginate
65-
from utils.environment import group_branches_by_environment, get_environment_for_branch
63+
from services.github import GitHubService
64+
from services.github_installation import GitHubInstallationService
65+
from services.project_detection import detect_project_settings
6666
from utils.color import COLORS
67+
from utils.environment import get_environment_for_branch, group_branches_by_environment
68+
from utils.pagination import paginate
69+
from utils.project import get_latest_deployments, get_latest_projects
70+
from utils.team import get_latest_teams
6771
from utils.user import get_user_github_token
6872

6973
logger = logging.getLogger(__name__)
@@ -126,6 +130,28 @@ async def new_project_details(
126130
form.name.data = repo_name
127131
form.production_branch.data = repo_default_branch
128132

133+
try:
134+
github_oauth_token = await get_user_github_token(db, current_user)
135+
if github_oauth_token:
136+
detected = await detect_project_settings(
137+
github_service,
138+
github_oauth_token,
139+
int(repo_id),
140+
repo_default_branch,
141+
)
142+
if detected.preset:
143+
form.preset.data = detected.preset
144+
if detected.image:
145+
form.image.data = detected.image
146+
if detected.build_command:
147+
form.build_command.data = detected.build_command
148+
if detected.start_command:
149+
form.start_command.data = detected.start_command
150+
if detected.pre_deploy_command:
151+
form.pre_deploy_command.data = detected.pre_deploy_command
152+
except Exception:
153+
logger.warning("Failed to detect project settings", exc_info=True)
154+
129155
if request.method == "POST" and await form.validate_on_submit():
130156
try:
131157
github_oauth_token = await get_user_github_token(db, current_user)

app/services/github.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import httpx
2-
import jwt
31
import time
4-
from typing import Any
52
from email.utils import parsedate_to_datetime
3+
from typing import Any
4+
5+
import httpx
6+
import jwt
67

78

89
class GitHubService:
@@ -298,3 +299,38 @@ async def get_repository_installation(self, repo_full_name: str) -> dict:
298299
)
299300
response.raise_for_status()
300301
return response.json()
302+
303+
async def get_repository_file(
304+
self,
305+
user_access_token: str,
306+
repo_id: int,
307+
path: str,
308+
ref: str | None = None,
309+
) -> dict | None:
310+
"""Get file contents from a repository.
311+
312+
Args:
313+
user_access_token: GitHub access token
314+
repo_id: Repository ID
315+
path: Path to the file (e.g., "package.json")
316+
ref: Optional branch/tag/commit to fetch from
317+
318+
Returns:
319+
File content dict with 'content' (base64 encoded) and metadata,
320+
or None if file doesn't exist.
321+
"""
322+
params = {}
323+
if ref:
324+
params["ref"] = ref
325+
326+
response = httpx.get(
327+
f"https://api.github.com/repositories/{repo_id}/contents/{path}",
328+
headers={"Authorization": f"Bearer {user_access_token}"},
329+
params=params,
330+
)
331+
332+
if response.status_code == 404:
333+
return None
334+
335+
response.raise_for_status()
336+
return response.json()

0 commit comments

Comments
 (0)