Skip to content

Commit dc36cbd

Browse files
WIP create command
1 parent b33178b commit dc36cbd

4 files changed

Lines changed: 82 additions & 10 deletions

File tree

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,69 @@
1-
from ..utils import with_auth
1+
import os
2+
import re
3+
import shutil
4+
import tempfile
5+
import urllib.request
6+
import zipfile
7+
28
import click
39

10+
from ..utils import with_auth
11+
412

513
@click.command()
614
@click.argument("app-name", type=str, required=True)
7-
@click.option("--crawl", type=str, required=True, help="A URL which should be crawled by AI to generate the app.")
8-
@click.option("--prompt", type=str, required=True, help="A prompt to guide the AI in generating the app.")
15+
@click.option("--crawl", type=str, required=False, help="A URL which should be crawled by AI to generate the app.")
16+
@click.option("--prompt", type=str, required=False, help="A prompt to guide the AI in generating the app.")
917
@with_auth
10-
def create(api_key: str, app_name: str, crawl: str, prompt: str) -> None:
18+
def create(app_name: str, crawl: str, prompt: str, api_key: str) -> None:
1119
"""
12-
The dev command create a temporary private app in a Stacksync workspace,
13-
runs a Stacksync application on localhost,
14-
and uses the local bridge to route traffic to the application.
20+
Create a new app folder in the current directory from the connector template.
1521
"""
16-
print("Oh boy", api_key)
22+
target_dir = os.path.join(os.getcwd(), _to_kebab_case(app_name))
23+
if os.path.exists(target_dir):
24+
raise click.ClickException(
25+
f"Destination already exists: {os.path.basename(target_dir)!r}"
26+
)
27+
28+
temp_folder = _get_templates_folder()
29+
template_dir = _get_connector_template_dir(temp_folder)
30+
31+
try:
32+
shutil.copytree(template_dir, target_dir)
33+
finally:
34+
shutil.rmtree(temp_folder, ignore_errors=True)
35+
36+
click.secho(f"Created app in {target_dir}", fg="green")
37+
38+
39+
def _to_kebab_case(value: str) -> str:
40+
normalized = re.sub(r"([a-z0-9])([A-Z])", r"\1-\2", value.strip())
41+
normalized = re.sub(r"[^A-Za-z0-9]+", "-", normalized)
42+
normalized = normalized.strip("-").lower()
43+
if not normalized:
44+
raise click.ClickException("App name must contain at least one letter or number.")
45+
return normalized
46+
47+
48+
def _get_templates_folder() -> str:
49+
"""
50+
This function downloads a zip from Github and extracts it to a temporary folder.
51+
"""
52+
temp_folder = tempfile.mkdtemp()
53+
zip_url = "https://github.com/stacksyncdata/cdk/archive/refs/heads/prod.zip"
54+
zip_path = os.path.join(temp_folder, "cdk.zip")
55+
with urllib.request.urlopen(zip_url) as response, open(zip_path, "wb") as out_file:
56+
shutil.copyfileobj(response, out_file)
57+
with zipfile.ZipFile(zip_path, "r") as zip_ref:
58+
zip_ref.extractall(temp_folder)
59+
return temp_folder
60+
61+
62+
def _get_connector_template_dir(temp_folder: str) -> str:
63+
extracted_root = os.path.join(temp_folder, "cdk-prod")
64+
template_dir = os.path.join(extracted_root, "templates", "connector")
65+
if not os.path.isdir(template_dir):
66+
raise click.ClickException(
67+
"Failed to locate the connector template in the downloaded archive."
68+
)
69+
return template_dir
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import click
2+
from ..utils import DEFAULTS, get_env_file, update_env_file
3+
4+
def complete_env_key(ctx: click.Context, param: click.Parameter, incomplete: str):
5+
return [key for key in DEFAULTS if key.startswith(incomplete)]
6+
7+
8+
@click.command(help="Get or set environment variables. Available keys: " + ", ".join(DEFAULTS.keys()))
9+
@click.argument("key", type=str, required=True, shell_complete=complete_env_key)
10+
@click.argument("value", type=str, required=False)
11+
def env(key: str, value: str) -> None:
12+
if (not value):
13+
click.echo(get_env_file()[key])
14+
else:
15+
update_env_file({key: value})

cli/src/stacksync_cli/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .commands.dev import dev as dev_cmd
44
from .commands.create import create as create_cmd
55
from .commands.hello import hello as hello_cmd
6+
from .commands.env import env as env_cmd
67

78

89
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
@@ -14,3 +15,4 @@ def cli() -> None:
1415
cli.add_command(dev_cmd)
1516
cli.add_command(create_cmd)
1617
cli.add_command(hello_cmd)
18+
cli.add_command(env_cmd)

cli/src/stacksync_cli/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ def print_logo() -> None:
2121
)
2222

2323

24+
# TODO: Move this somewhere nicer. They're not very sensitive so this will do the trick for now.
2425
DEFAULTS = {
25-
"local_bridge_url": "ws://localhost:8787",
26-
"login_url": "http://localhost:8080/cdk/login",
26+
"local_bridge_url": "https://local-bridge-prod-besg-1011549189584.europe-west1.run.app/",
27+
"login_url": "https://app.stacksync.com/cdk/login",
28+
"templates_source_branch": "prod",
2729
}
2830

2931

0 commit comments

Comments
 (0)