From 8e99ca9c0ce387c20260733467666494aa67fce3 Mon Sep 17 00:00:00 2001 From: Jeff Bailey Date: Sat, 13 Sep 2025 21:59:46 +0000 Subject: [PATCH] Add tooling for GitHub Codespaces With this, a new user can fork Rustlings and start a Github Codespace. The Container will already be correctly set up with everything needed, and VSCode tasks are set up for starting rustlings. --- .devcontainer/Dockerfile | 41 ++++++++++++++ .devcontainer/devcontainer.json | 58 ++++++++++++++++++++ .devcontainer/validate.sh | 75 ++++++++++++++++++++++++++ .vscode/tasks.json | 95 +++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/validate.sh create mode 100644 .vscode/tasks.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..ac1e903925 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,41 @@ +# Use the official Rust image as base +FROM mcr.microsoft.com/devcontainers/rust:1-1-bullseye + +# Set the minimum required Rust version for Rustlings +ENV RUST_VERSION=1.88.0 +ENV RUSTUP_HOME=/usr/local/rustup +ENV PATH=/usr/local/rustup/bin:$PATH + +# Install the specific Rust version and required components +RUN rustup install ${RUST_VERSION} \ + && rustup default ${RUST_VERSION} \ + && rustup component add clippy rustfmt rust-src \ + && rustup --version \ + && rustc --version \ + && cargo --version \ + && cargo clippy --version + +# Install additional useful tools for Rust development +RUN apt-get update && apt-get install -y \ + # Git for version control (should already be installed but ensuring it's present) + git \ + # Build essentials for any native dependencies + build-essential \ + # Tools for debugging and profiling + gdb \ + valgrind \ + # Text processing tools that might be useful for Rustlings + jq \ + tree \ + # Clean up apt cache to reduce image size + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Set up the workspace directory +WORKDIR /workspaces/rustlings + +# Verify the installation works correctly +RUN rustc --version \ + && cargo --version \ + && clippy-driver --version \ + && rustfmt --version diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..0f9cf35b85 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,58 @@ +{ + "name": "Rustlings Development Environment", + "build": { + "dockerfile": "Dockerfile", + "context": "." + }, + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + "ghcr.io/devcontainers/features/git:1": { + "ppa": true, + "version": "latest" + } + }, + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": { + "setup-tools": "rustc --version && cargo --version && rustup component add clippy rustfmt", + "validate": ".devcontainer/validate.sh", + "setup-welcome": "echo 'echo \"🦀 Welcome to the Rustlings development environment!\"' >> ~/.bashrc && echo 'echo \"Rust version: $(rustc --version)\"' >> ~/.bashrc && echo 'echo \"Ready to start learning Rust with Rustlings!\"' >> ~/.bashrc" + }, + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "rust-lang.rust-analyzer" + ], + "settings": { + "rust-analyzer.cargo.buildScripts.enable": true, + "rust-analyzer.procMacro.enable": true, + "editor.formatOnSave": true, + "editor.tabSize": 4, + "editor.insertSpaces": true, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "terminal.integrated.defaultProfile.linux": "bash", + "[rust]": { + "editor.defaultFormatter": null + } + } + } + }, + // Set container environment variables + "containerEnv": { + "RUSTLINGS_DEV_MODE": "1" + }, + // Port forwarding for any potential web servers or debugging + "forwardPorts": [ + 8080, + 3000 + ], + // Lifecycle scripts + "onCreateCommand": { + "install-tools": "rustup component add clippy rustfmt && rustup update" + }, + // Mount the workspace to preserve file permissions and git configuration + "mounts": [ + "source=${localWorkspaceFolder}/.git,target=${containerWorkspaceFolder}/.git,type=bind,consistency=cached" + ] +} diff --git a/.devcontainer/validate.sh b/.devcontainer/validate.sh new file mode 100755 index 0000000000..dd53a41710 --- /dev/null +++ b/.devcontainer/validate.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Devcontainer validation script for Rustlings +# This script tests that all required tools are available and working + +set -e + +echo "🦀 Validating Rustlings Devcontainer Setup..." +echo "==============================================" + +# Check Rust toolchain +echo "✓ Checking Rust toolchain..." +rustc --version +cargo --version + +# Check minimum Rust version (1.88+) +RUST_VERSION=$(rustc --version | sed 's/rustc \([0-9]\+\.[0-9]\+\).*/\1/') +REQUIRED_VERSION="1.88" + +if [ "$(printf '%s\n' "$REQUIRED_VERSION" "$RUST_VERSION" | sort -V | head -n1)" = "$REQUIRED_VERSION" ]; then + echo " ✓ Rust version $RUST_VERSION meets requirement (≥ $REQUIRED_VERSION)" +else + echo " ✗ Rust version $RUST_VERSION does not meet requirement (≥ $REQUIRED_VERSION)" + exit 1 +fi + +# Check required components +echo "✓ Checking required Rust components..." +rustup component list --installed | grep -q "clippy" && echo " ✓ Clippy is installed" +rustup component list --installed | grep -q "rustfmt" && echo " ✓ rustfmt is installed" + +# Test clippy works +echo "✓ Testing Clippy..." +clippy-driver --version > /dev/null && echo " ✓ Clippy is functional" + +# Test rustfmt works +echo "✓ Testing rustfmt..." +rustfmt --version > /dev/null && echo " ✓ rustfmt is functional" + +# Check Git +echo "✓ Checking Git..." +git --version > /dev/null && echo " ✓ Git is installed" + +# Test basic Rust compilation +echo "✓ Testing Rust compilation..." +cd /tmp +cargo init --name test_project > /dev/null 2>&1 +cd test_project +cargo check > /dev/null 2>&1 && echo " ✓ Rust compilation works" +cd .. && rm -rf test_project + +# Check if we're in the Rustlings workspace +echo "✓ Checking Rustlings workspace..." +if [ -f "/workspaces/rustlings/Cargo.toml" ]; then + echo " ✓ Rustlings workspace is mounted correctly" + cd /workspaces/rustlings + + # Test Rustlings build + echo "✓ Testing Rustlings build..." + cargo check > /dev/null 2>&1 && echo " ✓ Rustlings compiles successfully" + + # Test Rustlings clippy + echo "✓ Testing Rustlings clippy..." + cargo clippy -- --deny warnings > /dev/null 2>&1 && echo " ✓ Rustlings passes clippy checks" +else + echo " ⚠ Rustlings workspace not found at expected path" +fi + +echo "" +echo "🎉 Devcontainer validation completed successfully!" +echo "Ready to start learning Rust with Rustlings!" +echo "" +echo "Try running:" +echo " cargo run -- init # Initialize exercises" +echo " cargo run -- watch # Start watching for changes" \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..6f641e3dcf --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,95 @@ +{ + "version": "2.0.0", + "tasks": [ + + { + "label": "Start Rustlings", + "type": "shell", + "command": "cargo", + "args": [ + "run" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new", + "clear": true + }, + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Rustlings: Run Exercise", + "type": "shell", + "command": "cargo", + "args": [ + "run", + "--", + "run", + "${input:exerciseName}" + ], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Rustlings: Get Hint", + "type": "shell", + "command": "cargo", + "args": [ + "run", + "--", + "hint", + "${input:exerciseName}" + ], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "Rustlings: Reset Exercise", + "type": "shell", + "command": "cargo", + "args": [ + "run", + "--", + "reset", + "${input:exerciseName}" + ], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "exerciseName", + "description": "Exercise name (e.g., variables1, functions2)", + "default": "", + "type": "promptString" + } + ] +}