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

chore: explicit sequential execution; extra args param for run/deploy commands #557

Merged
merged 13 commits into from
Aug 23, 2024
7 changes: 6 additions & 1 deletion docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
- [-p, --project-name ](#-p---project-name--1)
- [Arguments](#arguments-8)
- [ENVIRONMENT_NAME](#environment_name)
- [EXTRA_ARGS](#extra_args)
- [link](#link)
- [Options](#options-22)
- [-p, --project-name ](#-p---project-name--2)
Expand Down Expand Up @@ -916,7 +917,7 @@ algokit project bootstrap poetry [OPTIONS]
Deploy smart contracts from AlgoKit compliant repository.

```shell
algokit project deploy [OPTIONS] [ENVIRONMENT_NAME]
algokit project deploy [OPTIONS] [ENVIRONMENT_NAME] [EXTRA_ARGS]...
```

### Options
Expand Down Expand Up @@ -951,6 +952,10 @@ Specify the project directory. If not provided, current working directory will b
### ENVIRONMENT_NAME
Optional argument


### EXTRA_ARGS
Optional argument(s)

### link

Automatically invoke 'algokit generate client' on contract projects available in the workspace.
Expand Down
15 changes: 14 additions & 1 deletion docs/features/project/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Deploy your smart contracts effortlessly to various networks with the algokit pr
## Usage

```sh
$ algokit project deploy [OPTIONS] [ENVIRONMENT_NAME]
$ algokit project deploy [OPTIONS] [ENVIRONMENT_NAME] [EXTRA_ARGS]
```

This command deploys smart contracts from an AlgoKit compliant repository to the specified network.
Expand All @@ -22,6 +22,7 @@ This command deploys smart contracts from an AlgoKit compliant repository to the
- `-p, --project-name`: (Optional) Projects to execute the command on. Defaults to all projects found in
the current directory. Option is mutually exclusive with `--command`.
- `-h, --help`: Show this message and exit.
- `[EXTRA_ARGS]...`: Additional arguments to pass to the deploy command. For instance, `algokit project deploy -- {custom args}`. This will ensure that the extra arguments are passed to the deploy command specified in the `.algokit.toml` file or directly via `--command` option.

## Environment files

Expand Down Expand Up @@ -183,6 +184,18 @@ Example:
$ algokit project deploy testnet --ci
```

## Passing Extra Arguments

You can pass additional arguments to the deploy command. These extra arguments will be appended to the end of the deploy command specified in your `.algokit.toml` file or to the command specifified directly via `--command` option. Declare after `--` to mark distinction between arguments used by cli vs arguments to be passed as extras to the deploy command/script defined.

Example:

```sh
$ algokit project deploy testnet -- my_contract_name --some_contract_related_param
```

In this example, `my_contract_name` is an extra argument that can be utilized under custom deploy command invocation to filter the deployment to a specific contract.

## Example of a Full Deployment

```sh
Expand Down
74 changes: 59 additions & 15 deletions docs/features/project/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ $ algokit project run [OPTIONS] COMMAND [ARGS]

This command executes a custom command defined in the `.algokit.toml` file of the current project or workspace.

### Options

- `-l, --list`: List all projects associated with the workspace command. (Optional)
- `-p, --project-name`: Execute the command on specified projects. Defaults to all projects in the current directory. (Optional)
- `-t, --type`: Limit execution to specific project types if executing from workspace. (Optional)
- `-s, --sequential`: Execute workspace commands sequentially, for cases where you do not have a preference on the execution order, but want to disable concurrency. (Optional, defaults to concurrent)
- `[ARGS]...`: Additional arguments to pass to the custom command. These will be appended to the end of the command specified in the `.algokit.toml` file.

To get detailed help on the above options, execute:

```bash
algokit project run {name_of_your_command} --help
```

### Workspace vs Standalone Projects

AlgoKit supports two main types of project structures: Workspaces and Standalone Projects. This flexibility caters to the diverse needs of developers, whether managing multiple related projects or focusing on a single application.
Expand Down Expand Up @@ -113,27 +127,57 @@ Executing `algokit project run hello` from the root of the workspace will concur

Executing `algokit project run hello` from the root of `project_(a|b)` will execute `echo hello` in the `project_(a|b)` directory.

### Controlling order of execution
### Controlling Execution Order

To control order of execution, simply define the order for a particular command as follows:
Customize the execution order of commands in workspaces for precise control:

```yaml
# ... other non [project.run] related metadata
[project]
type = 'workspace'
projects_root_path = 'projects'
1. Define order in `.algokit.toml`:

[project.run]
hello = ['project_a', 'project_b']
# ... other non [project.run] related metadata
```
```yaml
[project]
type = 'workspace'
projects_root_path = 'projects'

[project.run]
hello = ['project_a', 'project_b']
```

2. Execution behavior:
- Projects are executed in the specified order
- Invalid project names are skipped
- Partial project lists: Specified projects run first, others follow

> Note: Explicit order always triggers sequential execution.

### Controlling Concurrency

You can control whether commands are executed concurrently or sequentially:

1. Use command-line options:

Now if project_a and project_b are both defined as standalone projects, the order of execution will be respected. Additional behaviour can be described as follows:
```sh
$ algokit project run hello -s # or --sequential
$ algokit project run hello -c # or --concurrent
```

- Providing invalid project names will skip the execution of the command for the invalid project names.
- If only a subset of projects declaring this command are specified, the order of execution will be respected for the subset of projects first before the rest of the projects are executed in non-deterministic order.
2. Behavior:
- Default: Concurrent execution
- Sequential: Use `-s` or `--sequential` flag
- Concurrent: Use `-c` or `--concurrent` flag or omit the flag (defaults to concurrent)

> Note: When an explicit order is specified in `.algokit.toml`, execution is always sequential regardless of these flags.

### Passing Extra Arguments

You can pass additional arguments to the custom command. These extra arguments will be appended to the end of the command specified in your `.algokit.toml` file.

Example:

```sh
$ algokit project run hello -- world
```

> Please note, when enabling explicit order of execution all commands will always run sequentially.
In this example, if the `hello` command in `.algokit.toml` is defined as `echo "Hello"`, the actual command executed will be `echo "Hello" world`.

## Further Reading

Expand Down
11 changes: 10 additions & 1 deletion src/algokit/cli/project/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def _execute_deploy_command( # noqa: PLR0913
interactive: bool,
deployer_alias: str | None,
dispenser_alias: str | None,
extra_args: tuple[str],
) -> None:
logger.debug(f"Deploying from project directory: {path}")
logger.debug("Loading deploy command from project config")
Expand All @@ -103,7 +104,7 @@ def _execute_deploy_command( # noqa: PLR0913
"and no generic command available."
)
raise click.ClickException(msg)
resolved_command = resolve_command_path(config.command)
resolved_command = resolve_command_path(config.command + list(extra_args))
logger.info(f"Using deploy command: {' '.join(resolved_command)}")
logger.info("Loading deployment environment variables...")
config_dotenv = load_deploy_env_files(environment_name, path)
Expand Down Expand Up @@ -209,6 +210,11 @@ def convert(
"command",
],
)
@click.argument(
"extra_args",
nargs=-1,
type=click.UNPROCESSED,
)
@click.pass_context
def deploy_command( # noqa: PLR0913
ctx: click.Context,
Expand All @@ -220,6 +226,7 @@ def deploy_command( # noqa: PLR0913
deployer_alias: str | None,
dispenser_alias: str | None,
project_names: tuple[str],
extra_args: tuple[str],
) -> None:
"""Deploy smart contracts from AlgoKit compliant repository."""

Expand Down Expand Up @@ -272,6 +279,7 @@ def deploy_command( # noqa: PLR0913
interactive=interactive,
deployer_alias=deployer_alias,
dispenser_alias=dispenser_alias,
extra_args=extra_args,
)
else:
_execute_deploy_command(
Expand All @@ -281,4 +289,5 @@ def deploy_command( # noqa: PLR0913
interactive=interactive,
deployer_alias=deployer_alias,
dispenser_alias=dispenser_alias,
extra_args=extra_args,
)
33 changes: 23 additions & 10 deletions src/algokit/cli/project/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ def _load_project_commands(project_dir: Path) -> dict[str, click.Command]:

for custom_command in custom_commands:
# Define the base command function
def base_command(
def base_command( # noqa: PLR0913
*,
args: list[str],
custom_command: ProjectCommand | WorkspaceProjectCommand = custom_command,
project_names: tuple[str] | None = None,
list_projects: bool = False,
project_type: str | None = None,
sequential: bool = False,
extra_args: tuple[str] | None = None,
) -> None:
"""
Executes a base command function with optional parameters for listing projects or specifying project names.
Expand All @@ -57,32 +58,35 @@ def base_command(
within a workspace.

Args:
args (list[str]): The command arguments to be passed to the custom command.
extra_args (list[str]): The command arguments to be passed to the custom command.
custom_command (ProjectCommand | WorkspaceProjectCommand): The custom command to be executed.
project_names (list[str] | None): Optional. A list of project names to execute the command on.
list_projects (bool): Optional. A flag indicating whether to list projects associated
with a workspace command.
project_type (str | None): Optional. Only execute commands in projects of specified type.

sequential (bool): Whether to execute wokspace commands sequentially. Defaults to False.
Returns:
None
"""
if args:
logger.warning("Ignoring unrecognized arguments: %s.", " ".join(args))

if list_projects and isinstance(custom_command, WorkspaceProjectCommand):
for command in custom_command.commands:
cmds = " && ".join(" ".join(cmd) for cmd in command.commands)
logger.info(f"ℹ️ Project: {command.project_name}, Command name: {command.name}, Command(s): {cmds}") # noqa: RUF001
return

run_command(command=custom_command) if isinstance(
run_command(command=custom_command, extra_args=extra_args) if isinstance(
custom_command, ProjectCommand
) else run_workspace_command(custom_command, list(project_names or []), project_type)
) else run_workspace_command(
workspace_command=custom_command,
project_names=list(project_names or []),
project_type=project_type,
sequential=sequential,
extra_args=extra_args,
)

# Check if the command is a WorkspaceProjectCommand and conditionally decorate
is_workspace_command = isinstance(custom_command, WorkspaceProjectCommand)
command = click.argument("args", nargs=-1, type=click.UNPROCESSED, required=False)(base_command)
command = click.argument("extra_args", nargs=-1, type=click.UNPROCESSED, required=False)(base_command)
if is_workspace_command:
command = click.option(
"project_names",
Expand Down Expand Up @@ -118,6 +122,15 @@ def base_command(
default=None,
help="Limit execution to specific project types if executing from workspace. (Optional)",
)(command)
command = click.option(
aorumbayev marked this conversation as resolved.
Show resolved Hide resolved
"sequential",
"--sequential/--concurrent",
"-s/-c",
help="Execute workspace commands sequentially. Defaults to concurrent.",
default=False,
is_flag=True,
required=False,
)(command)

# Apply the click.command decorator with common options
command = click.command(
Expand Down
Loading