-
Notifications
You must be signed in to change notification settings - Fork 255
Setup running e2e tests in Docker image #70
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
Changes from all commits
bc6dc39
a38f1bd
0480f6f
d44c4e9
9cc925b
7fa5c84
fdc9575
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 |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # syntax=docker/dockerfile:1.4 | ||
|
|
||
| ARG PYTHON_VERSION="3.11.4" | ||
| FROM python:${PYTHON_VERSION}-slim-bullseye | ||
|
|
||
| SHELL ["/bin/bash", "-o", "pipefail", "-e", "-u", "-x", "-c"] | ||
|
|
||
| # Setup Pip | ||
| ARG PIP_VERSION="23.2.1" | ||
| ENV PIP_VERSION=${PIP_VERSION} | ||
|
|
||
| RUN pip install --no-cache-dir --upgrade "pip==${PIP_VERSION}" && pip --version | ||
|
|
||
| ENV PYTHONUNBUFFERED=1 | ||
| ENV PIP_ROOT_USER_ACTION=ignore | ||
| RUN mkdir /component | ||
| WORKDIR /component | ||
|
|
||
| # Install streamlit and components | ||
| ARG STREAMLIT_VERSION="latest" | ||
| ENV E2E_STREAMLIT_VERSION=${STREAMLIT_VERSION} | ||
|
|
||
| RUN <<"EOF" | ||
| if [[ "${E2E_STREAMLIT_VERSION}" == "latest" ]]; then | ||
| pip install --no-cache-dir "streamlit" | ||
| elif [[ "${E2E_STREAMLIT_VERSION}" == "nightly" ]]; then | ||
| pip uninstall --yes streamlit | ||
| pip install --no-cache-dir "streamlit-nightly" | ||
| else | ||
| pip install --no-cache-dir "streamlit==${E2E_STREAMLIT_VERSION}" | ||
| fi | ||
|
|
||
| # Coherence check | ||
| installed_streamlit_version=$(python -c "import streamlit; print(streamlit.__version__)") | ||
| echo "Installed Streamlit version: ${installed_streamlit_version}" | ||
| if [[ "${E2E_STREAMLIT_VERSION}" == "nightly" ]]; then | ||
| echo "${installed_streamlit_version}" | grep 'dev' | ||
| else | ||
| echo "${installed_streamlit_version}" | grep -v 'dev' | ||
| fi | ||
| EOF |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |||||||
| import json | ||||||||
| import sys | ||||||||
| import shutil | ||||||||
| import os | ||||||||
|
|
||||||||
| THIS_DIRECTORY = Path(__file__).parent.absolute() | ||||||||
| EXAMPLE_DIRECTORIES = [d for d in (THIS_DIRECTORY / 'examples').iterdir() if d.is_dir()] | ||||||||
|
|
@@ -35,60 +36,85 @@ def run_verbose(cmd_args, *args, **kwargs): | |||||||
|
|
||||||||
| # Commands | ||||||||
| def cmd_all_npm_install(args): | ||||||||
| """"Install all node dependencies for all examples""" | ||||||||
| """Install all node dependencies for all examples""" | ||||||||
| for project_dir in EXAMPLE_DIRECTORIES + TEMPLATE_DIRECTORIES: | ||||||||
| frontend_dir = next(project_dir.glob("*/frontend/")) | ||||||||
| run_verbose(["npm", "install"], cwd=str(frontend_dir)) | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_all_npm_build(args): | ||||||||
| """"Build javascript code for all examples and templates""" | ||||||||
| """Build javascript code for all examples and templates""" | ||||||||
| for project_dir in EXAMPLE_DIRECTORIES + TEMPLATE_DIRECTORIES: | ||||||||
| frontend_dir = next(project_dir.glob("*/frontend/")) | ||||||||
| run_verbose(["npm", "run", "build"], cwd=str(frontend_dir)) | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_all_install_python_deps(args): | ||||||||
| """"Install all dependencies needed to run e2e tests for all examples and templates""" | ||||||||
| def cmd_e2e_build_images(args): | ||||||||
| """Build docker images for each component e2e tests""" | ||||||||
| for project_dir in EXAMPLE_DIRECTORIES + TEMPLATE_DIRECTORIES: | ||||||||
|
Contributor
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. Is it possible to aggregate all examples in one docker container? Setup docker takes some time and we probably only need separate dockers for template and template-reactless. Probably it can be done in later PR
Contributor
Author
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, it's possible. Let's discuss it with Kamil tomorrow, but I like the idea :)
Contributor
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. Our goal is less than 15 minutes and we seem to be achieving this so there is no need to optimize. If I was optimizing something, I would put playwright in the docker image so that we don't have to install the browser multiple times, but that's not even needed now. Multiple images and multiple containers are not a problem for me as long as they are lightweight.
Contributor
Author
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. Ok, fine for me for now. Do you agree @sfc-gh-pbelczyk ?
Contributor
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. Fine by me |
||||||||
| run_verbose(["pip", "install", "-e", ".[devel]"], cwd=str(project_dir)) | ||||||||
| e2e_dir = next(project_dir.glob("**/e2e/"), None) | ||||||||
| if e2e_dir and os.listdir(e2e_dir): | ||||||||
| # Define the image tag for the docker image | ||||||||
| image_tag = ( | ||||||||
| f"component-template:py-{args.python_version}-st-{args.streamlit_version}-component-{project_dir.parts[-1]}" | ||||||||
| ) | ||||||||
| # Build the docker image with specified build arguments | ||||||||
| run_verbose( | ||||||||
| [ | ||||||||
| "docker", | ||||||||
| "build", | ||||||||
| ".", | ||||||||
| f"--build-arg=STREAMLIT_VERSION={args.streamlit_version}", | ||||||||
| f"--build-arg=PYTHON_VERSION={args.python_version}", | ||||||||
| f"--tag={image_tag}", | ||||||||
| "--progress=plain", | ||||||||
| ], | ||||||||
| env={**os.environ, "DOCKER_BUILDKIT": "1"}, | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_all_install_wheel_packages(args): | ||||||||
| """"Install wheel packages of all examples and templates for e2e tests""" | ||||||||
| def cmd_e2e_run(args): | ||||||||
| """Run e2e tests for all examples and templates in separate docker images""" | ||||||||
| for project_dir in EXAMPLE_DIRECTORIES + TEMPLATE_DIRECTORIES: | ||||||||
| wheel_files = list(project_dir.glob("dist/*.whl")) | ||||||||
| if wheel_files: | ||||||||
| wheel_file = wheel_files[0] | ||||||||
| run_verbose(["pip", "install", str(wheel_file)], cwd=str(project_dir)) | ||||||||
| else: | ||||||||
| print(f"No wheel files found in {project_dir}") | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_install_browsers(args): | ||||||||
| """"Install multiple browsers to run e2e for all examples and templates""" | ||||||||
| run_verbose(["playwright", "install", "webkit", "chromium", "firefox", "--with-deps"]) | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_all_run_e2e(args): | ||||||||
| """"Run e2e tests for all examples and templates""" | ||||||||
| for project_dir in TEMPLATE_DIRECTORIES: | ||||||||
| container_name = project_dir.parts[-1] | ||||||||
| image_tag = ( | ||||||||
| f"component-template:py-{args.python_version}-st-{args.streamlit_version}-component-{container_name}" | ||||||||
| ) | ||||||||
| e2e_dir = next(project_dir.glob("**/e2e/"), None) | ||||||||
| if e2e_dir: | ||||||||
| with tempfile.TemporaryDirectory() as tmp_dir: | ||||||||
| run_verbose(['python', '-m', 'venv', f"{tmp_dir}/venv"]) | ||||||||
| wheel_files = list(project_dir.glob("dist/*.whl")) | ||||||||
| if wheel_files: | ||||||||
| wheel_file = wheel_files[0] | ||||||||
| run_verbose([f"{tmp_dir}/venv/bin/pip", "install", f"{str(wheel_file)}[devel]"], cwd=str(project_dir)) | ||||||||
| else: | ||||||||
| print(f"No wheel files found in {project_dir}") | ||||||||
| run_verbose([f"{tmp_dir}/venv/bin/pytest", "-s", "--browser", "webkit", "--browser", "chromium", "--browser", "firefox", "--reruns", "5", str(e2e_dir)]) | ||||||||
|
|
||||||||
| for project_dir in EXAMPLE_DIRECTORIES: | ||||||||
| if e2e_dir and os.listdir(e2e_dir): | ||||||||
| run_verbose([ | ||||||||
| "docker", | ||||||||
| "run", | ||||||||
| "--tty", | ||||||||
|
Contributor
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.
Suggested change
Running containers with |
||||||||
| "--rm", | ||||||||
| "--name", container_name, | ||||||||
| "--volume", f"{e2e_dir.parent}/:/component/", | ||||||||
| image_tag, | ||||||||
| "/bin/sh", "-c", # Run a shell command inside the container | ||||||||
| "find /component/dist/ -name '*.whl' | xargs -I {} echo '{}[devel]' | xargs pip install && " # Install whl package and dev dependencies | ||||||||
| f"playwright install webkit chromium firefox --with-deps && " # Install browsers | ||||||||
| f"pytest", # Run pytest | ||||||||
| "-s", | ||||||||
| "--browser", "webkit", | ||||||||
| "--browser", "chromium", | ||||||||
| "--browser", "firefox", | ||||||||
| "--reruns", "5", | ||||||||
| "--capture=no", | ||||||||
| "--setup-show" | ||||||||
| ]) | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_docker_images_cleanup(args): | ||||||||
| """Cleanup docker images and containers""" | ||||||||
| for project_dir in EXAMPLE_DIRECTORIES + TEMPLATE_DIRECTORIES: | ||||||||
| container_name = project_dir.parts[-1] | ||||||||
| image_name = ( | ||||||||
| f"component-template:py-{args.python_version}-st-{args.streamlit_version}-component-{container_name}" | ||||||||
| ) | ||||||||
| e2e_dir = next(project_dir.glob("**/e2e/"), None) | ||||||||
| if e2e_dir: | ||||||||
| run_verbose(["pytest", "-s", "--browser", "webkit", "--browser", "chromium", "--browser", "firefox", "--reruns", "5", str(e2e_dir)]) | ||||||||
| if e2e_dir and os.listdir(e2e_dir): | ||||||||
| # Remove the associated Docker image | ||||||||
| run_verbose(["docker", "rmi", image_name]) | ||||||||
|
|
||||||||
|
|
||||||||
| def cmd_all_python_build_package(args): | ||||||||
|
|
@@ -104,8 +130,8 @@ def cmd_all_python_build_package(args): | |||||||
|
|
||||||||
| def check_deps(template_package_json, current_package_json): | ||||||||
| return ( | ||||||||
| check_deps_section(template_package_json, current_package_json, 'dependencies') + | ||||||||
| check_deps_section(template_package_json, current_package_json, 'devDependencies') | ||||||||
| check_deps_section(template_package_json, current_package_json, 'dependencies') + | ||||||||
| check_deps_section(template_package_json, current_package_json, 'devDependencies') | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
||||||||
|
|
@@ -169,7 +195,8 @@ def cmd_check_templates_using_cookiecutter(args): | |||||||
| replay_file_content = json.loads(cookiecutter_variant.replay_file.read_text()) | ||||||||
|
|
||||||||
| with tempfile.TemporaryDirectory() as output_dir: | ||||||||
| print(f"Generating template with replay file: {cookiecutter_variant.replay_file.relative_to(THIS_DIRECTORY)}") | ||||||||
| print( | ||||||||
| f"Generating template with replay file: {cookiecutter_variant.replay_file.relative_to(THIS_DIRECTORY)}") | ||||||||
| run_verbose( | ||||||||
| [ | ||||||||
| "cookiecutter", | ||||||||
|
|
@@ -243,10 +270,27 @@ def cmd_update_templates(args): | |||||||
| "examples-check-deps": cmd_example_check_deps, | ||||||||
| "templates-check-not-modified": cmd_check_templates_using_cookiecutter, | ||||||||
| "templates-update": cmd_update_templates, | ||||||||
| "install-python-deps": cmd_all_install_python_deps, | ||||||||
| "install-wheel-packages": cmd_all_install_wheel_packages, | ||||||||
| "install-browsers": cmd_install_browsers, | ||||||||
| "run-e2e": cmd_all_run_e2e, | ||||||||
| "e2e-build-images": cmd_e2e_build_images, | ||||||||
| "e2e-run-tests": cmd_e2e_run, | ||||||||
| "docker-images-cleanup": cmd_docker_images_cleanup | ||||||||
| } | ||||||||
|
|
||||||||
| ARG_STREAMLIT_VERSION = ("--streamlit-version", "latest", "Streamlit version for which tests will be run.") | ||||||||
| ARG_PYTHON_VERSION = ("--python-version", os.environ.get("PYTHON_VERSION", "3.11.4"), "Python version for which tests will be run.") | ||||||||
|
|
||||||||
| ARGUMENTS = { | ||||||||
| "e2e-build-images": [ | ||||||||
| ARG_STREAMLIT_VERSION, | ||||||||
| ARG_PYTHON_VERSION | ||||||||
| ], | ||||||||
| "e2e-run-tests": [ | ||||||||
| ARG_STREAMLIT_VERSION, | ||||||||
| ARG_PYTHON_VERSION | ||||||||
| ], | ||||||||
| "docker-images-cleanup": [ | ||||||||
| (*ARG_STREAMLIT_VERSION[:2], f"Streamlit version used to create the Docker resources"), | ||||||||
| (*ARG_PYTHON_VERSION[:2], f"Python version used to create the Docker resources") | ||||||||
| ] | ||||||||
| } | ||||||||
|
|
||||||||
|
|
||||||||
|
|
@@ -256,7 +300,14 @@ def get_parser(): | |||||||
| subparsers = parser.add_subparsers(dest="subcommand", metavar="COMMAND") | ||||||||
| subparsers.required = True | ||||||||
| for command_name, command_fn in COMMANDS.items(): | ||||||||
| subparsers.add_parser(command_name, help=command_fn.__doc__).set_defaults(func=command_fn) | ||||||||
| subparser = subparsers.add_parser(command_name, help=command_fn.__doc__) | ||||||||
|
|
||||||||
| if command_name in ARGUMENTS: | ||||||||
| for arg_name, arg_default, arg_help in ARGUMENTS[command_name]: | ||||||||
| subparser.add_argument(arg_name, default=arg_default, help=arg_help) | ||||||||
|
|
||||||||
| subparser.set_defaults(func=command_fn) | ||||||||
|
|
||||||||
| return parser | ||||||||
|
|
||||||||
|
|
||||||||
|
|
||||||||
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.
This action job is called
build-examples-templates, but I think it does a little more now.