-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
airbyte-ci: make airbyte-ci test
able to run any poetry run command
#33784
Changes from all commits
87665c1
a84b5a3
004cdcc
05f5378
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,48 @@ | ||
# | ||
# Copyright (c) 2023 Airbyte, Inc., all rights reserved. | ||
# | ||
from __future__ import annotations | ||
|
||
import logging | ||
from pathlib import Path | ||
from typing import TYPE_CHECKING | ||
|
||
import asyncclick as click | ||
import asyncer | ||
from pipelines.cli.click_decorators import click_ignore_unused_kwargs, click_merge_args_into_context_obj | ||
from pipelines.consts import DOCKER_VERSION | ||
from pipelines.helpers.utils import sh_dash_c | ||
from pipelines.models.contexts.click_pipeline_context import ClickPipelineContext, pass_pipeline_context | ||
|
||
if TYPE_CHECKING: | ||
from typing import List, Tuple | ||
|
||
import dagger | ||
|
||
## HELPERS | ||
async def run_poetry_command(container: dagger.Container, command: str) -> Tuple[str, str]: | ||
"""Run a poetry command in a container and return the stdout and stderr. | ||
Args: | ||
container (dagger.Container): The container to run the command in. | ||
command (str): The command to run. | ||
Returns: | ||
Tuple[str, str]: The stdout and stderr of the command. | ||
""" | ||
container = container.with_exec(["poetry", "run", *command.split(" ")]) | ||
return await container.stdout(), await container.stderr() | ||
|
||
|
||
@click.command() | ||
@click.argument("poetry_package_path") | ||
@click.option("--test-directory", default="tests", help="The directory containing the tests to run.") | ||
@click.option( | ||
"-c", | ||
"--poetry-run-command", | ||
multiple=True, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This means you can pass the option multiple times, not that you can pass multiple arguements to the option, right? An example in the docs of how to pass multiple may be helpful, as well as noting somewhere (new column?) that it can be passed multiple times. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes you're right. I added an example to the README. |
||
help="The poetry run command to run.", | ||
required=True, | ||
) | ||
@click_merge_args_into_context_obj | ||
@pass_pipeline_context | ||
@click_ignore_unused_kwargs | ||
|
@@ -24,7 +53,10 @@ async def test(pipeline_context: ClickPipelineContext): | |
pipeline_context (ClickPipelineContext): The context object. | ||
""" | ||
poetry_package_path = pipeline_context.params["poetry_package_path"] | ||
test_directory = pipeline_context.params["test_directory"] | ||
if not Path(f"{poetry_package_path}/pyproject.toml").exists(): | ||
raise click.UsageError(f"Could not find pyproject.toml in {poetry_package_path}") | ||
|
||
commands_to_run: List[str] = pipeline_context.params["poetry_run_command"] | ||
|
||
logger = logging.getLogger(f"{poetry_package_path}.tests") | ||
logger.info(f"Running tests for {poetry_package_path}") | ||
|
@@ -47,7 +79,7 @@ async def test(pipeline_context: ClickPipelineContext): | |
|
||
pipeline_name = f"Unit tests for {poetry_package_path}" | ||
dagger_client = await pipeline_context.get_dagger_client(pipeline_name=pipeline_name) | ||
pytest_container = await ( | ||
test_container = await ( | ||
dagger_client.container() | ||
.from_("python:3.10.12") | ||
.with_env_variable("PIPX_BIN_DIR", "/usr/local/bin") | ||
|
@@ -73,10 +105,20 @@ async def test(pipeline_context: ClickPipelineContext): | |
), | ||
) | ||
.with_workdir(f"/airbyte/{poetry_package_path}") | ||
.with_exec(["poetry", "install"]) | ||
.with_exec(["poetry", "install", "--with=dev"]) | ||
.with_unix_socket("/var/run/docker.sock", dagger_client.host().unix_socket("/var/run/docker.sock")) | ||
.with_env_variable("CI", str(pipeline_context.params["is_ci"])) | ||
.with_exec(["poetry", "run", "pytest", test_directory]) | ||
.with_workdir(f"/airbyte/{poetry_package_path}") | ||
) | ||
|
||
await pytest_container | ||
soon_command_executions_results = [] | ||
async with asyncer.create_task_group() as poetry_commands_task_group: | ||
for command in commands_to_run: | ||
logger.info(f"Running command: {command}") | ||
soon_command_execution_result = poetry_commands_task_group.soonify(run_poetry_command)(test_container, command) | ||
soon_command_executions_results.append(soon_command_execution_result) | ||
|
||
for result in soon_command_executions_results: | ||
stdout, stderr = result.value | ||
logger.info(stdout) | ||
logger.error(stderr) | ||
Comment on lines
+114
to
+124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still get the exit on failure behavior that we want? I know in format, when running multiple steps concurrently we had to add explicit error exiting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If an execution fails the async context will throw an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, great! I guess before it was different because we wanted to continue all the others and collect successes and failures? I suppose that could be helpful here too, but it's not blocking for me, as its all our code that we're checking There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes you're right, we could a Dagger exec error handling to better handle success / failure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With multiple commands this will output a jumble of lines which will be very hard to relate to the command itself. Sequentializing the output will require more code. Is the concurrency here really worth it in terms of the added complexity? I'm asking because I don't actually know; however if it's not really worth it then let's execute the poetry commands sequentially. More code => bigger maintenance burden. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we be better off returning a
dagger.Container
here? I'm a bit surprised that we're not interested in the exit code, but I guess that dagger handles failures for us transparently.