Skip to content
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
2 changes: 1 addition & 1 deletion .dev/install
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash

pip install -U pip poetry
poetry install --no-root
poetry install
2 changes: 1 addition & 1 deletion metablock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .spaces import Block, Service, Space, SpaceExtension
from .user import User

__version__ = "0.9.0"
__version__ = "1.0.0"

__all__ = [
"Metablock",
Expand Down
78 changes: 74 additions & 4 deletions metablock/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import os
import zipfile
from pathlib import Path

import click
Expand All @@ -8,6 +9,8 @@
from metablock import Metablock

METABLOCK_SPACE = os.environ.get("METABLOCK_SPACE", "")
METABLOCK_ENV = os.environ.get("METABLOCK_ENV", "prod")
METABLOCK_BLOCK_ID = os.environ.get("METABLOCK_BLOCK_ID", "")
METABLOCK_API_TOKEN = os.environ.get("METABLOCK_API_TOKEN", "")


Expand All @@ -22,15 +25,50 @@ def main() -> None:

@main.command()
@click.argument("path", type=click.Path(exists=True))
@click.option("--space", "space_name", help="Space name", default=METABLOCK_SPACE)
@click.option(
"--space",
"space_name",
help="Space name",
default=METABLOCK_SPACE,
show_default=True,
)
@click.option("--token", help="metablock API token", default=METABLOCK_API_TOKEN)
def apply(path: str, space_name: str, token: str) -> None:
"""Apply metablock manifest"""
"""Apply metablock manifest to a metablock space"""
asyncio.get_event_loop().run_until_complete(
_apply(path, space_name or METABLOCK_SPACE, token or METABLOCK_API_TOKEN)
)


@main.command()
@click.argument("path", type=click.Path(exists=True))
@click.option(
"--env",
help="Environment to ship to",
type=click.Choice(["prod", "stage"]),
default=METABLOCK_ENV,
show_default=True,
)
@click.option(
"--block-id",
help="Block ID",
default=METABLOCK_BLOCK_ID,
show_default=True,
)
@click.option(
"--name",
help="Optional deployment name",
default="shipped from metablock-py",
show_default=True,
)
@click.option("--token", help="metablock API token", default=METABLOCK_API_TOKEN)
def ship(path: str, env: str, block_id: str, name: str, token: str) -> None:
"""Deploy a new version of html block"""
asyncio.get_event_loop().run_until_complete(
_ship(path, env, block_id, name, token or METABLOCK_API_TOKEN)
)


async def _apply(path: str, space_name: str, token: str) -> None:
if not token:
click.echo("metablock API token is required", err=True)
Expand All @@ -52,9 +90,41 @@ async def _apply(path: str, space_name: str, token: str) -> None:
block = by_name.get(name)
if block:
# update
await mb.services.update(block.id, **config)
await mb.blocks.update(block.id, **config)
click.echo(f"updated block {name}")
else:
# create
await space.services.create(name=name, **config)
await space.blocks.create(name=name, **config)
click.echo(f"created new block {name}")


async def _ship(path: str, env: str, block_id: str, name: str, token: str) -> None:
if not token:
click.echo("metablock API token is required", err=True)
raise click.Abort()
if not block_id:
click.echo("metablock block-id is required", err=True)
raise click.Abort()
p = Path(path)
if not p.is_dir():
click.echo(f"path {p} does not exist", err=True)
raise click.Abort()

# Create a zip file from the directory
zip_path = p.with_suffix(".zip")
try:
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
for file in p.rglob("*"): # Recursively add all files in the directory
arcname = file.relative_to(p) # Preserve relative paths in the archive
zipf.write(file, arcname)
click.echo(f"Created zip file: {zip_path}")

async with Metablock(auth_key=token) as mb:
block = await mb.blocks.get(block_id)
await block.ship(zip_path, name=name, env=env)
click.echo(f"shipped {zip_path} to {block.name} {env}")
finally:
# Clean up the zip file after shipping
if zip_path.exists():
zip_path.unlink()
click.echo(f"Removed temporary zip file: {zip_path}")
18 changes: 6 additions & 12 deletions metablock/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@ def __init__(
}
self.orgs: Orgs = Orgs(self, Org)
self.spaces: Spaces = Spaces(self, Space)
self.blocks: Blocks = Blocks(self, Block, "services")
self.blocks: Blocks = Blocks(self, Block)
self.plugins: Plugins = Plugins(self, Plugin)
self.extensions: Extensions = Extensions(self, Extension)
self.domains = Domains(self)
self.services = self.blocks

@property
def cli(self) -> Self:
Expand All @@ -66,7 +65,7 @@ async def __aexit__(self, exc_type: type, exc_val: Any, exc_tb: Any) -> None:
await self.close()

async def spec(self) -> dict:
return await self.request(f"{self.url}/spec")
return await self.request(f"{self.url}/openapi.json")

async def get(self, url: str, **kwargs: Any) -> Any:
kwargs["method"] = "GET"
Expand All @@ -93,7 +92,7 @@ async def request(
url: str,
method: str = "",
headers: dict[str, str] | None = None,
callback: Callback | None = None,
callback: Callback | bool | None = None,
wrap: Any = None,
**kw: Any,
) -> Any:
Expand All @@ -103,7 +102,9 @@ async def request(
headers_ = self.get_default_headers()
headers_.update(headers or ())
response = await self.session.request(method, url, headers=headers_, **kw)
if callback:
if callback is True:
return response
elif callback:
return await callback(response)
else:
return await self.handle_response(response, wrap=wrap)
Expand All @@ -125,10 +126,6 @@ async def get_user(self, **kw: Any) -> User:
kw.setdefault("wrap", self._user)
return await self.get(f"{self.url}/user", **kw)

async def get_space(self, **kw: Any) -> Space:
kw.setdefault("wrap", self._space)
return await self.get(f"{self.url}/space", **kw)

async def update_user(self, **kw: Any) -> User:
kw.setdefault("wrap", self._user)
return await self.patch(f"{self.url}/user", **kw)
Expand All @@ -144,6 +141,3 @@ def get_default_headers(self) -> dict[str, str]:

def _user(self, data: dict) -> User:
return User(self, data)

def _space(self, data: dict) -> Space:
return Space(self.spaces, data)
4 changes: 2 additions & 2 deletions metablock/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class Extensions(CrudComponent[Extension]):

# Plugin
class Plugin(MetablockEntity):
"""Object representing an Plugin"""
"""Object representing a Plugin"""

async def blocks(self, **kwargs: Any) -> list[BlockPlugin]:
return await self.cli.get(f"{self.url}/services", **kwargs)
return await self.cli.get(f"{self.url}/blocks", **kwargs)


class Plugins(CrudComponent[Plugin]):
Expand Down
16 changes: 9 additions & 7 deletions metablock/spaces.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from pathlib import Path
from typing import Any

from .components import Component, CrudComponent, MetablockEntity
Expand All @@ -11,12 +12,7 @@ class Space(MetablockEntity):

@property
def blocks(self) -> SpaceBlocks:
return SpaceBlocks(self, Block, "services")

@property
def services(self) -> SpaceBlocks: # pragma: no cover
# TODO: Deprecate
return self.blocks
return SpaceBlocks(self, Block, "blocks")

@property
def extensions(self) -> SpaceExtensions:
Expand Down Expand Up @@ -46,8 +42,14 @@ async def certificate(self, *, callback: Any = None) -> dict:
return await self.cli.get(f"{self.url}/certificate", callback=callback)

async def ship(
self, name: str, bundle: str, env: str = "stage", *, callback: Any = None
self,
bundle_path: str | Path,
name: str = "",
env: str = "stage",
*,
callback: Any = None,
) -> dict:
bundle = str(bundle_path)
return await self.cli.post(
f"{self.url}/deployments",
data=dict(name=name, env=env),
Expand Down
Loading