From 64e1d9bd833ba3fe7e6a8b9eff7051d04315e3ba Mon Sep 17 00:00:00 2001 From: chris-mendoza Date: Mon, 14 Jul 2025 20:43:10 -0600 Subject: [PATCH] Devcontainer and dev_server.py for local dev --- .devcontainer/Dockerfile | 53 ++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 23 +++++++++++ .devcontainer/docker-compose.yml | 12 ++++++ .dockerignore | 3 ++ README.md | 62 +++++++++++++++++++++++++++++- dev_server.py | 65 ++++++++++++++++++++++++++++++++ requirements.txt | 6 +++ server.sh | 3 +- 8 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .dockerignore create mode 100644 dev_server.py create mode 100644 requirements.txt diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..690388e --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,53 @@ +FROM python:3.9-slim + +# Install necessary packages +RUN apt-get update && apt-get install -y \ + git \ + curl \ + openssl \ + ca-certificates \ + gnupg \ + lsb-release \ + && rm -rf /var/lib/apt/lists/* + +# Install Chrome for WebSerial support (headless mode) +RUN curl -fsSL https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg \ + && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] https://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list \ + && apt-get update && apt-get install -y google-chrome-stable \ + && rm -rf /var/lib/apt/lists/* + +# Create a non-root user +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ + && apt-get update \ + && apt-get install -y sudo \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +# Python dependencies will be installed from requirements.txt after container starts +# This avoids the COPY issue during build + +# Install Node.js for http-server +RUN apt-get update && apt-get install -y nodejs npm \ + && npm install -g http-server \ + && rm -rf /var/lib/apt/lists/* + +# Set up working directory +WORKDIR /workspace + +# Generate a self-signed certificate for HTTPS +RUN mkdir -p /workspace/certs \ + && openssl req -new -x509 -keyout /workspace/certs/dummy.pem -out /workspace/certs/dummy.pem -days 365 -nodes -subj "/CN=localhost" + +# Switch to non-root user +USER $USERNAME + +# Set environment variables +ENV PYTHONUNBUFFERED=1 + +# Expose ports +EXPOSE 8080 443 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..7bfebf6 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +{ + "name": "XRPCode Development", + "dockerFile": "Dockerfile", + "forwardPorts": [8080, 443], + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.debugpy", + "ms-vscode.js-debug", + "vscode.html-language-features" + ], + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "editor.formatOnSave": true + } + } + }, + "postCreateCommand": "pip install -r requirements.txt || echo 'No requirements.txt found, skipping pip install'", + "remoteUser": "vscode" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..298bb3b --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3' +services: + xrpcode: + build: + context: . + dockerfile: Dockerfile + volumes: + - ..:/workspace:cached + command: /bin/sh -c "while sleep 1000; do :; done" + ports: + - "8080:8080" + - "443:443" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b3cd0e8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +# Ignore requirements.txt during Docker build +# This prevents auto-added COPY commands from failing +requirements.txt diff --git a/README.md b/README.md index 9e8a417..677adc3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ A web code editor for the XRP using Blockly and micropython -Features: +## Features + * Code editor with Python syntax highlighting * Shell for interacting with the XRP MicroPython REPL * Output from running programs shown in shell @@ -13,4 +14,61 @@ Features: * Easy connect and disconnect of XRP * Automatically connects XRP if it has connected to the page before -Only Google Chrome and Microsoft Edge are officially supported by the WebSerial JavaScript API +**Note:** Only Google Chrome and Microsoft Edge are officially supported by the WebSerial JavaScript API + +## Development Container + +This project includes a development container that provides all the necessary dependencies to run and develop the XRPCode project. + +### Prerequisites + +- [Visual Studio Code](https://code.visualstudio.com/) +- [Docker](https://www.docker.com/products/docker-desktop) +- [VS Code Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) + +### Getting Started + +1. Open this project in Visual Studio Code +2. When prompted, click "Reopen in Container" or run the "Remote-Containers: Reopen in Container" command from the command palette +3. VS Code will build the container and set up the development environment + +### Running the Application + +Once inside the development container, you can run the application using the development server: + +```bash +# Run with HTTP on port 8080 (default) +python dev_server.py + +# Run with HTTPS on port 443 +python dev_server.py --https + +# Run with HTTP on custom port +python dev_server.py --port 3000 + +# Run with HTTPS on custom port +python dev_server.py --https --port 8443 +``` + +#### Legacy Methods + +You can also use the original methods if needed: + +```bash +# HTTP Server (Port 8080) +python -m http.server 8080 + +# HTTPS Server (Port 443) +python server.py +``` + +### Accessing the Application + +- Default HTTP Server: http://localhost:8080 +- Default HTTPS Server: https://localhost:443 + +### Notes + +- The container forwards ports 8080 and 443 to your local machine +- Chrome or Edge browser is required for WebSerial API support +- The container includes a self-signed certificate for HTTPS support diff --git a/dev_server.py b/dev_server.py new file mode 100644 index 0000000..bb825c5 --- /dev/null +++ b/dev_server.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +""" +XRPCode Development Server +Run with: python dev_server.py [--https] [--port PORT] + +Options: + --https Run server with HTTPS (default: HTTP) + --port Specify port number (default: 8080 for HTTP, 443 for HTTPS) +""" + +import argparse +import http.server +import ssl +import os +import sys + +class CORSRequestHandler(http.server.SimpleHTTPRequestHandler): + """HTTP request handler with CORS support""" + + def send_response(self, *args, **kwargs): + http.server.SimpleHTTPRequestHandler.send_response(self, *args, **kwargs) + self.send_header('Access-Control-Allow-Origin', '*') + + def end_headers(self): + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization') + return super(CORSRequestHandler, self).end_headers() + +def main(): + # Parse command line arguments + parser = argparse.ArgumentParser(description='XRPCode Development Server') + parser.add_argument('--https', action='store_true', help='Run server with HTTPS') + parser.add_argument('--port', type=int, help='Port number (default: 8080 for HTTP, 443 for HTTPS)') + args = parser.parse_args() + + # Set default port based on protocol + if args.port is None: + args.port = 443 if args.https else 8080 + + # Create server + server_address = ('127.0.0.1', args.port) + httpd = http.server.HTTPServer(server_address, CORSRequestHandler) + + # Configure SSL if HTTPS is requested + if args.https: + cert_path = './dummy.pem' + if not os.path.exists(cert_path): + print(f"Error: Certificate file '{cert_path}' not found.") + print("You can create one with: openssl req -new -x509 -keyout dummy.pem -out dummy.pem -days 365 -nodes") + sys.exit(1) + httpd.socket = ssl.wrap_socket(httpd.socket, certfile=cert_path, server_side=True) + protocol = "HTTPS" + else: + protocol = "HTTP" + + # Start server + print(f"Starting {protocol} server on http{'s' if args.https else ''}://127.0.0.1:{args.port}") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nServer stopped.") + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1661ce1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +# These packages are part of the Python standard library +# No additional packages required for basic functionality + +# Optional packages for development +pyserial>=3.5 +pyopenssl>=23.0.0 diff --git a/server.sh b/server.sh index 427b582..023d262 100755 --- a/server.sh +++ b/server.sh @@ -1,2 +1,3 @@ - python3 -m http.server --cgi 8080 +#!/bin/bash +python3 -m http.server --cgi 8080