Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

95 - merge CLI Main into main Main for release #97

Merged
merged 7 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
{
directory: "Minimal",
environment: "edge-minimal-example",
image: "quay.io/enthought/edge-example-minimal:1.2.0"
image: "quay.io/enthought/edge-minimal-example:1.2.0"
},
{
directory: "Panel",
Expand All @@ -35,7 +35,7 @@ jobs:
{
directory: "React",
environment: "edge-react-example",
image: "quay.io/enthought/edge-native-app-flask-demo:1.3.0"
image: "quay.io/enthought/edge-react-example:1.3.0"
},
{
directory: "Streamlit",
Expand Down
2 changes: 1 addition & 1 deletion Minimal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ version = AppVersion(
description="This Is An Example Edge Application",
icon=ICON,
kind=AppKindEnum.Native,
proxy_kind=AppProxyKindEnum.Generic,
framework=AppProxyKindEnum.Generic,
link="quay.io/<YOUR_ORGANIZATION>/YOUR_IMAGE_NAME_HERE:TAG",
recommended_profile="edge.medium"
)
Expand Down
28 changes: 28 additions & 0 deletions Minimal/app_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
app_id: edge-minimal-example
framework: generic
app_version: 1.2.0
python_version: 3.8
repository: quay.io
organisation: enthought
env_deps:
edm:
- click
- enthought_edge
- filetype
- oauthlib
- pip
- python_dateutil
- pyyaml
- requests
- setuptools
pip:
- questionary
app_deps:
- flask
- setuptools
- gunicorn
- enthought_edge
cmd_deps:
- docker
exclude:
- bootstrap.py
9 changes: 5 additions & 4 deletions Minimal/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
import argparse
import subprocess


ENV_NAME = "edge-minimal-example"
EDM_DEPS = ["click", "pip", "setuptools"]
EDM_DEPS = ["click", "pip", "pyyaml", "setuptools"]


def bootstrap(ci_mode):
Expand All @@ -25,7 +26,7 @@ def bootstrap(ci_mode):
"""

if ENV_NAME not in _list_edm_envs():
print(f"Creating development environment {ENV_NAME}...")
print(f"Creating development environment '{ENV_NAME}'...")
cmd = ["edm", "envs", "create", ENV_NAME, "--version", "3.8", "--force"]
subprocess.run(cmd, check=True)

Expand All @@ -35,10 +36,10 @@ def bootstrap(ci_mode):
print("Bootstrap complete.")

else:
print("Environment already exists; reusing.")
print(f"Environment '{ENV_NAME}' already exists; reusing.")

if not ci_mode:
print(f"Activating dev environment {ENV_NAME}")
print(f"Activating environment '{ENV_NAME}'")
subprocess.run(["edm", "shell", "-e", ENV_NAME])


Expand Down
91 changes: 70 additions & 21 deletions Minimal/ci/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,44 @@
"""
This is the "ci" module for the minimal example.
"""

import click
import os.path as op
import subprocess
import json
import yaml

SRC_ROOT = op.abspath(op.join(op.dirname(__file__), ".."))

# Docker image will be tagged "IMAGE:VERSION"
IMAGE = "quay.io/enthought/edge-example-minimal"
VERSION = "1.2.0"

# These will go into the built Docker image. You may wish to modify this
# minimal example to pin the dependencies, or use a bundle file to define them.
APP_DEPENDENCIES = ["flask", "setuptools", "gunicorn", "enthought_edge"]

# This will be used when running locally ("run" command).
# We just use the last component of the full image URL.
CONTAINER_NAME = IMAGE.split("/")[-1]
def _bundle_image(config: dict) -> str:
"""Assemble a default bundle image name from the configuration settings.
"""
return "/".join([config["repository"],
config["organisation"],
config["app_id"]])


@click.group()
def cli():
@click.pass_context
def cli(ctx):
"""Group for Click commands"""
pass
config = _load_config_settings()
ctx.obj = config


@cli.command()
@click.option("--bundle-image", default=None)
@click.option("--rebuild-zbundle", default=False, is_flag=True)
def build(rebuild_zbundle):
@click.option("--verbose", default=False, is_flag=True)
@click.pass_obj
def build(config, bundle_image, rebuild_zbundle, verbose):
"""Build the Docker image"""

# Configuration details
bundle_image = bundle_image or _bundle_image(config)
version = config["app_version"]
app_deps = config["app_deps"]

# First, we build a "zbundle" which contains all the eggs needed to
# build the environment within the Docker image.
fname = "app_environment.zbundle"
Expand All @@ -58,35 +64,59 @@ def build(rebuild_zbundle):
"2.0",
"-f",
fname,
] + APP_DEPENDENCIES
] + app_deps
if verbose:
click.echo(" ".join(cmd))
subprocess.run(cmd, check=True, cwd=SRC_ROOT)

# Finally, we run Docker. The Dockerfile will copy the zbundle into
# a temp folder and install it.
cmd = ["docker", "build", "-t", f"{IMAGE}:{VERSION}", "."]
cmd = ["docker", "build", "-t", f"{bundle_image}:{version}", "."]
if verbose:
click.echo(" ".join(cmd))
subprocess.run(cmd, check=True, cwd=SRC_ROOT)


@cli.command()
def run():
@click.option("--bundle-image", default=None)
@click.option("--verbose", default=False, is_flag=True)
@click.pass_obj
def run(config, bundle_image, verbose):
"""Run the Docker image for testing"""

# Configuration details
bundle_image = bundle_image or _bundle_image(config)
container_name = config["app_id"]
version = config["app_version"]

# Get values from the dev settings file (API tokens for testing, etc.)
envs = _load_dev_settings()

cmd = ["docker", "run", "--rm", "-p", "9000:9000", "--name", CONTAINER_NAME]
cmd = ["docker", "run", "--rm", "-p", "9000:9000", "--name", container_name]
for key, value in envs.items():
cmd += ["--env", f"{key}={value}"]
cmd += ["--env", "HOST_ADDRESS=0.0.0.0"]
cmd += [f"{IMAGE}:{VERSION}"]
cmd += [f"{bundle_image}:{version}"]

if verbose:
click.echo(" ".join(cmd))
subprocess.run(cmd, check=True, cwd=SRC_ROOT)


@cli.command()
def publish():
@click.option("--bundle-image", default=None)
@click.option("--verbose", default=False, is_flag=True)
@click.pass_obj
def publish(config, bundle_image, verbose):
"""Publish the Docker image for use with Edge"""
cmd = ["docker", "push", f"{IMAGE}:{VERSION}"]

# Configuration details
bundle_image = bundle_image or _bundle_image(config)
version = config["app_version"]

cmd = ["docker", "push", f"{bundle_image}:{version}"]
if verbose:
click.echo(" ".join(cmd))
subprocess.run(cmd, check=True)


Expand All @@ -104,5 +134,24 @@ def _load_dev_settings():
return {k: v for k, v in data.items() if k.startswith("EDGE_")}


def _load_config_settings():
"""Load the application configuration settings.

Returns
-------
dict
The configuration settings.
"""
data = {}
fpath = op.join(SRC_ROOT, "app_config.yaml")
if op.exists(fpath):
with open(fpath, "r") as f:
data = yaml.safe_load(f)

if not data:
raise ValueError("Could not load app_config.yaml")
return data


if __name__ == "__main__":
cli()
2 changes: 1 addition & 1 deletion Panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ version = AppVersion(
description="This Is An Example Edge Application",
icon=ICON,
kind=AppKindEnum.Native,
proxy_kind=AppProxyKindEnum.Panel,
framework=AppProxyKindEnum.Panel,
link="quay.io/<YOUR_ORGANIZATION>/YOUR_IMAGE_NAME_HERE:TAG",
recommended_profile="edge.medium"
)
Expand Down
35 changes: 35 additions & 0 deletions Panel/app_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
app_id: edge-panel-example
framework: panel
app_version: 1.3.0
python_version: 3.8
repository: quay.io
organisation: enthought
env_deps:
edm:
- click
- enthought_edge
- filetype
- oauthlib
- pip
- python_dateutil
- pyyaml
- requests
- setuptools
pip:
- questionary
app_deps:
- enthought_edge>=2.16.0
- appdirs
- packaging
- pip
- pyparsing
- setuptools
- six
- panel
- numpy
- pandas
- matplotlib
cmd_deps:
- docker
exclude:
- bootstrap.py
9 changes: 5 additions & 4 deletions Panel/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
import argparse
import subprocess


ENV_NAME = "edge-panel-example"
EDM_DEPS = ["click", "pip", "setuptools"]
EDM_DEPS = ["click", "pip", "pyyaml", "setuptools"]


def bootstrap(ci_mode):
Expand All @@ -25,7 +26,7 @@ def bootstrap(ci_mode):
"""

if ENV_NAME not in _list_edm_envs():
print(f"Creating development environment {ENV_NAME}...")
print(f"Creating development environment '{ENV_NAME}'...")
cmd = ["edm", "envs", "create", ENV_NAME, "--version", "3.8", "--force"]
subprocess.run(cmd, check=True)

Expand All @@ -35,10 +36,10 @@ def bootstrap(ci_mode):
print("Bootstrap complete.")

else:
print("Environment already exists; reusing.")
print(f"Environment '{ENV_NAME}' already exists; reusing.")

if not ci_mode:
print(f"Activating dev environment {ENV_NAME}")
print(f"Activating environment '{ENV_NAME}'")
subprocess.run(["edm", "shell", "-e", ENV_NAME])


Expand Down
Loading
Loading