From d3f402c4809e104b00ce394620e032b278acd5c1 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Fri, 14 Aug 2026 15:09:45 +0200 Subject: [PATCH 1/2] feat: Add mitmproxy to the devcontainer Lets scripts/proxy_lp.py run inside the container, serving locally built CSS and JS into the live wiki as described on the Local Development Setup wiki page, without every contributor having to set the proxy up by hand. Port 8080 is forwarded so the browser on the host can reach it, and the mitmproxy CA lives in a volume so the certificate only has to be trusted in the browser once rather than after every rebuild. mitmproxy is deliberately kept out of requirements.txt, which covers the deploy scripts rather than optional local tooling, and pinned in the Dockerfile like the rest of the toolchain. --- .devcontainer/Dockerfile | 4 ++++ .devcontainer/devcontainer.json | 17 +++++++++++++++++ .devcontainer/post-create.sh | 4 ++-- README.md | 11 +++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 47879a23824..43fa5de44bd 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,6 +8,9 @@ FROM mcr.microsoft.com/playwright:v1.61.1-noble ARG LUA_VERSION=5.1.5 ARG LUAROCKS_VERSION=3.11.1 ARG LUALS_VERSION=3.18.2 +# Not in requirements.txt on purpose: only needed to run scripts/proxy_lp.py, +# which is a local-development tool rather than a dependency of the repo +ARG MITMPROXY_VERSION=12.2.3 ENV DEBIAN_FRONTEND=noninteractive @@ -62,6 +65,7 @@ ENV VIRTUAL_ENV=/opt/venv ENV PATH=/opt/venv/bin:${PATH} RUN python3 -m venv "${VIRTUAL_ENV}" \ && pip install --no-cache-dir --upgrade pip \ + && pip install --no-cache-dir "mitmproxy==${MITMPROXY_VERSION}" \ && chmod -R a+rwX "${VIRTUAL_ENV}" # Runs before the features do, which is where the user we run as gets created. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7a3fe0400de..c0858b35dd5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -27,8 +27,25 @@ "source": "lua-modules-node-modules", "target": "${containerWorkspaceFolder}/node_modules", "type": "volume" + }, + // Keeps the mitmproxy CA across rebuilds, so the certificate only has + // to be trusted in the browser once + { + "source": "lua-modules-mitmproxy", + "target": "/home/vscode/.mitmproxy", + "type": "volume" } ], + // mitmproxy, for serving locally built CSS/JS into the live wiki + "forwardPorts": [ + 8080 + ], + "portsAttributes": { + "8080": { + "label": "mitmproxy", + "onAutoForward": "silent" + } + }, "containerEnv": { "PLAYWRIGHT_BROWSERS_PATH": "/ms-playwright" }, diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index ec7e6e7a398..0d18e7235eb 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -4,8 +4,8 @@ set -euo pipefail cd "$(dirname "$0")/.." echo "==> Installing npm dependencies" -# node_modules is a named volume, which docker creates owned by root -sudo chown "$(id -u):$(id -g)" node_modules +# these are named volumes, which docker creates owned by root +sudo chown "$(id -u):$(id -g)" node_modules ~/.mitmproxy npm install echo "==> Installing python dependencies" diff --git a/README.md b/README.md index 4eda132e0f8..828b3825a29 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,17 @@ Open the repository in VS Code and pick *Dev Containers: Reopen in Container* (r If you prefer a native setup, follow the platform instructions below instead. +###### Previewing CSS and JS changes on the live wiki + +The container also ships [mitmproxy](https://mitmproxy.org/) and the dependencies for `scripts/proxy_lp.py`, which serves your locally built `lua/output/css/main.css` and `lua/output/js/main.js` in place of the ones liquipedia.net would load. See the [wiki page](https://github.com/Liquipedia/Lua-Modules/wiki/Local-Development-Setup-for-CSS-and-JS) for the full background; inside the container the setup is: + +1. Start the proxy with `python scripts/proxy_lp.py` from the repository root, or via the *Launch proxy* task in `.vscode/tasks.json`. It listens on port 8080, which is forwarded to `127.0.0.1:8080` on your host. +2. Point your browser at that proxy. A switcher extension such as Proxy SwitchyOmega (HTTP, `127.0.0.1`, port `8080`) makes it easy to toggle on and off; a system-wide proxy setting works too. +3. With the proxy enabled, open and install the certificate for your browser, so it will trust the intercepted HTTPS responses. The CA is kept in a volume, so this is only needed once, not after every rebuild. +4. Edit `.scss` or `.js`, run `npm run build`, then hard refresh the wiki page (Ctrl+Shift+R / Cmd+Shift+R). + +Turn the proxy off in your browser when you are done — while it is enabled, every request goes through the container. + ##### Windows Recommended to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). Then follow the Unix instructions. From 5f084a712b9750c5669041dc3ed89850f063cf32 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Wed, 19 Aug 2026 15:01:48 +0200 Subject: [PATCH 2/2] fix: Publish the proxy port and say when there is nothing to serve forwardPorts is an editor side tunnel, so a container started with the CLI never published 8080 and the README's claim that it reaches 127.0.0.1:8080 on the host only held in VS Code. Adds appPort next to it, bound to loopback rather than every interface, since the alternative leaves an open forward proxy reachable from the local network. A fresh container also had no lua/output, so the mapper raised FileNotFoundError, mitmproxy passed the request upstream and the page looked untouched. Nothing was logged: proxy_lp.py builds its own Master from default_addons(), which has no TermLog, so addon errors go nowhere. Builds in post-create.sh and says which file is missing instead of failing mutely, and moves the build to the front of the README steps rather than leaving it at the end of the edit loop. Reported by Eetwalt. --- .devcontainer/devcontainer.json | 9 +++++++- .devcontainer/post-create.sh | 4 ++++ README.md | 9 ++++---- scripts/proxy_lp.py | 41 ++++++++++++++++++--------------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c0858b35dd5..f3c5d6e8558 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -36,10 +36,17 @@ "type": "volume" } ], - // mitmproxy, for serving locally built CSS/JS into the live wiki + // mitmproxy, for serving locally built CSS/JS into the live wiki. + // forwardPorts is the editor side tunnel, appPort is what publishes the port + // for anyone driving the container from the CLI instead. Bound to loopback + // on purpose: publishing on every interface would leave an open forward + // proxy reachable from the local network. "forwardPorts": [ 8080 ], + "appPort": [ + "127.0.0.1:8080:8080" + ], "portsAttributes": { "8080": { "label": "mitmproxy", diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 0d18e7235eb..215635a7c8c 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -11,6 +11,10 @@ npm install echo "==> Installing python dependencies" pip install --no-cache-dir -r requirements.txt +echo "==> Building css and js" +# The proxy serves these, and without them it silently passes requests through +npm run build + if [ ! -f .env ]; then echo "==> Creating .env from .env.example (fill in your bot credentials before deploying)" # Nobody chose these settings, we generated them, so start in dry-run and diff --git a/README.md b/README.md index 828b3825a29..e2356b7c7d4 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,11 @@ If you prefer a native setup, follow the platform instructions below instead. The container also ships [mitmproxy](https://mitmproxy.org/) and the dependencies for `scripts/proxy_lp.py`, which serves your locally built `lua/output/css/main.css` and `lua/output/js/main.js` in place of the ones liquipedia.net would load. See the [wiki page](https://github.com/Liquipedia/Lua-Modules/wiki/Local-Development-Setup-for-CSS-and-JS) for the full background; inside the container the setup is: -1. Start the proxy with `python scripts/proxy_lp.py` from the repository root, or via the *Launch proxy* task in `.vscode/tasks.json`. It listens on port 8080, which is forwarded to `127.0.0.1:8080` on your host. -2. Point your browser at that proxy. A switcher extension such as Proxy SwitchyOmega (HTTP, `127.0.0.1`, port `8080`) makes it easy to toggle on and off; a system-wide proxy setting works too. -3. With the proxy enabled, open and install the certificate for your browser, so it will trust the intercepted HTTPS responses. The CA is kept in a volume, so this is only needed once, not after every rebuild. -4. Edit `.scss` or `.js`, run `npm run build`, then hard refresh the wiki page (Ctrl+Shift+R / Cmd+Shift+R). +1. Run `npm run build`, so there is something to serve. The container does this on first start, but the proxy has nothing to substitute until it has run, and it will tell you so rather than quietly serving you the real files. +2. Start the proxy with `python scripts/proxy_lp.py` from the repository root, or via the *Launch proxy* task in `.vscode/tasks.json`. It listens on port 8080, published to `127.0.0.1:8080` on your host. +3. Point your browser at that proxy. A switcher extension such as Proxy SwitchyOmega (HTTP, `127.0.0.1`, port `8080`) makes it easy to toggle on and off; a system-wide proxy setting works too. +4. With the proxy enabled, open and install the certificate for your browser, so it will trust the intercepted HTTPS responses. The CA is kept in a volume, so this is only needed once, not after every rebuild. +5. Edit `.scss` or `.js`, run `npm run build` again, then hard refresh the wiki page (Ctrl+Shift+R / Cmd+Shift+R). Turn the proxy off in your browser when you are done — while it is enabled, every request goes through the container. diff --git a/scripts/proxy_lp.py b/scripts/proxy_lp.py index 316d67a0ec7..a26da397085 100644 --- a/scripts/proxy_lp.py +++ b/scripts/proxy_lp.py @@ -23,26 +23,31 @@ def request(self, flow: http.HTTPFlow) -> None: self.__serve_local_js_resource(flow) def __serve_local_css_resource(self, flow: http.HTTPFlow): - with open("lua/output/css/main.css", "rb") as f: - flow.response = http.Response.make( - HTTPStatus.OK, - f.read(), - {"Content-Type": "text/css; charset=utf-8"}, - ) - flow.response.headers["Via"] = ( - f"{flow.response.http_version} LiquipediaMapper" - ) + self.__serve_local_file( + flow, "lua/output/css/main.css", "text/css; charset=utf-8" + ) def __serve_local_js_resource(self, flow: http.HTTPFlow): - with open("lua/output/js/main.js", "rb") as f: - flow.response = http.Response.make( - HTTPStatus.OK, - f.read(), - {"Content-Type": "text/javascript; charset=utf-8"}, - ) - flow.response.headers["Via"] = ( - f"{flow.response.http_version} LiquipediaMapper" - ) + self.__serve_local_file( + flow, "lua/output/js/main.js", "text/javascript; charset=utf-8" + ) + + def __serve_local_file(self, flow: http.HTTPFlow, path: str, content_type: str): + try: + with open(path, "rb") as f: + body = f.read() + except OSError as error: + # Leaving this unsaid means the request goes upstream, the page looks + # untouched and nothing explains why + print(f"LiquipediaMapper: {error}. Run npm run build first.") + return + + flow.response = http.Response.make( + HTTPStatus.OK, + body, + {"Content-Type": content_type}, + ) + flow.response.headers["Via"] = f"{flow.response.http_version} LiquipediaMapper" async def main():