From 3020494022caf9020dc5e7a12f620e0b939fe4df Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Sun, 19 Jan 2025 15:25:08 -0800 Subject: [PATCH 1/4] . --- .devcontainer/Dockerfile | 3 +++ .devcontainer/devcontainer.json | 2 +- docker-compose.yaml | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 docker-compose.yaml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a8d9ab6c..7187e482 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,3 +1,6 @@ FROM ghcr.io/league-infrastructure/jtlpython:20240719 ENV VNC_RESOLUTION=600x600x16 +ENV PASSWORD=code4life +RUN curl -fsSL https://code-server.dev/install.sh | sh +CMD ["code-server", "--bind-addr", "0.0.0.0:8080", "/workspace"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1c48802d..b57bb65a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -30,7 +30,7 @@ // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [6080, 5901], + "forwardPorts": [8080, 6080, 5901], "portsAttributes": { "6080":{ diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..b006cb63 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,16 @@ +version: '3.8' + +services: + devcontainer: + build: + context: . + dockerfile: .devcontainer/Dockerfile + container_name: PythonApprentice + ports: + - "8080:8080" # Access code-server + volumes: + - .:/workspace + environment: + - PASSWORD=code4life # Optional: Secure access + command: > + code-server --bind-addr 0.0.0.0:8080 /workspace From 5bf55c5c4fcbcd8cf4ea7224ce0467e6fa5db07a Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Sun, 19 Jan 2025 15:25:17 -0800 Subject: [PATCH 2/4] . --- .devcontainer/Dockerfile | 32 +- .devcontainer/README.md | 18 + .devcontainer/devcontainer.json | 24 +- .devcontainer/install-extensions.sh | 39 +++ .devcontainer/install.sh | 441 ++++++++++++++++++++++++ .devcontainer/scrape-display.sh | 6 + docker-compose.yaml | 28 +- lessons/00_Turtles/01_Get_Started.ipynb | 2 +- 8 files changed, 575 insertions(+), 15 deletions(-) create mode 100644 .devcontainer/README.md create mode 100755 .devcontainer/install-extensions.sh create mode 100644 .devcontainer/install.sh create mode 100755 .devcontainer/scrape-display.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7187e482..9d27c3e2 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,34 @@ -FROM ghcr.io/league-infrastructure/jtlpython:20240719 +#FROM ghcr.io/league-infrastructure/jtlpython:20240719 + +FROM mcr.microsoft.com/devcontainers/python:3.12-bookworm ENV VNC_RESOLUTION=600x600x16 ENV PASSWORD=code4life +COPY .devcontainer/install.sh /tmp/install-vnc.sh +# installs /usr/local/share/desktop-init.sh +RUN chmod 775 /tmp/install-vnc.sh +RUN /tmp/install-vnc.sh + +RUN apt-get update && apt-get install -y --no-install-recommends \ + x11-apps \ + git \ + imagemagick && \ + rm -rf /var/lib/apt/lists/* + +COPY requirements.txt /tmp/pip-tmp/ +RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ + && rm -rf /tmp/pip-tmp + +EXPOSE 6080 +EXPOSE 5901 +EXPOSE 8080 + +#RUN mkdir /workspace +#RUN git clone https://github.com/league-curriculum/Python-Apprentice /workspace + + RUN curl -fsSL https://code-server.dev/install.sh | sh -CMD ["code-server", "--bind-addr", "0.0.0.0:8080", "/workspace"] + +USER vscode + +CMD ["code-server", "--disable-workspace-trust", "--bind-addr", "0.0.0.0:8080", "/workspace"] diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 00000000..2279f67f --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,18 @@ + + + +docker run \ + --sig-proxy=false \ + -a STDOUT \ + -a STDERR \ + --mount type=bind,source=/Users/eric/proj/league-projects/curriculum/Python-Apprentice,target=/workspaces/Python-Apprentice,consistency=cached \ + -l devcontainer.local_folder=/Users/eric/proj/league-projects/curriculum/Python-Apprentice \ + -l devcontainer.config_file=/Users/eric/proj/league-projects/curriculum/Python-Apprentice/.devcontainer/devcontainer.json \ + -e SDL_VIDEO_WINDOW_POS=0,0 \ + -e SDL_AUDIODRIVER=dummy \ + -p 8080:8080 \ + -p 6080:6080 \ + --init \ + --entrypoint /bin/sh \ + vsc-python-apprentice-c62618d2fa57d89881a493d3711298df25a53492283fc9697df93e45ef9b6a14 \ + -c "echo Container started" \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b57bb65a..df7897d2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -22,25 +22,28 @@ // Features to add to the dev container. More info: https://containers.dev/features. "features": { - "ghcr.io/devcontainers/features/desktop-lite:1": { - "password" : "code4life" - } + //"ghcr.io/devcontainers/features/desktop-lite:1": { + // "password" : "code4life" + //} }, // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [8080, 6080, 5901], + "forwardPorts": [8080, 6080], "portsAttributes": { "6080":{ "label": "VNC Web Screen" }, - "5901":{ - "label":"VNC App" - } }, + "runArgs": [ + "-p", "8080:8080", // Expose the code-server port + "-p", "6080:6080" + ], + "overrideCommand": false , // Ensure CMD in Dockerfile is used + // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "./.devcontainer/setup.sh", @@ -55,12 +58,17 @@ "vscode": { "extensions": [ "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.autopep8", "ms-python.debugpy", "ms-python.isort", "ms-toolsai.jupyter" ], "settings": { - + "python.defaultInterpreterPath": "/usr/local/python/current/bin/python", + "[python]": { + "editor.defaultFormatter": "ms-python.autopep8" + } } } } diff --git a/.devcontainer/install-extensions.sh b/.devcontainer/install-extensions.sh new file mode 100755 index 00000000..153a288a --- /dev/null +++ b/.devcontainer/install-extensions.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# List of extensions to install +extensions=( + "ms-python.python" + "ms-python.vscode-pylance" + "ms-python.autopep8" + "ms-python.debugpy" + "ms-python.isort" + "ms-toolsai.jupyter" +) + +# Determine the command to use +if command -v code-server &> /dev/null; then + CODE="code-server" +elif command -v code &> /dev/null; then + CODE="code" +else + echo "Error: Neither 'code' (VS Code CLI) nor 'code-server' is installed." + exit 1 +fi +# Function to install extensions +install_extensions() { + for extension in "${extensions[@]}"; do + echo "Installing VS Code extension: $extension" + $CODE --install-extension "$extension" + done +} + +# Check if VS Code is installed +if ! command -v code &> /dev/null; then + echo "Error: VS Code (code) command is not available. Please install Visual Studio Code first." + exit 1 +fi + +# Run the installation function +install_extensions + +echo "All extensions have been installed successfully!" diff --git a/.devcontainer/install.sh b/.devcontainer/install.sh new file mode 100644 index 00000000..6e6fff26 --- /dev/null +++ b/.devcontainer/install.sh @@ -0,0 +1,441 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/desktop-lite.md +# Maintainer: The VS Code and Codespaces Teams + +NOVNC_VERSION="${NOVNCVERSION:-"1.2.0"}" # TODO: Add in a 'latest' auto-detect and swap name to 'version' +VNC_PASSWORD=${PASSWORD:-"vscode"} +if [ "$VNC_PASSWORD" = "noPassword" ]; then + unset VNC_PASSWORD +fi +NOVNC_PORT="${WEBPORT:-6080}" +VNC_PORT="${VNCPORT:-5901}" + +INSTALL_NOVNC="${INSTALL_NOVNC:-"true"}" +USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" + +WEBSOCKETIFY_VERSION=0.10.0 + +package_list=" + tigervnc-standalone-server \ + tigervnc-common \ + fluxbox \ + dbus-x11 \ + x11-utils \ + x11-xserver-utils \ + xdg-utils \ + fbautostart \ + at-spi2-core \ + xterm \ + eterm \ + nautilus\ + mousepad \ + seahorse \ + gnome-icon-theme \ + gnome-keyring \ + libx11-dev \ + libxkbfile-dev \ + libsecret-1-dev \ + libgbm-dev \ + libnotify4 \ + libnss3 \ + libxss1 \ + xfonts-base \ + xfonts-terminus \ + fonts-noto \ + fonts-wqy-microhei \ + fonts-droid-fallback \ + htop \ + ncdu \ + curl \ + ca-certificates\ + unzip \ + nano \ + locales" + +# Packages to attempt to install if essential tools are missing (ie: vncpasswd). +# This is useful, at least, for Ubuntu 22.04 (jammy) +package_list_additional=" + tigervnc-tools" + +set -e + +# Clean up +rm -rf /var/lib/apt/lists/* + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi +# Add default Fluxbox config files if none are already present +fluxbox_apps="$(cat \ +<< 'EOF' +[transient] (role=GtkFileChooserDialog) + [Dimensions] {70% 70%} + [Position] (CENTER) {0 0} +[end] +EOF +)" + +fluxbox_init="$(cat \ +<< 'EOF' +session.configVersion: 13 +session.menuFile: ~/.fluxbox/menu +session.keyFile: ~/.fluxbox/keys +session.styleFile: /usr/share/fluxbox/styles/qnx-photon +session.screen0.workspaces: 1 +session.screen0.workspacewarping: false +session.screen0.toolbar.widthPercent: 100 +session.screen0.strftimeFormat: %a %l:%M %p +session.screen0.toolbar.tools: RootMenu, clock, iconbar, systemtray +session.screen0.workspaceNames: One, +EOF +)" + +fluxbox_menu="$(cat \ +<< 'EOF' +[begin] ( Application Menu ) + [exec] (File Manager) { nautilus ~ } <> + [exec] (Text Editor) { mousepad } <> + [exec] (Terminal) { tilix -w ~ -e $(readlink -f /proc/$$/exe) -il } <> + [exec] (Web Browser) { x-www-browser --disable-dev-shm-usage } <> + [submenu] (System) {} + [exec] (Set Resolution) { tilix -t "Set Resolution" -e bash /usr/local/bin/set-resolution } <> + [exec] (Edit Application Menu) { mousepad ~/.fluxbox/menu } <> + [exec] (Passwords and Keys) { seahorse } <> + [exec] (Top Processes) { tilix -t "Top" -e htop } <> + [exec] (Disk Utilization) { tilix -t "Disk Utilization" -e ncdu / } <> + [exec] (Editres) {editres} <> + [exec] (Xfontsel) {xfontsel} <> + [exec] (Xkill) {xkill} <> + [exec] (Xrefresh) {xrefresh} <> + [end] + [config] (Configuration) + [workspaces] (Workspaces) +[end] +EOF +)" + +# Copy config files if the don't already exist +copy_fluxbox_config() { + local target_dir="$1" + mkdir -p "${target_dir}/.fluxbox" + touch "${target_dir}/.Xmodmap" + if [ ! -e "${target_dir}/.fluxbox/apps" ]; then + echo "${fluxbox_apps}" > "${target_dir}/.fluxbox/apps" + fi + if [ ! -e "${target_dir}/.fluxbox/init" ]; then + echo "${fluxbox_init}" > "${target_dir}/.fluxbox/init" + fi + if [ ! -e "${target_dir}/.fluxbox/menu" ]; then + echo "${fluxbox_menu}" > "${target_dir}/.fluxbox/menu" + fi +} + +apt_get_update() +{ + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update + apt-get -y install --no-install-recommends "$@" + fi +} + +########################## +# Install starts here # +########################## + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +apt_get_update + +# On older Ubuntu, Tilix is in a PPA. on Debian stretch its in backports. +if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then + . /etc/os-release + if [ "${ID}" = "ubuntu" ]; then + check_packages apt-transport-https software-properties-common + add-apt-repository -y ppa:webupd8team/terminix + elif [ "${VERSION_CODENAME}" = "stretch" ]; then + echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list + fi + apt-get update + if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then + echo "(!) WARNING: Tilix not available on ${ID} ${VERSION_CODENAME} architecture $(uname -m). Skipping." + else + package_list="${package_list} tilix" + fi +else + package_list="${package_list} tilix" +fi + +# Install X11, fluxbox and VS Code dependencies +check_packages ${package_list} + +# if Ubuntu-24.04, noble(numbat) found, then will install libasound2-dev instead of libasound2. +# this change is temporary, https://packages.ubuntu.com/noble/libasound2 will switch to libasound2 once it is available for Ubuntu-24.04, noble(numbat) +. /etc/os-release +if [ "${ID}" = "ubuntu" ] && [ "${VERSION_CODENAME}" = "noble" ]; then + echo "Ubuntu 24.04, Noble(Numbat) detected. Installing libasound2-dev package..." + check_packages "libasound2-dev" +else + check_packages "libasound2" +fi + +# On newer versions of Ubuntu (22.04), +# we need an additional package that isn't provided in earlier versions +if ! type vncpasswd > /dev/null 2>&1; then + check_packages ${package_list_additional} +fi + +# Install Emoji font if available in distro - Available in Debian 10+, Ubuntu 18.04+ +if dpkg-query -W fonts-noto-color-emoji > /dev/null 2>&1 && ! dpkg -s fonts-noto-color-emoji > /dev/null 2>&1; then + apt-get -y install --no-install-recommends fonts-noto-color-emoji +fi + +# Check at least one locale exists +if ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen + locale-gen +fi + +# Install the Cascadia Code fonts - https://github.com/microsoft/cascadia-code +if [ ! -d "/usr/share/fonts/truetype/cascadia" ]; then + curl -sSL https://github.com/microsoft/cascadia-code/releases/download/v2008.25/CascadiaCode-2008.25.zip -o /tmp/cascadia-fonts.zip + unzip /tmp/cascadia-fonts.zip -d /tmp/cascadia-fonts + mkdir -p /usr/share/fonts/truetype/cascadia + mv /tmp/cascadia-fonts/ttf/* /usr/share/fonts/truetype/cascadia/ + rm -rf /tmp/cascadia-fonts.zip /tmp/cascadia-fonts +fi + +# Install noVNC +if [ "${INSTALL_NOVNC}" = "true" ] && [ ! -d "/usr/local/novnc" ]; then + mkdir -p /usr/local/novnc + curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip + unzip /tmp/novnc-install.zip -d /usr/local/novnc + cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html + curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip + unzip /tmp/websockify-install.zip -d /usr/local/novnc + ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify + rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip + + # Install noVNC dependencies and use them. + check_packages python3-minimal python3-numpy + sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run +fi + +# Set up folders for scripts and init files +mkdir -p /var/run/dbus /usr/local/etc/vscode-dev-containers/ + +# Script to change resolution of desktop +cat << EOF > /usr/local/bin/set-resolution +#!/bin/bash +RESOLUTION=\${1:-\${VNC_RESOLUTION:-1920x1080}} +DPI=\${2:-\${VNC_DPI:-96}} +IGNORE_ERROR=\${3:-"false"} +if [ -z "\$1" ]; then + echo -e "**Current Settings **\n" + xrandr + echo -n -e "\nEnter new resolution (WIDTHxHEIGHT, blank for \${RESOLUTION}, Ctrl+C to abort).\n> " + read NEW_RES + if [ "\${NEW_RES}" != "" ]; then + RESOLUTION=\${NEW_RES} + fi + if ! echo "\${RESOLUTION}" | grep -E '[0-9]+x[0-9]+' > /dev/null; then + echo -e "\nInvalid resolution format!\n" + exit 1 + fi + if [ -z "\$2" ]; then + echo -n -e "\nEnter new DPI (blank for \${DPI}, Ctrl+C to abort).\n> " + read NEW_DPI + if [ "\${NEW_DPI}" != "" ]; then + DPI=\${NEW_DPI} + fi + fi +fi + +xrandr --fb \${RESOLUTION} --dpi \${DPI} > /dev/null 2>&1 + +if [ \$? -ne 0 ] && [ "\${IGNORE_ERROR}" != "true" ]; then + echo -e "\nFAILED TO SET RESOLUTION!\n" + exit 1 +fi + +echo -e "\nSuccess!\n" +EOF + +# Container ENTRYPOINT script +cat << EOF > /usr/local/share/desktop-init.sh +#!/bin/bash + +user_name="${USERNAME}" +group_name="$(id -gn ${USERNAME})" +LOG=/tmp/container-init.log + +export DBUS_SESSION_BUS_ADDRESS="\${DBUS_SESSION_BUS_ADDRESS:-"autolaunch:"}" +export DISPLAY="\${DISPLAY:-:1}" +export VNC_RESOLUTION="\${VNC_RESOLUTION:-1440x768x16}" +export LANG="\${LANG:-"en_US.UTF-8"}" +export LANGUAGE="\${LANGUAGE:-"en_US.UTF-8"}" + +# Execute the command it not already running +startInBackgroundIfNotRunning() +{ + log "Starting \$1." + echo -e "\n** \$(date) **" | sudoIf tee -a /tmp/\$1.log > /dev/null + if ! pgrep -x \$1 > /dev/null; then + keepRunningInBackground "\$@" + while ! pgrep -x \$1 > /dev/null; do + sleep 1 + done + log "\$1 started." + else + echo "\$1 is already running." | sudoIf tee -a /tmp/\$1.log > /dev/null + log "\$1 is already running." + fi +} + +# Keep command running in background +keepRunningInBackground() +{ + (\$2 bash -c "while :; do echo [\\\$(date)] Process started.; \$3; echo [\\\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/\$1.log > /dev/null & echo "\$!" | sudoIf tee /tmp/\$1.pid > /dev/null) +} + +# Use sudo to run as root when required +sudoIf() +{ + if [ "\$(id -u)" -ne 0 ]; then + sudo "\$@" + else + "\$@" + fi +} + +# Use sudo to run as non-root user if not already running +sudoUserIf() +{ + if [ "\$(id -u)" -eq 0 ] && [ "\${user_name}" != "root" ]; then + sudo -u \${user_name} "\$@" + else + "\$@" + fi +} + +# Log messages +log() +{ + echo -e "[\$(date)] \$@" | sudoIf tee -a \$LOG > /dev/null +} + +log "** SCRIPT START **" + +# Start dbus. +log 'Running "/etc/init.d/dbus start".' +if [ -f "/var/run/dbus/pid" ] && ! pgrep -x dbus-daemon > /dev/null; then + sudoIf rm -f /var/run/dbus/pid +fi +sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null +while ! pgrep -x dbus-daemon > /dev/null; do + sleep 1 +done + +# Startup tigervnc server and fluxbox +sudoIf rm -rf /tmp/.X11-unix /tmp/.X*-lock +mkdir -p /tmp/.X11-unix +sudoIf chmod 1777 /tmp/.X11-unix +sudoIf chown root:\${group_name} /tmp/.X11-unix +if [ "\$(echo "\${VNC_RESOLUTION}" | tr -cd 'x' | wc -c)" = "1" ]; then VNC_RESOLUTION=\${VNC_RESOLUTION}x16; fi +screen_geometry="\${VNC_RESOLUTION%*x*}" +screen_depth="\${VNC_RESOLUTION##*x}" + +# Check if VNC_PASSWORD is set and use the appropriate command +common_options="tigervncserver \${DISPLAY} -geometry \${screen_geometry} -depth \${screen_depth} -rfbport ${VNC_PORT} -dpi \${VNC_DPI:-96} -localhost -desktop fluxbox -fg" + +if [ -n "\${VNC_PASSWORD+x}" ]; then + startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "\${common_options} -passwd /usr/local/etc/vscode-dev-containers/vnc-passwd" +else + startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "\${common_options} -SecurityTypes None" +fi + +# Spin up noVNC if installed and not running. +if [ -d "/usr/local/novnc" ] && [ "\$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then + keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT} --vnc localhost:${VNC_PORT}" + log "noVNC started." +else + log "noVNC is already running or not installed." +fi + +# Run whatever was passed in +if [ -n "$1" ]; then + log "Executing \"\$@\"." + exec "$@" +else + log "No command provided to execute." +fi +log "** SCRIPT EXIT **" +EOF + +if [ -n "${VNC_PASSWORD+x}" ]; then + echo "${VNC_PASSWORD}" | vncpasswd -f > /usr/local/etc/vscode-dev-containers/vnc-passwd +fi +chmod +x /usr/local/share/desktop-init.sh /usr/local/bin/set-resolution + +# Set up fluxbox config +copy_fluxbox_config "/root" +if [ "${USERNAME}" != "root" ]; then + copy_fluxbox_config "/home/${USERNAME}" + chown -R ${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox +fi + +# Clean up +rm -rf /var/lib/apt/lists/* + +# Determine the message based on whether VNC_PASSWORD is set +if [ -n "${VNC_PASSWORD+x}" ]; then + PASSWORD_MESSAGE="In both cases, use the password \"${VNC_PASSWORD}\" when connecting" +else + PASSWORD_MESSAGE="In both cases, no password is required." +fi + +# Display the message +cat << EOF + + +You now have a working desktop! Connect to in one of the following ways: + +- Forward port ${NOVNC_PORT} and use a web browser to start the noVNC client (recommended) +- Forward port ${VNC_PORT} using VS Code client and connect using a VNC Viewer + +${PASSWORD_MESSAGE} + +(*) Done! + +EOF diff --git a/.devcontainer/scrape-display.sh b/.devcontainer/scrape-display.sh new file mode 100755 index 00000000..e086e425 --- /dev/null +++ b/.devcontainer/scrape-display.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +while true; do + xwd -root -silent -display :1 | convert xwd:- /workspace/frame.png + sleep 1 +done diff --git a/docker-compose.yaml b/docker-compose.yaml index b006cb63..5770b719 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,5 +1,3 @@ -version: '3.8' - services: devcontainer: build: @@ -7,10 +5,32 @@ services: dockerfile: .devcontainer/Dockerfile container_name: PythonApprentice ports: - - "8080:8080" # Access code-server + - 6080 + - 8082:8082 volumes: - .:/workspace + environment: - PASSWORD=code4life # Optional: Secure access + - VSCODE_PROXY_URI=./proxy/{{port}} + - DISPLAY=novnc:0.0 command: > - code-server --bind-addr 0.0.0.0:8080 /workspace + code-server --disable-workspace-trust --bind-addr 0.0.0.0:8082 /workspace + + networks: + - x11 + + novnc: + image: ghcr.io/league-infrastructure/docker-novnc/docker-novnc:latest + environment: + # Adjust to your screen size + - DISPLAY_WIDTH=600 + - DISPLAY_HEIGHT=600 + - RUN_XTERM=no + ports: + - "8080:8080" + networks: + - x11 + +networks: + x11: diff --git a/lessons/00_Turtles/01_Get_Started.ipynb b/lessons/00_Turtles/01_Get_Started.ipynb index f95fa153..4e5ebe02 100644 --- a/lessons/00_Turtles/01_Get_Started.ipynb +++ b/lessons/00_Turtles/01_Get_Started.ipynb @@ -9,7 +9,7 @@ "This is the first formal lesson of your first Python class with the Leage of\n", "Amazing Programmers. To follow these lessons, you should be reading this file in\n", "Visual Studio Code if you are on your own computer, or if you are using a\n", - "website, it should be Github Codespaces." + "website, it should be Github Codespaces.\n" ] }, { From 60b8b92daa8634f467bdbdc2a2a5284933ae59d8 Mon Sep 17 00:00:00 2001 From: League Student Date: Tue, 21 Jan 2025 06:26:02 +0000 Subject: [PATCH 3/4] Fixed Typo. --- lessons/00_Turtles/02_Meet_Tina.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lessons/00_Turtles/02_Meet_Tina.py b/lessons/00_Turtles/02_Meet_Tina.py index 273c17ed..0b2b8f05 100644 --- a/lessons/00_Turtles/02_Meet_Tina.py +++ b/lessons/00_Turtles/02_Meet_Tina.py @@ -23,7 +23,7 @@ tina.left(90) # Turn tina left by the left turn tina.pencolor('red') # Set the pen color to red -tina.forward(150) # Continuie the last two steps three more times +tina.forward(150) # Continue the last two steps three more times tina.left(90) # to draw a square tina.pencolor('green') # Set the pen color to green From 4d33c2d475ecf44ee7476186b4b1762fcf7af42a Mon Sep 17 00:00:00 2001 From: Eric Busboom Date: Wed, 5 Feb 2025 15:09:09 -0800 Subject: [PATCH 4/4] Refactor VS Code extension installation and add cleanup script --- .devcontainer/install-extensions.sh | 43 +++++------------------------ .devcontainer/jtl-setup.sh | 11 ++++++++ 2 files changed, 18 insertions(+), 36 deletions(-) create mode 100755 .devcontainer/jtl-setup.sh diff --git a/.devcontainer/install-extensions.sh b/.devcontainer/install-extensions.sh index 153a288a..321c5491 100755 --- a/.devcontainer/install-extensions.sh +++ b/.devcontainer/install-extensions.sh @@ -1,39 +1,10 @@ #!/bin/bash -# List of extensions to install -extensions=( - "ms-python.python" - "ms-python.vscode-pylance" - "ms-python.autopep8" - "ms-python.debugpy" - "ms-python.isort" - "ms-toolsai.jupyter" -) -# Determine the command to use -if command -v code-server &> /dev/null; then - CODE="code-server" -elif command -v code &> /dev/null; then - CODE="code" -else - echo "Error: Neither 'code' (VS Code CLI) nor 'code-server' is installed." - exit 1 -fi -# Function to install extensions -install_extensions() { - for extension in "${extensions[@]}"; do - echo "Installing VS Code extension: $extension" - $CODE --install-extension "$extension" - done -} - -# Check if VS Code is installed -if ! command -v code &> /dev/null; then - echo "Error: VS Code (code) command is not available. Please install Visual Studio Code first." - exit 1 -fi - -# Run the installation function -install_extensions - -echo "All extensions have been installed successfully!" +code --extensions-dir /app/extensions \ +--install-extension "ms-python.python" \ +--install-extension "ms-python.vscode-pylance" \ +--install-extension "ms-python.autopep8" \ +--install-extension "ms-python.debugpy" \ +--install-extension "ms-python.isort" \ +--install-extension "ms-toolsai.jupyter" \ No newline at end of file diff --git a/.devcontainer/jtl-setup.sh b/.devcontainer/jtl-setup.sh new file mode 100755 index 00000000..85b13b7d --- /dev/null +++ b/.devcontainer/jtl-setup.sh @@ -0,0 +1,11 @@ + +# Clean out distracting files we no longer need. +TARGET_DIR=$1 + +#cd "$TARGET_DIR" || exit 1 + +rm -rf .devcontainer .github .lib requirements.txt LICENSE +mv lessons/* . +rm -rf lessons +git add -A +git commit -m "codeserver init"