Skip to content

Application and CLI

Paasify CLI: paasify.cli

paasify.cli

Paasify CLI interface

This API provides a similar experience as the CLI, but in Python.

Example:

test.py
from paasify.cli import app

paasify = app()
paasify.info()
paasify.apply()

CatchErrors

Bases: typer.core.TyperGroup

Class to catch program exceptions and make a nice shutdown

Source code in paasify/cli.py
class CatchErrors(typer.core.TyperGroup):
    "Class to catch program exceptions and make a nice shutdown"

    def __call__(self, *args, **kwargs):
        "Used to catch program exceptions"

        try:
            return self.main(*args, **kwargs)
        except Exception as exc:
            clean_terminate(exc)

            # If we reached here, then let's go fo a good old Exception
            raise
__call__(*args, **kwargs)

Used to catch program exceptions

Source code in paasify/cli.py
def __call__(self, *args, **kwargs):
    "Used to catch program exceptions"

    try:
        return self.main(*args, **kwargs)
    except Exception as exc:
        clean_terminate(exc)

        # If we reached here, then let's go fo a good old Exception
        raise

DocSubCmd

Bases: str, Enum

Source sub commands

Source code in paasify/cli.py
class DocSubCmd(str, Enum):
    "Source sub commands"
    COLLECTION = "collection"
    CONF = "conf"

SrcSubCmd

Bases: str, Enum

Source sub commands

Source code in paasify/cli.py
class SrcSubCmd(str, Enum):
    "Source sub commands"

    LS = "ls"
    UPDATE = "update"
    TREE = "tree"
    INSTALL = "install"

app()

Return a Paasify App instance

Source code in paasify/cli.py
def app():
    "Return a Paasify App instance"

    try:
        return cli_app()

    # pylint: disable=broad-except
    except Exception as err:

        # Developper catchall
        log.error(traceback.format_exc())
        log.critical(f"Uncatched error {err.__class__}; this may be a bug!")
        log.critical("Exit 1 with bugs")
        sys.exit(1)

clean_terminate(err)

Terminate nicely the program depending the exception

Source code in paasify/cli.py
def clean_terminate(err):
    "Terminate nicely the program depending the exception"

    if PAASIFY_TRACE:
        log.error(traceback.format_exc())

    oserrors = [
        PermissionError,
        FileExistsError,
        FileNotFoundError,
        InterruptedError,
        IsADirectoryError,
        NotADirectoryError,
        TimeoutError,
    ]

    # Choose dead end way
    if isinstance(err, error.PaasifyError):
        err_name = err.__class__.__name__
        if isinstance(err.advice, str):
            log.warning(err.advice)

        log.error(err)
        log.critical(f"Paasify exited with: error {err.rc}: {err_name}")
        sys.exit(err.rc)

    if isinstance(err, yaml.scanner.ScannerError):
        log.critical(err)
        log.critical("Paasify exited with: YAML Scanner error (file syntax)")
        sys.exit(error.YAMLError.rc)

    if isinstance(err, yaml.composer.ComposerError):
        log.critical(err)
        log.critical("Paasify exited with: YAML Composer error (file syntax)")
        sys.exit(error.YAMLError.rc)

    if isinstance(err, yaml.parser.ParserError):
        log.critical(err)
        log.critical("Paasify exited with: YAML Parser error (file format)")
        sys.exit(error.YAMLError.rc)

    if isinstance(err, sh.ErrorReturnCode):
        log.critical(err)
        log.critical(f"Paasify exited with: failed command returned {err.exit_code}")
        sys.exit(error.ShellCommandFailed.rc)

    if isinstance(err, CaframException):
        log.critical(err)
        log.critical(f"Paasify exited with backend error. {type(err)}")
        sys.exit(error.ConfigBackendError.rc)

    if err.__class__ in oserrors:

        # Decode OS errors
        # errno = os.strerror(err.errno)
        # errint = str(err.errno)

        log.critical(f"Paasify exited with OS error: {err}")
        sys.exit(err.errno)

cli_apply(ctx, logs=typer.Option(False, '--logs', '-l', help='Show running logs after action'), stack=typer.Argument(None, help='Stack to target, current directory or all'))

Build and apply stack

Source code in paasify/cli.py
@cli_app.command("apply", rich_help_panel=HELP_STACKS_HELPERS)
def cli_apply(
    ctx: typer.Context,
    logs: bool = typer.Option(
        False, "--logs", "-l", help="Show running logs after action"
    ),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Build and apply stack"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_apply(stack_names=stack)

    if logs:
        prj.stacks.cmd_stack_logs(stack_names=stack, follow=True)

cli_assemble(ctx, explain=typer.Option(False, '--explain', '-X', help='Show diff between modifications'), stack=typer.Argument(None, help='Stack to target, current directory or all'))

Build docker-files

Source code in paasify/cli.py
@cli_app.command("build", rich_help_panel=HELP_STACKS_CMD)
def cli_assemble(
    ctx: typer.Context,
    explain: bool = typer.Option(
        False, "--explain", "-X", help="Show diff between modifications"
    ),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Build docker-files"""

    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_assemble(stack_names=stack, explain=explain)

cli_doc_collection(ctx, out=typer.Option(None, '-o', '--out', help='Out directory, where all files are generated'), mkdocs_config=typer.Option(None, '-m', '--mkdocs_config', help='Directory where to create mkdocs config, omited if empty'), path=typer.Argument('.', help='Directory of the project to create'))

Build collection documentation

Source code in paasify/cli.py
@cli_app.command("document_collection", rich_help_panel=HELP_DOC_CMD)
def cli_doc_collection(
    ctx: typer.Context,
    out: Optional[Path] = typer.Option(
        None,
        "-o",
        "--out",
        help="Out directory, where all files are generated",
    ),
    mkdocs_config: Optional[Path] = typer.Option(
        None,
        "-m",
        "--mkdocs_config",
        help="Directory where to create mkdocs config, omited if empty",
    ),
    path: Optional[str] = typer.Argument(
        ".", help="Directory of the project to create"
    ),
):
    """Build collection documentation"""
    psf = ctx.obj["paasify"]
    assert isinstance(path, str), f"Got: {path}"
    psf.cmd_document(path=path, dest_dir=out, mkdocs_config=mkdocs_config)

cli_doc_conf(ctx, out=typer.Option(None, '-o', '--out', help='Out directory, where all files are generated'), format_=typer.Option(OutputFormat.json, '-t', '--format', help='Output format for stdout'))

Build configuration schema documentation

Source code in paasify/cli.py
@cli_app.command("document_conf", rich_help_panel=HELP_DOC_CMD)
def cli_doc_conf(
    ctx: typer.Context,
    out: Optional[Path] = typer.Option(
        None,
        "-o",
        "--out",
        help="Out directory, where all files are generated",
    ),
    format_: OutputFormat = typer.Option(
        OutputFormat.json,
        "-t",
        "--format",
        help="Output format for stdout",
    ),
):
    """Build configuration schema documentation"""
    psf = ctx.obj["paasify"]
    ret = psf.cmd_config_schema(dest_dir=out)
    if not out:
        if format_ == OutputFormat.yaml:
            ret = to_yaml(ret)
        elif format_ == OutputFormat.json:
            ret = to_json(ret)
        print(ret)

cli_document(ctx, cmd)

Command sources

Source code in paasify/cli.py
@cli_app.command(
    "document",
    hidden=True,
    context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
)
def cli_document(
    ctx: typer.Context,
    cmd: DocSubCmd,
):
    """Command sources"""
    sub = {
        "collection": cli_doc_collection,
        "conf": cli_doc_conf,
    }
    func = sub.get(cmd)
    func(ctx, *ctx.args)

cli_down(ctx, stack=typer.Argument(None, help='Stack to target, current directory or all'))

Stop docker stack

Source code in paasify/cli.py
@cli_app.command("down", rich_help_panel=HELP_STACKS_CMD)
def cli_down(
    ctx: typer.Context,
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Stop docker stack"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_down(stack_names=stack)

cli_help(ctx)

Show this help message

Source code in paasify/cli.py
@cli_app.command("help")
def cli_help(
    ctx: typer.Context,
):
    """Show this help message"""
    print(ctx.parent.get_help())

cli_info(ctx)

Show context infos

Source code in paasify/cli.py
@cli_app.command("info", rich_help_panel=HELP_PROJECT_CMD)
def cli_info(
    ctx: typer.Context,
):
    """Show context infos"""
    psf = ctx.obj["paasify"]
    psf.info(autoload=True)

cli_logs(ctx, follow=typer.Option(False, '--follow', '-f'), stack=typer.Argument(None, help='Stack to target, current directory or all'))

Show stack logs

Source code in paasify/cli.py
@cli_app.command("logs", rich_help_panel=HELP_STACKS_CMD)
def cli_logs(
    ctx: typer.Context,
    follow: bool = typer.Option(False, "--follow", "-f"),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Show stack logs"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_logs(stack_names=stack, follow=follow)

cli_ls(ctx, explain=typer.Option(False, '--explain', '-X', help='Show stacks structure'), stack=typer.Argument(None, help='Stack to target, current directory or all, only when explain is enabled'))

List all stacks

Source code in paasify/cli.py
@cli_app.command("ls", rich_help_panel=HELP_STACKS_CMD)
def cli_ls(
    ctx: typer.Context,
    explain: bool = typer.Option(
        False, "--explain", "-X", help="Show stacks structure"
    ),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all, only when explain is enabled",
    ),
):
    """List all stacks"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    if explain:
        prj.stacks.cmd_stack_explain(stack_names=stack)
    else:
        prj.stacks.cmd_stack_ls()

cli_new(ctx, source=typer.Option(None, '--from', help='Use another project as source'), path=typer.Argument('.', help='Directory of the project to create'))

Create a new paasify project

Source code in paasify/cli.py
@cli_app.command("new", rich_help_panel=HELP_PROJECT_CMD)
def cli_new(
    ctx: typer.Context,
    source: Optional[str] = typer.Option(
        None,
        "--from",
        help="Use another project as source",
    ),
    path: Optional[str] = typer.Argument(
        ".", help="Directory of the project to create"
    ),
):
    """Create a new paasify project"""
    psf = ctx.obj["paasify"]
    psf.new_project(path, source=source)

cli_ps(ctx, stack=typer.Argument(None, help='Stack to target, current directory or all'))

Show docker stack instances

Source code in paasify/cli.py
@cli_app.command("ps", rich_help_panel=HELP_STACKS_CMD)
def cli_ps(
    ctx: typer.Context,
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Show docker stack instances"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_ps(stack_names=stack)

cli_recreate(ctx, logs=typer.Option(False, '--logs', '-l', help='Show running logs after action'), stack=typer.Argument(None, help='Stack to target, current directory or all'))

Stop, rebuild and create stack

Source code in paasify/cli.py
@cli_app.command("recreate", rich_help_panel=HELP_STACKS_HELPERS)
def cli_recreate(
    ctx: typer.Context,
    logs: bool = typer.Option(
        False, "--logs", "-l", help="Show running logs after action"
    ),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Stop, rebuild and create stack"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_recreate(stack_names=stack)

    if logs:
        prj.stacks.cmd_stack_logs(stack_names=stack, follow=True)

cli_src(ctx, cmd)

Command sources

Source code in paasify/cli.py
@cli_app.command(
    "src",
    hidden=True,
    context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
)
def cli_src(
    ctx: typer.Context,
    cmd: SrcSubCmd,
):
    """Command sources"""
    sub = {
        "ls": src_ls,
        "update": src_update,
        "tree": src_tree,
        "install": src_install,
    }
    func = sub.get(cmd)
    return ctx.invoke(func, ctx)

cli_up(ctx, logs=typer.Option(False, '--logs', '-l', help='Show running logs after action'), stack=typer.Argument(None, help='Stack to target, current directory or all'))

Start docker stack

Source code in paasify/cli.py
@cli_app.command("up", rich_help_panel=HELP_STACKS_CMD)
def cli_up(
    ctx: typer.Context,
    logs: bool = typer.Option(
        False, "--logs", "-l", help="Show running logs after action"
    ),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Start docker stack"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.stacks.cmd_stack_up(stack_names=stack)

    if logs:
        prj.stacks.cmd_stack_logs(stack_names=stack, follow=True)

cli_vars(ctx, vars=typer.Option(None, help='List of vars comma separated to show only'), explain=typer.Option(False, '--explain', '-X', help='Show running logs after action'), stack=typer.Argument(None, help='Stack to target, current directory or all'))

Dump stack variables

Source code in paasify/cli.py
@cli_app.command("vars", rich_help_panel=HELP_STACKS_CMD)
def cli_vars(
    ctx: typer.Context,
    vars: Optional[str] = typer.Option(
        None,
        help="List of vars comma separated to show only",
    ),
    explain: bool = typer.Option(
        False, "--explain", "-X", help="Show running logs after action"
    ),
    stack: Optional[str] = typer.Argument(
        None,
        help="Stack to target, current directory or all",
    ),
):
    """Dump stack variables"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    vars = vars.split(",") if vars else True
    prj.stacks.cmd_stack_vars(stack_names=stack, vars_=vars, explain=explain)

main(ctx, verbose=typer.Option(0, '--verbose', '-v', count=True, min=0, max=5, help='Increase verbosity'), working_dir=typer.Option(None, '-c', '--config', help='Path of paasify.yml configuration file.', envvar='PAASIFY_PROJECT_DIR'), version=typer.Option(False, '-V', '--version', help='Show version info'), trace=typer.Option(False, '--trace', help='Show traces'))

Prepare Paasify App instance.

Source code in paasify/cli.py
@cli_app.callback(invoke_without_command=True, context_settings=help_args)
def main(
    ctx: typer.Context,
    verbose: int = typer.Option(
        0, "--verbose", "-v", count=True, min=0, max=5, help="Increase verbosity"
    ),
    working_dir: Optional[Path] = typer.Option(
        # os.getcwd(),  # For absolute paths
        # ".",          # For relative paths
        None,  # For automagic
        "-c",
        "--config",
        help="Path of paasify.yml configuration file.",
        envvar="PAASIFY_PROJECT_DIR",
    ),
    version: bool = typer.Option(
        False,
        "-V",
        "--version",
        help="Show version info",
    ),
    trace: bool = typer.Option(
        False,
        "--trace",
        help="Show traces",
    ),
):
    """

    Prepare Paasify App instance.
    """

    # pylint: disable=global-statement,invalid-name
    global log
    global PAASIFY_TRACE
    PAASIFY_TRACE = trace

    # 50: Crit
    # 40: Err
    # 30: Warn
    #   25: Notice
    # 20: Info
    #   15: Exec
    # 10: Debug
    #   5: Trace
    # 0: Not set

    # Detect extra logging
    dump_payload_log = False
    if verbose > 4:
        verbose -= 1
        dump_payload_log = True

    # Calculate log level
    verbose += 1
    verbose = 30 - (verbose * 5)
    verbose = verbose if verbose > 0 else 0

    # Logger format
    fmt = {
        0: "default",
        2: "struct",
        3: "precise",
        4: "time",
    }
    sformat = "default"
    if trace:
        sformat = fmt[2]

    # Get logger level
    log = get_logger(logger_name="paasify.cli", sformat=sformat)
    log.setLevel(level=verbose)
    # test_logging()

    # Report logging state
    if dump_payload_log:
        log.trace("Exta verbose mode enabled: data will be dumped into logs")
    else:
        log.trace("Trace mode enabled")

    if version:
        print(__version__)
        return

    app_conf = {
        "config": {
            "default_source": "default",
            "cwd": os.getcwd(),
            "root_hint": working_dir,
            "dump_payload_log": dump_payload_log,
            "no_tty": NOTTY,
            # "collections_dir": collections_dir,
        }
    }

    # Init paasify
    paasify = PaasifyApp(payload=app_conf)
    log.debug("Paasify app has been started")

    ctx.obj = {
        "paasify": paasify,
    }

src_install(ctx)

Install sources

Source code in paasify/cli.py
@cli_app.command("src install", rich_help_panel=HELP_SRC_CMD)
def src_install(
    ctx: typer.Context,
):
    """Install sources"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.sources.cmd_install()

src_ls(ctx, explain=typer.Option(False, '--explain', '-X', help='Show collection and apps'))

List sources

Source code in paasify/cli.py
@cli_app.command("src ls", rich_help_panel=HELP_SRC_CMD)
def src_ls(
    ctx: typer.Context,
    explain: bool = typer.Option(
        False, "--explain", "-X", help="Show collection and apps"
    ),
):
    """List sources"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.sources.cmd_ls(explain=explain)

src_tree(ctx)

Show source tree

Source code in paasify/cli.py
@cli_app.command("src tree", rich_help_panel=HELP_SRC_CMD)
def src_tree(
    ctx: typer.Context,
):
    """Show source tree"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.sources.cmd_tree()

src_update(ctx)

Update sources

Source code in paasify/cli.py
@cli_app.command("src update", rich_help_panel=HELP_SRC_CMD)
def src_update(
    ctx: typer.Context,
):
    """Update sources"""
    paasify = ctx.obj["paasify"]
    prj = paasify.load_project()
    prj.sources.cmd_update()

test_logging()

Function to test logging

Source code in paasify/cli.py
def test_logging():
    "Function to test logging"

    log.critical("SHOW CRITICAL")
    log.error("SHOW ERROR")
    log.warning("SHOW WARNING")
    log.notice("SHOW NOTICE")
    log.info("SHOW INFO")
    log.exec("SHOW EXEC")
    log.debug("SHOW DEBUG")
    log.trace("SHOW TRACE")

Paasify App: paasify.app

paasify.app

Paasify Application library

This library provides a convenient paasify user friendly API.

Example:

test.py
from paasify.app import PaasifyApp

app = PaasifyApp()

print (app.info())
prj = app.load_project()
prj.dump()

PaasifyApp

Bases: NodeMap, PaasifyObj

Paasify Main application Instance

Source code in paasify/app.py
class PaasifyApp(NodeMap, PaasifyObj):
    "Paasify Main application Instance"

    ident = "Paasify App"

    conf_default = {
        "config": {},
        "project": {},
    }

    conf_children = [
        {
            "key": "project",
            "cls": PaasifyProject,
            "action": "unset",
        },
    ]

    conf_schema = {
        # "$defs": {
        #     "AppProject": PaasifyProject.conf_schema,
        # },
        # "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "title": "Paasify App",
        "description": "Paasify app implementation",
        "additionalProperties": False,
        # "required": [
        #     "stacks"
        # ],
        "default": {},
        "properties": {
            "project": {
                "title": "Project configuration",
                "description": "See: schema prj",
                # "oneOf": [
                #     {
                #         "$ref": "#/$defs/AppProject",
                #         "description": "Instanciate project",
                #         "type": "object",
                #     },
                #     {
                #         "description": "Config file or path",
                #         "type": "string",
                #     },
                #     {
                #         "description": "Do not instanciate project",
                #         "type": "null",
                #     },
                # ],
            },
            "config": {
                "title": "Application configuration",
                "type": "object",
                "additionalProperties": True,
                # TODO: Add schema for appp
            },
        },
    }

    # Project management
    # ======================

    def load_project(self, path=None):
        "Return closest project"

        if self.project is not None:
            return self.project

        payload = path or {
            "_runtime": self.config,
        }

        prj = PaasifyProject(
            parent=self,
            payload=payload,  # Only string or nested runtime dict
        )

        self.add_child("project", prj)
        return prj

    def new_project(self, path, source=None):
        """Create a new project"""

        assets_dir = get_paasify_pkg_dir()
        changed = False
        created = ensure_dir_exists(path)
        prj_name = os.path.basename(os.path.abspath(path))
        print("NAME", prj_name)

        # Prepare configs
        build_conf = {
            "paasify": {
                "dest": os.path.join(path, "paasify.yml"),
            },
            "gitignore": {
                "dest": os.path.join(path, ".gitignore"),
            },
        }
        lookups = {
            "paasify": {
                "path": os.path.join(assets_dir, "assets"),
                "pattern": ["paasify.yml", "paasify.yaml"],
            },
            "gitignore": {
                "path": os.path.join(assets_dir, "assets"),
                "pattern": "gitignore",
            },
        }
        templates = {
            "requirements": {
                "content": "paasify>={version}\n",
                "dest": os.path.join(path, "requirements.txt"),
            },
            "readme": {
                "content": README_TEMPLATE,
                "dest": os.path.join(path, "README.md"),
            },
        }

        # Add source if requested
        if source:
            sources = {
                "paasify": {"path": source, "pattern": ["paasify.yml", "paasify.yaml"]},
                "gitignore": {
                    "path": source,
                    "pattern": ".gitignore",
                },
            }

        # Generate files
        for name, template in templates.items():
            dest = template["dest"]
            if not os.path.exists(dest):
                self.log.notice(f"Create: '{dest}'")
                content = template["content"].format(
                    name=prj_name, version=paasify_version
                )
                write_file(dest, content)
                changed = True
            else:
                self.log.info(f"Skip: '{dest}' as it already exists")

        # Build each assets
        for name, conf in lookups.items():
            lookup = FileLookup()
            if source:
                lookup.append(sources[name]["path"], sources[name]["pattern"])
            lookup.append(conf["path"], conf["pattern"])
            match = lookup.match(first=True)
            if not match:
                continue

            # Copy files
            src = match["match"]
            dest = build_conf[name]["dest"]
            if not os.path.exists(dest):
                self.log.notice(f"Create: '{dest}' from '{src}'")
                _exec("cp", cli_args=[src, dest])
                changed = True
            else:
                self.log.info(f"Skip: '{dest}' as it already exists")

        # Init git directory
        git_dir = os.path.join(path, ".git")
        if not os.path.exists(git_dir):
            self.log.notice(f"Create git repo in '{path}'")
            files = [
                file
                for file in os.listdir(path)
                if os.path.isfile(os.path.join(path, file))
            ]
            _exec("git", ["init", path])
            _exec("git", ["-C", path, "add"] + files)
            changed = True
        else:
            self.log.info(f"Directory is alrayd a git repository:{path}")

        # Report actions
        msg = "No changes in existing project"
        if created:
            if source:
                msg = f"New project created in: {path} from {source}"
            else:
                msg = f"New project created in: {path}"
        elif changed:
            if source:
                msg = f"New project updated in: {path} from {source}"
            else:
                msg = f"New project updated in: {path}"
        self.log.notice(msg)

    # App commands
    # ======================

    def info(self, autoload=None):
        """Report app config"""

        print("Paasify App Info:")
        print("==================")
        for key, val in self.config.items():
            print(f"  {key}: {val}")

        print("\nPaasify Project Info:")
        print("==================")

        # Autoload default project
        msg = ""
        if autoload is None or bool(autoload):
            try:
                if not self.project:
                    self.log.notice("Info is autoloading project")
                    self.load_project()
            except error.ProjectNotFound as err:
                msg = err
                if autoload is True:
                    raise error.ProjectNotFound(err) from err

        if self.project:
            # Report with active project if available
            for key, val in self.project.runtime.get_value(lvl=-1).items():
                print(f"  {key}: {val}")
        else:
            print(f"  {msg}")

    def cmd_document(self, path=None, dest_dir=None, mkdocs_config=None):
        "Generate documentation"

        documator = CollectionDocumator(
            parent=self, path=path, dest_dir=dest_dir, mkdocs_config=mkdocs_config
        )
        documator.generate(
            mkdocs_config=None,
        )

    # pylint: disable=redefined-builtin
    def cmd_config_schema(self, format=None, dest_dir=None):
        """Returns the configuration json schema

        Args:
            dest_dir (str, optional): Destination directory

        """

        config = dict(
            minify=False,
            deprecated_from_description=True,
            default_from_description=False,
            expand_buttons=False,
            link_to_reused_ref=False,
            copy_css=True,
            copy_js=True,
            # config=config_file,
            # config_parameters=config,
        )
        # config = {}
        self.log.info("TEST 1")

        # Ensure that dest dir exists
        if dest_dir:
            self.log.notice(f"Documentation directory: {dest_dir}")
            if not os.path.isdir(dest_dir):
                self.log.notice(f"Create parent directories: {dest_dir}")
                os.makedirs(dest_dir)

        targets = ["app", "prj", "prj_config", "prj_sources", "prj_stacks"]
        ret = {}
        for target in targets:

            # Select target to document
            if target == "app":
                schema = self.conf_schema
            elif target == "prj":
                schema = PaasifyProject.conf_schema
            elif target == "prj_config":
                schema = PaasifyProjectConfig.conf_schema
            elif target == "prj_sources":
                schema = SourcesManager.conf_schema
            elif target == "prj_stacks":
                schema = StackManager.conf_schema
            else:
                raise NotImplementedError(f"Target '{target}' is not supported")

            ret[target] = schema
            if not schema:
                assert False, "You found a bug!"

            if not dest_dir:
                continue

            slug = f"conf_{target}"
            json_file = os.path.join(dest_dir, f"{slug}.json")

            write_file(json_file, to_json(schema))
            self.log.info(f"Generate '{slug}.html' from: {json_file}")
            gen_schema_doc(json_file, dest_dir, f"{slug}.html", config=config)

        return ret
cmd_config_schema(format=None, dest_dir=None)

Returns the configuration json schema

Parameters:

Name Type Description Default
dest_dir str

Destination directory

None
Source code in paasify/app.py
def cmd_config_schema(self, format=None, dest_dir=None):
    """Returns the configuration json schema

    Args:
        dest_dir (str, optional): Destination directory

    """

    config = dict(
        minify=False,
        deprecated_from_description=True,
        default_from_description=False,
        expand_buttons=False,
        link_to_reused_ref=False,
        copy_css=True,
        copy_js=True,
        # config=config_file,
        # config_parameters=config,
    )
    # config = {}
    self.log.info("TEST 1")

    # Ensure that dest dir exists
    if dest_dir:
        self.log.notice(f"Documentation directory: {dest_dir}")
        if not os.path.isdir(dest_dir):
            self.log.notice(f"Create parent directories: {dest_dir}")
            os.makedirs(dest_dir)

    targets = ["app", "prj", "prj_config", "prj_sources", "prj_stacks"]
    ret = {}
    for target in targets:

        # Select target to document
        if target == "app":
            schema = self.conf_schema
        elif target == "prj":
            schema = PaasifyProject.conf_schema
        elif target == "prj_config":
            schema = PaasifyProjectConfig.conf_schema
        elif target == "prj_sources":
            schema = SourcesManager.conf_schema
        elif target == "prj_stacks":
            schema = StackManager.conf_schema
        else:
            raise NotImplementedError(f"Target '{target}' is not supported")

        ret[target] = schema
        if not schema:
            assert False, "You found a bug!"

        if not dest_dir:
            continue

        slug = f"conf_{target}"
        json_file = os.path.join(dest_dir, f"{slug}.json")

        write_file(json_file, to_json(schema))
        self.log.info(f"Generate '{slug}.html' from: {json_file}")
        gen_schema_doc(json_file, dest_dir, f"{slug}.html", config=config)

    return ret
cmd_document(path=None, dest_dir=None, mkdocs_config=None)

Generate documentation

Source code in paasify/app.py
def cmd_document(self, path=None, dest_dir=None, mkdocs_config=None):
    "Generate documentation"

    documator = CollectionDocumator(
        parent=self, path=path, dest_dir=dest_dir, mkdocs_config=mkdocs_config
    )
    documator.generate(
        mkdocs_config=None,
    )
info(autoload=None)

Report app config

Source code in paasify/app.py
def info(self, autoload=None):
    """Report app config"""

    print("Paasify App Info:")
    print("==================")
    for key, val in self.config.items():
        print(f"  {key}: {val}")

    print("\nPaasify Project Info:")
    print("==================")

    # Autoload default project
    msg = ""
    if autoload is None or bool(autoload):
        try:
            if not self.project:
                self.log.notice("Info is autoloading project")
                self.load_project()
        except error.ProjectNotFound as err:
            msg = err
            if autoload is True:
                raise error.ProjectNotFound(err) from err

    if self.project:
        # Report with active project if available
        for key, val in self.project.runtime.get_value(lvl=-1).items():
            print(f"  {key}: {val}")
    else:
        print(f"  {msg}")
load_project(path=None)

Return closest project

Source code in paasify/app.py
def load_project(self, path=None):
    "Return closest project"

    if self.project is not None:
        return self.project

    payload = path or {
        "_runtime": self.config,
    }

    prj = PaasifyProject(
        parent=self,
        payload=payload,  # Only string or nested runtime dict
    )

    self.add_child("project", prj)
    return prj
new_project(path, source=None)

Create a new project

Source code in paasify/app.py
def new_project(self, path, source=None):
    """Create a new project"""

    assets_dir = get_paasify_pkg_dir()
    changed = False
    created = ensure_dir_exists(path)
    prj_name = os.path.basename(os.path.abspath(path))
    print("NAME", prj_name)

    # Prepare configs
    build_conf = {
        "paasify": {
            "dest": os.path.join(path, "paasify.yml"),
        },
        "gitignore": {
            "dest": os.path.join(path, ".gitignore"),
        },
    }
    lookups = {
        "paasify": {
            "path": os.path.join(assets_dir, "assets"),
            "pattern": ["paasify.yml", "paasify.yaml"],
        },
        "gitignore": {
            "path": os.path.join(assets_dir, "assets"),
            "pattern": "gitignore",
        },
    }
    templates = {
        "requirements": {
            "content": "paasify>={version}\n",
            "dest": os.path.join(path, "requirements.txt"),
        },
        "readme": {
            "content": README_TEMPLATE,
            "dest": os.path.join(path, "README.md"),
        },
    }

    # Add source if requested
    if source:
        sources = {
            "paasify": {"path": source, "pattern": ["paasify.yml", "paasify.yaml"]},
            "gitignore": {
                "path": source,
                "pattern": ".gitignore",
            },
        }

    # Generate files
    for name, template in templates.items():
        dest = template["dest"]
        if not os.path.exists(dest):
            self.log.notice(f"Create: '{dest}'")
            content = template["content"].format(
                name=prj_name, version=paasify_version
            )
            write_file(dest, content)
            changed = True
        else:
            self.log.info(f"Skip: '{dest}' as it already exists")

    # Build each assets
    for name, conf in lookups.items():
        lookup = FileLookup()
        if source:
            lookup.append(sources[name]["path"], sources[name]["pattern"])
        lookup.append(conf["path"], conf["pattern"])
        match = lookup.match(first=True)
        if not match:
            continue

        # Copy files
        src = match["match"]
        dest = build_conf[name]["dest"]
        if not os.path.exists(dest):
            self.log.notice(f"Create: '{dest}' from '{src}'")
            _exec("cp", cli_args=[src, dest])
            changed = True
        else:
            self.log.info(f"Skip: '{dest}' as it already exists")

    # Init git directory
    git_dir = os.path.join(path, ".git")
    if not os.path.exists(git_dir):
        self.log.notice(f"Create git repo in '{path}'")
        files = [
            file
            for file in os.listdir(path)
            if os.path.isfile(os.path.join(path, file))
        ]
        _exec("git", ["init", path])
        _exec("git", ["-C", path, "add"] + files)
        changed = True
    else:
        self.log.info(f"Directory is alrayd a git repository:{path}")

    # Report actions
    msg = "No changes in existing project"
    if created:
        if source:
            msg = f"New project created in: {path} from {source}"
        else:
            msg = f"New project created in: {path}"
    elif changed:
        if source:
            msg = f"New project updated in: {path} from {source}"
        else:
            msg = f"New project updated in: {path}"
    self.log.notice(msg)