Skip to content

Commit

Permalink
Merge pull request #351 from calkit/envs
Browse files Browse the repository at this point in the history
Add environments page to projects
  • Loading branch information
petebachant authored Feb 14, 2025
2 parents 5c2ba73 + aff1c3c commit cb35354
Show file tree
Hide file tree
Showing 18 changed files with 524 additions and 28 deletions.
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,16 @@ local-api: ## Run the FastAPI backend directly.
.PHONY: build-dev
build-dev: ## Build containers for development.
${DOCKER_COMPOSE_DEV} build

.PHONY: format
format: ## Format all code.
@cd frontend && make format
@cd backend && make format

.PHONY: frontend-client
frontend-client: ## Regenerate the OpenAPI client for the frontend.
@cd frontend && make client

.PHONY: frontend
frontend: ## Build the frontend.
@cd frontend && npm run build
5 changes: 5 additions & 0 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ local-api:
.PHONY: test
test:
docker compose exec backend bash ./scripts/test.sh

.PHONY: format
format:
@echo "🚀 Formatting backend"
@uv run ruff format
83 changes: 81 additions & 2 deletions backend/app/api/routes/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2158,11 +2158,90 @@ def get_project_references(


class Environment(BaseModel):
kind: Literal["docker", "conda"]
path: str
name: str
kind: str
path: str | None = None
description: str | None = None
imported_from: str | None = None
all_attrs: dict
file_content: str | None = None


@router.get("/projects/{owner_name}/{project_name}/environments")
def get_project_environments(
owner_name: str,
project_name: str,
current_user: CurrentUserOptional,
session: SessionDep,
) -> list[Environment]:
project = app.projects.get_project(
owner_name=owner_name,
project_name=project_name,
session=session,
current_user=current_user,
min_access_level="read",
)
repo = get_repo(
project=project, user=current_user, session=session, ttl=120
)
ck_info = get_ck_info_from_repo(repo)
envs = ck_info.get("environments", {})
resp = []
for env_name, env in envs.items():
env_resp = env | {"all_attrs": env}
env_resp["name"] = env_name
env_path = env.get("path")
if env_path:
fpath = os.path.join(repo.working_dir, env_path)
if os.path.isfile(fpath):
with open(fpath) as f:
env_resp["file_content"] = f.read()
resp.append(Environment.model_validate(env_resp))
return resp


@router.post("/projects/{owner_name}/{project_name}/environments")
def post_project_environment(
owner_name: str,
project_name: str,
current_user: CurrentUser,
session: SessionDep,
req: Environment,
) -> Environment:
project = app.projects.get_project(
owner_name=owner_name,
project_name=project_name,
session=session,
current_user=current_user,
min_access_level="write",
)
repo = get_repo(
project=project, user=current_user, session=session, ttl=None
)
ck_info = get_ck_info_from_repo(repo)
envs = ck_info.get("environments", {})
if req.name in envs:
raise HTTPException(400, "Environment with same name already exists")
new_env = req.all_attrs
if req.imported_from and "imported_from" not in new_env:
new_env["imported_from"] = req.imported_from
envs[req.name] = new_env
ck_info["environments"] = envs
fpath = os.path.join(repo.working_dir, "calkit.yaml")
with open(fpath, "w") as f:
ryaml.dump(ck_info, f)
repo.git.add("calkit.yaml")
if req.path and req.file_content:
fpath = os.path.join(repo.working_dir, req.path)
os.makedirs(os.path.dirname(fpath), exist_ok=True)
with open(fpath, "w") as f:
f.write(req.file_content)
repo.git.add(fpath)
repo.git.commit(["-m", f"Add environment {req.name}"])
repo.git.push(["origin", repo.active_branch])
return Environment.model_validate(new_env | {"all_attrs": new_env})


class Software(BaseModel):
environments: list[Environment]
# TODO: Add scripts, packages, apps?
Expand Down
4 changes: 2 additions & 2 deletions backend/app/subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ def get_monthly_price(


def get_storage_limit(
plan_name: Literal["free", "standard", "professional"]
plan_name: Literal["free", "standard", "professional"],
) -> int:
"""Return the storage limit for a given plan in GB."""
return STORAGE_LIMITS_BY_PLAN_NAME[plan_name]


def get_private_projects_limit(
plan_name: Literal["free", "standard", "professional"]
plan_name: Literal["free", "standard", "professional"],
) -> int | None:
return PRIVATE_PROJECTS_LIMITS_BY_PLAN_NAME[plan_name]

Expand Down
7 changes: 4 additions & 3 deletions backend/app/tests/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ def test_init_successful_connection() -> None:
except Exception:
connection_successful = False

assert (
connection_successful
), "The database connection should be successful and not raise an exception."
assert connection_successful, (
"The database connection should be successful "
"and not raise an exception."
)

assert session_mock.exec.called_once_with(
select(1)
Expand Down
12 changes: 7 additions & 5 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ dependencies = [
"s3fs==2024.10.0",
"python-slugify==8.0.4",
"cryptography==43.0.0",
"dvc==3.54.1",
"gitpython==3.1.43",
"dvc==3.59.0",
"gitpython>=3.1.43",
"ruamel-yaml==0.18.6",
"bibtexparser==1.4.1",
"filelock==3.16.0",
"stripe==10.11.0",
"mixpanel==4.10.1",
"gcsfs==2024.10.0",
"calkit-python>=0.12.0",
"calkit-python>=0.20.0",
]

[tool.uv]
dev-dependencies = [
[dependency-groups]
dev = [
"pytest<8.0.0,>=7.4.3",
"mypy<2.0.0,>=1.8.0",
"ruff<1.0.0,>=0.2.2",
Expand All @@ -59,6 +59,8 @@ exclude = ["venv", ".venv", "alembic"]

[tool.ruff]
target-version = "py312"
line-length = 79
fix = true
exclude = ["alembic"]

[tool.ruff.lint.pyupgrade]
Expand Down
32 changes: 23 additions & 9 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ client:

.PHONY: format
format:
npx biome format --write ./src
@echo "🚀 Formatting frontend"
@npx biome format --write ./src
8 changes: 6 additions & 2 deletions frontend/src/client/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,12 @@ export type DiscountCodePublic = {
}

export type Environment = {
kind: "docker" | "conda"
path: string
name: string
kind: string
path?: string | null
description?: string | null
imported_from?: string | null
all_attrs: Record<string, unknown>
file_content?: string | null
}

Expand Down
45 changes: 42 additions & 3 deletions frontend/src/client/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -904,13 +904,52 @@ export const $DiscountCodePublic = {

export const $Environment = {
properties: {
name: {
type: "string",
isRequired: true,
},
kind: {
type: "Enum",
enum: ["docker", "conda"],
type: "string",
isRequired: true,
},
path: {
type: "string",
type: "any-of",
contains: [
{
type: "string",
},
{
type: "null",
},
],
},
description: {
type: "any-of",
contains: [
{
type: "string",
},
{
type: "null",
},
],
},
imported_from: {
type: "any-of",
contains: [
{
type: "string",
},
{
type: "null",
},
],
},
all_attrs: {
type: "dictionary",
contains: {
properties: {},
},
isRequired: true,
},
file_content: {
Expand Down
Loading

0 comments on commit cb35354

Please sign in to comment.