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

fix: adding fixes to allow working with local filesystem files when interacting with algokit goal commands #304

Merged
merged 42 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4b29d96
feat: Adding initial volume mount to docker compose
negar-abbasi Jul 19, 2023
06ab283
reverting two unnecessary files.
negar-abbasi Jul 19, 2023
ffb5981
chore: reverting two unnecessary files.
negar-abbasi Jul 19, 2023
34bc3ed
test: adding generated tests
negar-abbasi Jul 19, 2023
fe14827
Merge remote-tracking branch 'origin/volume-mount' into volume-mount
negar-abbasi Jul 19, 2023
3dbb290
feat: Adding seamless volume mount and copying files
negar-abbasi Jul 31, 2023
77e8af7
chore: wip tweaks/fixes for Negar
aorumbayev Jul 31, 2023
091f34f
fix: some bug fixes and reviews
negar-abbasi Aug 4, 2023
8cc2949
fix: fixing some ruff errors
negar-abbasi Aug 4, 2023
1d5a29b
test: adding tests
negar-abbasi Aug 9, 2023
24ed624
chore: updating poetry dependency
negar-abbasi Aug 9, 2023
29cab72
chore: fixing black errors
negar-abbasi Aug 9, 2023
90fe4a3
fix: fixing relative path issue
negar-abbasi Aug 9, 2023
545532f
test: changing some tests
negar-abbasi Aug 9, 2023
a3c5768
fix: adding a check for existing path for output file
negar-abbasi Aug 9, 2023
2323b0c
test: adding snapshot for tests
negar-abbasi Aug 9, 2023
50e774a
chore: wip extra tests
aorumbayev Aug 10, 2023
a3ce008
chore: fixing issue with the side_effect mock
aorumbayev Aug 10, 2023
919cbe7
chore: fixing ruff error for sideeffect
negar-abbasi Aug 11, 2023
388abfd
test: adding test fixtures(WIP)
negar-abbasi Aug 11, 2023
3f529e0
test: fixing test fixtures
negar-abbasi Aug 14, 2023
8708d11
test: Adding test asserts for postprocess
negar-abbasi Aug 14, 2023
7fbe1d8
test: add some test and fix creating files
negar-abbasi Aug 14, 2023
96f84db
chore: update poetry with gitpython
negar-abbasi Aug 14, 2023
c5d6383
Merge remote-tracking branch 'origin/main' into volume-mount
aorumbayev Aug 14, 2023
208f21c
refactor: fixing ruff, mypy errors, improving fixtures
aorumbayev Aug 14, 2023
e278fd3
fix: make tests compatible with windows paths.
negar-abbasi Aug 17, 2023
5866a91
test; adding tests to check the postprocess file existence.
negar-abbasi Aug 17, 2023
ee9b415
test: removing file paths in expected arguments
negar-abbasi Aug 17, 2023
ffac769
docs: adding a section in goal documents to explain delealing with files
negar-abbasi Aug 17, 2023
1e99525
test: remove unnecessary function
negar-abbasi Aug 17, 2023
10dab4e
test: remove unnecessary fixture
negar-abbasi Aug 17, 2023
93d20fc
fix: refactoring tests
aorumbayev Aug 17, 2023
76c80b0
fix: fixing tests
aorumbayev Aug 17, 2023
7f3f804
fix: fixing tests
aorumbayev Aug 17, 2023
4b0988c
refactor: removing todo comments
negar-abbasi Aug 18, 2023
7b98a01
refactor: adding normalize output function
negar-abbasi Aug 18, 2023
59cd97f
refactor: removing repetitive fixture usage
negar-abbasi Aug 18, 2023
dfad2a9
refactor: adding some comments
negar-abbasi Aug 18, 2023
982be3b
Update docs/features/goal.md
negar-abbasi Aug 20, 2023
315eb45
Update tests/utils/proc_mock.py
negar-abbasi Aug 20, 2023
b7913c3
docs: changes in wording of a doc string
negar-abbasi Aug 20, 2023
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
20 changes: 18 additions & 2 deletions docs/features/goal.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,29 @@ $ ~ algokit goal
Error: Error executing goal; ensure the Sandbox is started by executing `algokit sandbox status`
```

## Copying files in to the container
## Working with Files in the Container
When interacting with the container, especially if you're using tools like goal, you might need to reference files or directories. Here's how to efficiently deal with files and directories:

If you want to copy files into the container so you can access them via goal then you can use the following:
### Automatic File Mounting
When you specify a file or directory path in your `goal` command, the system will automatically mount that path from your local filesystem into the container. This way, you don't need to copy files manually each time.

For instance, if you want to compile a `teal` file:

```
goal clerk compile /Path/to/inputfile/approval.teal -o /Path/to/outputfile/approval.compiled
negar-abbasi marked this conversation as resolved.
Show resolved Hide resolved
```
Here, `/Path/to/inputfile/approval.teal` and `/Path/to/outputfile/approval.compiled` are paths on your local file system, and they will be automatically accessible to the `goal` command inside the container.

### Manual Copying of Files

In case you want to manually copy files into the container, you can do so using `docker cp`:

```
docker cp foo.txt algokit_algod:/root
```
This command copies the `foo.txt` from your local system into the root directory of the `algokit_algod` container.

Note: Manual copying is optional and generally only necessary if you have specific reasons for doing so since the system will auto-mount paths specified in commands.

## Running multiple commands

Expand Down
221 changes: 184 additions & 37 deletions poetry.lock

Large diffs are not rendered by default.

25 changes: 22 additions & 3 deletions src/algokit/cli/goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
import click

from algokit.core import proc
from algokit.core.sandbox import ComposeSandbox
from algokit.core.goal import (
get_volume_mount_path_docker,
get_volume_mount_path_local,
post_process,
preprocess_command_args,
)
from algokit.core.sandbox import ComposeFileStatus, ComposeSandbox

logger = logging.getLogger(__name__)

Expand All @@ -28,6 +34,9 @@ def goal_command(*, console: bool, goal_args: list[str]) -> None:

Look at https://developer.algorand.org/docs/clis/goal/goal/ for more information.
"""
volume_mount_path_local = get_volume_mount_path_local()
volume_mount_path_docker = get_volume_mount_path_docker()
goal_args = list(goal_args)
try:
proc.run(["docker", "version"], bad_return_code_error_message="Docker engine isn't running; please start it.")
except OSError as ex:
Expand All @@ -37,22 +46,32 @@ def goal_command(*, console: bool, goal_args: list[str]) -> None:
"Docker not found; please install Docker and add to path.\n"
"See https://docs.docker.com/get-docker/ for more information."
) from ex

sandbox = ComposeSandbox()
compose_file_status = sandbox.compose_file_status()
if compose_file_status is not ComposeFileStatus.UP_TO_DATE:
raise click.ClickException("Sandbox definition is out of date; please run `algokit localnet reset` first!")

if console:
if goal_args:
logger.warning("--console opens an interactive shell, remaining arguments are being ignored")
logger.info("Opening Bash console on the algod node; execute `exit` to return to original console")
result = proc.run_interactive("docker exec -it -w /root algokit_algod bash".split())
else:
cmd = "docker exec --interactive --workdir /root algokit_algod goal".split()
cmd.extend(goal_args)
input_files, output_files, goal_args = preprocess_command_args(
goal_args, volume_mount_path_local, volume_mount_path_docker
)
cmd = cmd + goal_args
result = proc.run(
cmd,
stdout_log_level=logging.INFO,
prefix_process=False,
pass_stdin=True,
)
post_process(input_files, output_files, volume_mount_path_local)

if result.exit_code != 0:
sandbox = ComposeSandbox()
ps_result = sandbox.ps("algod")
match ps_result:
case [{"State": "running"}]:
Expand Down
80 changes: 80 additions & 0 deletions src/algokit/core/goal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import logging
import re
import shutil
from pathlib import Path, PurePath

from algokit.core.conf import get_app_config_dir

logger = logging.getLogger(__name__)


def get_volume_mount_path_docker() -> Path:
return Path("/root/goal_mount/")


def get_volume_mount_path_local() -> Path:
return get_app_config_dir().joinpath("sandbox", "goal_mount")


def is_path_or_filename(argument: str) -> bool:
filename_pattern = re.compile(r"^[\w-]+\.\w+$")
return filename_pattern.match(argument) is not None or len(PurePath(argument).parts) > 1


def delete_files_from_volume_mount(filename: str, volume_mount_path_docker: Path) -> None:
try:
volume_mount_path_docker.joinpath(filename).unlink()
except Exception as e:
logger.error(e)


def list_files_in_volume(volume_path: Path) -> list[str]:
file_paths = []
if volume_path.exists() and volume_path.is_dir():
for file in volume_path.rglob("*"):
if file.is_file():
file_paths.append(str(file))
else:
logger.error(f"{volume_path} does not exist or is not a directory.")
return file_paths


def preprocess_command_args(
command: list[str], volume_mount_path_local: Path, docker_mount_path_local: Path
) -> tuple[list[str], list[Path], list[str]]:
input_filenames = []
output_filenames = []
try:
for i, arg in enumerate(command):
if is_path_or_filename(arg):
arg_path = Path(arg)
arg_changed = docker_mount_path_local.joinpath(arg_path.name)
command[i] = str(arg_changed)

file_exists = arg_path.exists() or Path.cwd().joinpath(arg_path.name).exists()
is_output_arg = i > 0 and command[i - 1] in ["-o", "--outdir", "--outfile"]
if file_exists and not is_output_arg:
input_filenames.append(arg_path.name)
shutil.copy(arg_path, volume_mount_path_local)
elif is_output_arg: # it is an output file that is not exist now
output_filenames.append(arg_path)
else:
raise FileNotFoundError(f"{arg} does not exist.")
except Exception as e:
logger.error(e)
raise e
return input_filenames, output_filenames, command


def post_process(input_filenames: list, output_filenames: list[Path], volume_mount_path_local: Path) -> None:
for input_filename in input_filenames:
delete_files_from_volume_mount(input_filename, volume_mount_path_local)

files_in_volume_mount = {Path(file).name for file in list_files_in_volume(volume_mount_path_local)}
for output_filename in output_filenames:
if output_filename.name in files_in_volume_mount:
target_path = (
output_filename if output_filename.is_absolute() else Path.cwd().joinpath(output_filename.name)
)
shutil.copy(volume_mount_path_local.joinpath(output_filename.name), target_path)
delete_files_from_volume_mount(output_filename.name, volume_mount_path_local)
1 change: 1 addition & 0 deletions src/algokit/core/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def get_docker_compose_yml(
- type: bind
source: ./algod_config.json
target: /etc/algorand/config.json
- ./goal_mount:/root/goal_mount

indexer:
container_name: {name}_indexer
Expand Down
Loading