Docs

Groups and local commands

Organize related functions into groups with shared secrets, and add local-only CLI commands.

A project can hold many functions. Put related ones in a Group. People see a group as one section of the project in the web app, and you run its functions as call <group> <action>.

from typing import Literal

from callables import Group

backups = Group(
    "backups",
    title="Database backups",
    description="Postgres dumps to object storage",
    secrets=["SPACES_KEY", "SPACES_SECRET"],
)


@backups.deploy(title="Back up now", secrets=["DATABASE_URL"])
def run(env: Literal["prod", "staging"] = "prod", keep: int = 7) -> str:
    ...


@backups.deploy(title="List backups")
def list_all(days: int = 30) -> list[dict]:
    ...
call backups --help
call backups run --help
call backups run --env staging --keep 3

Rules

  • A group's secrets go to every action in it. Each action can declare more of its own.
  • A group name is lowercase letters, digits, - or _, and can't be a built-in command such as check or local.
  • Define each group once and import it wherever you need it.
  • An action's full name is <group>.<name>, such as backups.run. Use that with call local.
  • @group.deploy takes the same options as @deploy.
  • Moving an existing function into a group gives it a new name, so it starts with fresh run history and sharing settings.
  • Plain @deploy functions still work. They appear in the project without a section.

Local commands

Some tools only make sense on your own machine: they read local files, write reports to disk or open a browser. Write them with @command (or @group.command) instead of @deploy, so the project has one CLI:

from callables import Group

audits = Group("audits", title="Audits")


@audits.command
def render(slug: str, open_browser: bool = False) -> str:
    ...
  • They run as call audits render --slug x, and with call local. call check lists them as "(local only)".
  • They're never deployed. Nobody can run them from the web app.
  • @command takes name, title, description and next, but no secrets, schedule or timeout. Read secrets from your own .env.
  • Their module is still imported when the platform builds, so keep imports that only work locally inside the function.
  • Return the result instead of printing it. The CLI prints what you return: aligned columns in a terminal, JSON when piped.
  • To deploy one later, change @command to @deploy. It keeps its name.