From 923442d24cefb978aadf719b7fa9f00cdefa5595 Mon Sep 17 00:00:00 2001 From: Matt Wright Date: Thu, 1 Feb 2024 18:21:55 -0500 Subject: [PATCH] Make static dir configurable --- .gitignore | 1 + CI.py | 8 +++--- Dockerfile | 6 ++--- example_app/cli.py | 13 ++++++++-- example_app/factory.py | 22 ++++++++++------ example_app/templates.py | 35 +++++++++++++++----------- tests/test_factory.py | 11 ++++++++ webpack.config.js | 54 ++++++++++++++++++++-------------------- 8 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 tests/test_factory.py diff --git a/.gitignore b/.gitignore index 01f1fbb..ffa9c57 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ venv/ ENV/ env.bak/ venv.bak/ +dist/ # mypy .mypy_cache/ diff --git a/CI.py b/CI.py index 4f56fff..0fe13fc 100644 --- a/CI.py +++ b/CI.py @@ -14,10 +14,12 @@ async def test(): ".venv", ".vscode", "**/*/__pycache__", + "dist", "node_modules", "test-results", - "example_app/static/bundles", + ".gitgnore", "CI.py", + "docker-compose.yaml", "webpack-stats.json", ], ) @@ -50,8 +52,8 @@ async def test(): .with_exec(["poetry", "run", "playwright", "install", "chromium"]) .with_directory("/src", source_code) .with_directory( - "/src/example_app/static/bundles", - javascript.directory("/src/example_app/static/bundles"), + "/opt/app/static/js", + javascript.directory("/src/dist/js"), ) .with_exec(["poetry", "install", "--without", "playwright"]) .with_exec(["poetry", "run", "pytest"]) diff --git a/Dockerfile b/Dockerfile index dbeb655..c37f57e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,16 +15,16 @@ RUN apt-get update \ ADD pyproject.toml poetry.lock /usr/src/ RUN pip install poetry RUN poetry config virtualenvs.in-project true -RUN poetry install --no-ansi --no-dev && rm pyproject.toml poetry.lock +RUN poetry install --no-ansi --without playwright,dev && rm pyproject.toml poetry.lock FROM python:3.12-slim WORKDIR /app COPY --from=python-build /usr/src /app -COPY --from=static-build /usr/src/example_app/static/bundles /app/example_app/static/bundles +COPY --from=static-build /usr/src/dist/js /opt/app/static/js ADD ./example_app /app/example_app RUN addgroup --gid 1000 app RUN adduser app -h /app -u 1000 -G app -DH USER 1000 EXPOSE 8000 -ENTRYPOINT [".venv/bin/python", "-m", "example_app"] +ENTRYPOINT [".venv/bin/python", "-m", "example_app", "run", "--debug", "--log-level", "debug"] diff --git a/example_app/cli.py b/example_app/cli.py index 898477c..36049e8 100644 --- a/example_app/cli.py +++ b/example_app/cli.py @@ -16,6 +16,15 @@ def cli(): @click.option("--debug", is_flag=True, default=False, help="Enable debug mode") @click.option("--reload", is_flag=True, default=False, help="Reload code on changes") @click.option("--log-level", default="info", help="Log level") -def run(host: str, port: int, debug: bool, reload: bool, log_level: str): +@click.option( + "--static-dir", + default="/opt/app/static", + help="Filesystem path to static files directory", +) +def run( + host: str, port: int, debug: bool, reload: bool, log_level: str, static_dir: str +): configure_logging(log_level) - run_server(host, port, reload, log_level, AppConfig(debug=debug)) + run_server( + host, port, reload, log_level, AppConfig(debug=debug, static_dir=static_dir) + ) diff --git a/example_app/factory.py b/example_app/factory.py index d46f950..4809628 100644 --- a/example_app/factory.py +++ b/example_app/factory.py @@ -5,10 +5,10 @@ from litestar.channels.backends.redis import RedisChannelsPubSubBackend from litestar.static_files import StaticFilesConfig from redis.asyncio import Redis - from example_app.etc import app_resource + from example_app.routes import index, ws -from example_app.templates import template_config +from example_app.templates import create_template_config def create_redis_client(uri: str): @@ -19,10 +19,11 @@ def create_redis_client(uri: str): class AppConfig: debug: bool = False redis_uri: str = "redis://redis:6379/0" + static_dir: str = "/opt/app/static" -def create_app(options: AppConfig): - redis = create_redis_client(options.redis_uri) +def create_app(config: AppConfig): + redis = create_redis_client(config.redis_uri) channels_plugin = ChannelsPlugin( backend=RedisChannelsPubSubBackend(redis=redis), @@ -30,11 +31,18 @@ def create_app(options: AppConfig): create_ws_route_handlers=False, ) + template_config = create_template_config( + app_resource("templates"), config.static_dir + ) + + static_files_config = StaticFilesConfig( + directories=[config.static_dir], path="/static" + ) + return Litestar( [index, ws], + debug=config.debug, template_config=template_config, - static_files_config=[ - StaticFilesConfig(directories=[app_resource("static")], path="/static"), - ], + static_files_config=[static_files_config], plugins=[channels_plugin], ) diff --git a/example_app/templates.py b/example_app/templates.py index f546e79..56c9b39 100644 --- a/example_app/templates.py +++ b/example_app/templates.py @@ -1,27 +1,32 @@ import json +import structlog + from litestar.contrib.jinja import JinjaTemplateEngine from litestar.template.config import TemplateConfig from pathlib import Path -from example_app.etc import app_resource +log: structlog.BoundLogger = structlog.get_logger() -def webpack_bundle(_, name: str): - manifest_file = app_resource("static/bundles/manifest.json") - webpack_bundles = json.load(open(manifest_file)) - return webpack_bundles[name] +def make_callback(static_dir: str): + def webpack_bundle(_, name: str): + manifest_file = Path(static_dir) / "js/manifest.json" + webpack_bundles = json.load(open(manifest_file)) + return webpack_bundles[name] + def callback(engine: JinjaTemplateEngine) -> None: + engine.register_template_callable( + key="webpack_bundle", + template_callable=webpack_bundle, + ) -def register_template_callables(engine: JinjaTemplateEngine) -> None: - engine.register_template_callable( - key="webpack_bundle", - template_callable=webpack_bundle, - ) + return callback -template_config = TemplateConfig( - directory=Path(__file__).parent / "templates", - engine=JinjaTemplateEngine, - engine_callback=register_template_callables, -) +def create_template_config(template_dir: str, static_dir: str): + return TemplateConfig( + directory=template_dir, + engine=JinjaTemplateEngine, + engine_callback=make_callback(static_dir), + ) diff --git a/tests/test_factory.py b/tests/test_factory.py new file mode 100644 index 0000000..650aae6 --- /dev/null +++ b/tests/test_factory.py @@ -0,0 +1,11 @@ +from litestar.status_codes import HTTP_200_OK +from litestar.testing import TestClient +from example_app.factory import AppConfig, create_app + + +def test_index_page(): + config = AppConfig(debug=True) + app = create_app(config) + with TestClient(app=app) as client: + response = client.get("/") + assert response.status_code == HTTP_200_OK diff --git a/webpack.config.js b/webpack.config.js index 034031c..b82bf4d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,33 +1,33 @@ const path = require("path"); const webpack = require("webpack"); const BundleTracker = require("webpack-bundle-tracker"); -const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); +const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); module.exports = { - context: __dirname, - entry: { - "main": "./static/js/index.js", - }, - output: { - path: path.resolve(__dirname, "example_app/static/bundles/"), - filename: "[name]-[contenthash].js", - }, - module: { - rules: [ - { - test: /\.css$/, - use: ['style-loader', 'css-loader'] - }, - { - test: /\.ttf$/, - type: 'asset/resource' - } - ] - }, - plugins: [ - new BundleTracker({ path: __dirname, filename: "webpack-stats.json" }), - new WebpackManifestPlugin({ - publicPath: "static/bundles" - }), + context: __dirname, + entry: { + main: "./static/js/index.js", + }, + output: { + path: path.resolve(__dirname, "./dist/js"), + filename: "[name]-[contenthash].js", + }, + module: { + rules: [ + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.ttf$/, + type: "asset/resource", + }, ], -}; \ No newline at end of file + }, + plugins: [ + new BundleTracker({ path: __dirname, filename: "webpack-stats.json" }), + new WebpackManifestPlugin({ + publicPath: "static/js", + }), + ], +};