Skip to content

Commit c8eaeb9

Browse files
committed
feat: Add Docker and VS Code Dev Container setup for Ground Control Station
- Introduced a devcontainer configuration to standardize development environments. - Created a Docker Compose setup for backend, frontend, and a simulated robot API. - Added CI workflow for testing and building Docker images. - Implemented a FastAPI backend with health check and robot status endpoints. - Set up Nginx as a static file server for the frontend with API proxying. - Included example environment variables and a .gitignore file for Python projects. - Developed a basic HTML dashboard for monitoring robot status.
0 parents  commit c8eaeb9

14 files changed

Lines changed: 1151 additions & 0 deletions

File tree

.devcontainer/devcontainer.json

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
{
2+
// ============================================================
3+
// WHAT IS A DEV CONTAINER?
4+
// ============================================================
5+
// A Dev Container (devcontainer) lets you define your entire
6+
// development environment as code. VS Code (and GitHub Codespaces)
7+
// reads this file and automatically builds/starts the right
8+
// Docker containers, installs extensions, and configures settings
9+
// — so every team member gets the *exact same* environment.
10+
//
11+
// This project uses the "Docker Compose" variant of dev containers,
12+
// meaning the environment is made up of *multiple* cooperating
13+
// services defined in docker-compose.yml (a multi-container setup).
14+
// ============================================================
15+
// A human-readable label shown in VS Code's title bar / status bar.
16+
"name": "Ground Control Station — Dev Environment",
17+
// ============================================================
18+
// DOCKER COMPOSE FILES
19+
// ============================================================
20+
// Dev containers that use Docker Compose need at least one
21+
// Compose file. Here we list TWO files; Docker Compose merges
22+
// them in order, so the second file can *override or extend*
23+
// values from the first:
24+
//
25+
// 1. ../docker-compose.yml — the production-style base
26+
// Compose file shared with the rest of the project. It defines
27+
// all services (robot-api, backend, frontend) with their images,
28+
// networks, and environment variables.
29+
//
30+
// 2. docker-compose.devcontainer.yml — a dev-only override that
31+
// mounts your local source code *into* the running containers
32+
// (using a bind mount), so edits you make in VS Code are
33+
// immediately visible inside the container without rebuilding.
34+
//
35+
// This "base + override" pattern keeps production config clean
36+
// while adding dev-friendly conveniences only when needed.
37+
"dockerComposeFile": [
38+
"../docker-compose.yml",
39+
"docker-compose.devcontainer.yml"
40+
],
41+
// ============================================================
42+
// PRIMARY SERVICE
43+
// ============================================================
44+
// When you have multiple services in your Compose file, VS Code
45+
// needs to know *which one* to "attach" to — i.e., which container
46+
// will be your interactive development shell, run your terminal
47+
// sessions, and host your workspace files.
48+
//
49+
// Here that is "backend" (the FastAPI service you are writing).
50+
// VS Code opens a remote connection directly inside this container.
51+
"service": "backend",
52+
// ============================================================
53+
// WORKSPACE FOLDER
54+
// ============================================================
55+
// The absolute path *inside the container* that VS Code treats as
56+
// the root of your project. The bind mount in
57+
// docker-compose.devcontainer.yml maps your local repo to this
58+
// path, so the files you see in the Explorer are the same files
59+
// the container runs.
60+
"workspaceFolder": "/workspace",
61+
// ============================================================
62+
// ADDITIONAL SERVICES TO START
63+
// ============================================================
64+
// By default VS Code only starts the primary "service" container.
65+
// "runServices" tells it to also start these extra services when
66+
// the dev container opens, so the full stack is available for
67+
// live testing without manual `docker compose up` commands:
68+
//
69+
// • robot-api — a pre-built simulator that mimics a physical
70+
// robot's HTTP API (running on port 5000).
71+
// Your backend talks to this as if it were a
72+
// real robot.
73+
// • frontend — the Nginx web server serving the student UI
74+
// (running on port 80 inside its container).
75+
"runServices": [
76+
"robot-api",
77+
"frontend"
78+
],
79+
// ============================================================
80+
// PORT FORWARDING
81+
// ============================================================
82+
// Containers run in an isolated Docker network. "forwardPorts"
83+
// creates tunnels so that your *host* machine (and VS Code's
84+
// Simple Browser) can reach ports inside that network.
85+
//
86+
// Format options:
87+
// "service:port" — forward a port from a *named service* container
88+
// number — forward a port from the *primary* service container
89+
//
90+
// • "robot-api:5000" — the simulated robot API (inside robot-api container)
91+
// • 8000 — your FastAPI/uvicorn backend (inside THIS backend devcontainer)
92+
// • "frontend:80" — the Nginx frontend (inside frontend container)
93+
//
94+
// After forwarding, localhost:5000 / localhost:8000 / localhost:80
95+
// on your host all reach the right service.
96+
"forwardPorts": [
97+
"robot-api:5000",
98+
8000,
99+
"frontend:80"
100+
],
101+
// ============================================================
102+
// PORT LABELS & AUTO-FORWARD BEHAVIOUR
103+
// ============================================================
104+
// "portsAttributes" customises how VS Code handles each forwarded
105+
// port. Two behaviours are configured here:
106+
//
107+
// "notify" — show a toast notification with a link when the
108+
// port becomes available (good for APIs you'll
109+
// call manually or via a REST client).
110+
//
111+
// "openBrowser" — automatically open the system browser when
112+
// the port becomes available (ideal for the UI).
113+
"portsAttributes": {
114+
"robot-api:5000": {
115+
"label": "Robot API (provided)",
116+
"onAutoForward": "notify"
117+
},
118+
"8000": {
119+
"label": "Backend API (uvicorn)",
120+
"onAutoForward": "notify"
121+
},
122+
"frontend:80": {
123+
"label": "Frontend",
124+
// Opens the browser automatically once Nginx is ready.
125+
"onAutoForward": "openBrowser"
126+
}
127+
},
128+
// ============================================================
129+
// POST-CREATE COMMAND
130+
// ============================================================
131+
// A shell command (or script) that runs *once*, after the
132+
// container is first created and VS Code has connected.
133+
// It does NOT run on subsequent reopens.
134+
//
135+
// Here it simply prints a reminder of how to start the uvicorn
136+
// development server. The server is intentionally NOT started
137+
// automatically so you can make code changes before starting it.
138+
//
139+
// To start the backend, open a terminal in VS Code and run:
140+
// cd /workspace/backend && uvicorn main:app --host 0.0.0.0 --port 8000 --reload
141+
//
142+
// The "--reload" flag makes uvicorn watch for file changes and
143+
// restart automatically — very useful during development.
144+
"postCreateCommand": "echo 'run \"cd /workspace/backend && uvicorn main:app --host 0.0.0.0 --port 8000 --reload\" to start the backend API'",
145+
// ============================================================
146+
// USER ID SYNCHRONISATION
147+
// ============================================================
148+
// On Linux hosts, files created inside the container are owned by
149+
// the container's user UID. If that UID differs from your host
150+
// user's UID, you get permission errors when editing files.
151+
// Setting this to true makes VS Code automatically remap the
152+
// container's user UID to match your host UID — preventing those
153+
// permission issues transparently.
154+
"updateRemoteUserUID": true,
155+
// ============================================================
156+
// VS CODE CUSTOMISATIONS
157+
// ============================================================
158+
// "customizations.vscode" lets you declare extensions and editor
159+
// settings that are automatically applied *inside* this dev
160+
// container. This keeps project-specific tooling separate from
161+
// your personal VS Code profile on the host.
162+
"customizations": {
163+
"vscode": {
164+
// ----------------------------------------------------------
165+
// EXTENSIONS
166+
// ----------------------------------------------------------
167+
// These extension IDs are installed automatically when the
168+
// dev container starts. Every student gets the same tools
169+
// without having to install anything manually.
170+
//
171+
// ms-python.python — core Python language support
172+
// (run/debug Python files, REPL)
173+
// ms-python.pylance — fast, smart IntelliSense,
174+
// type checking, and auto-imports
175+
// ms-python.black-formatter — code formatter; enforces a
176+
// consistent style automatically
177+
// ms-python.flake8 — linter; highlights style and
178+
// potential bugs as you type
179+
// ms-azuretools.vscode-docker — Docker integration: browse
180+
// containers, images, logs in VS Code
181+
// humao.rest-client — send HTTP requests from .http
182+
// files directly in the editor
183+
// (great for testing your API)
184+
// eamodio.gitlens — enhanced Git history, blame
185+
// annotations, and code authorship
186+
// github.vscode-github-actions — syntax highlighting and
187+
// validation for GitHub Actions
188+
// workflow files (.github/workflows/)
189+
"extensions": [
190+
"ms-python.python",
191+
"ms-python.pylance",
192+
"ms-python.black-formatter",
193+
"ms-python.flake8",
194+
"ms-azuretools.vscode-docker",
195+
"humao.rest-client",
196+
"eamodio.gitlens",
197+
"github.vscode-github-actions"
198+
],
199+
// ----------------------------------------------------------
200+
// EDITOR SETTINGS
201+
// ----------------------------------------------------------
202+
// These settings override (or add to) VS Code's defaults
203+
// *only* inside this dev container, so they don't affect
204+
// your normal editor setup.
205+
"settings": {
206+
// Tell the Python extension exactly which interpreter to use.
207+
// This must match the Python installed in the container image
208+
// (set in the Dockerfile / base image).
209+
"python.defaultInterpreterPath": "/usr/local/bin/python",
210+
// Automatically format every file when you save it,
211+
// using whichever formatter is configured for that language.
212+
"editor.formatOnSave": true,
213+
// For Python files specifically, use Black as the formatter.
214+
// Black is an opinionated, zero-configuration formatter that
215+
// produces consistent code style across the whole team.
216+
"[python]": {
217+
"editor.defaultFormatter": "ms-python.black-formatter"
218+
},
219+
// Enable pytest as the test runner (visible in the Testing
220+
// panel on the VS Code sidebar).
221+
"python.testing.pytestEnabled": true,
222+
// Arguments passed to pytest when running tests:
223+
// "tests/" — look for tests in the tests/ subdirectory
224+
// "-v" — verbose output (show each test name)
225+
// "--tb=short" — show a compact traceback on failures
226+
// (easier to read than the full traceback)
227+
"python.testing.pytestArgs": [
228+
"tests/",
229+
"-v",
230+
"--tb=short"
231+
]
232+
}
233+
}
234+
}
235+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# ==============================================================
2+
# WHAT IS THIS FILE?
3+
# ==============================================================
4+
# Docker Compose supports *multiple* Compose files that are merged
5+
# together. This file is a DEV-ONLY override layered on top of
6+
# docker-compose.yml when VS Code opens the dev container.
7+
#
8+
# Key idea — override, don't duplicate:
9+
# You only need to list the keys you want to CHANGE. Docker Compose
10+
# deep-merges this file with docker-compose.yml, so every key not
11+
# mentioned here keeps its value from the base file.
12+
#
13+
# Why a separate override file instead of editing docker-compose.yml?
14+
# Keeping dev conveniences (bind mounts, relaxed healthchecks) out
15+
# of the production Compose file means your production config stays
16+
# clean, and you can't accidentally ship dev tooling to production.
17+
# ==============================================================
18+
19+
# Dev-container overrides — mounts local source into containers so edits
20+
# are reflected immediately without a rebuild.
21+
services:
22+
23+
# ── Backend (dev overrides) ──────────────────────────────────────────
24+
backend:
25+
build:
26+
# Switches the build target from "production" (set in
27+
# docker-compose.yml) to "development". The development stage of
28+
# the Dockerfile typically installs extra tools (pytest, debuggers)
29+
# and may set ENV variables that enable debug mode.
30+
target: development
31+
32+
# Overrides the default CMD from the Dockerfile/base Compose file.
33+
# "sleep infinity" keeps the container alive indefinitely without
34+
# starting the application. This is intentional:
35+
#
36+
# • VS Code attaches to this container as your dev shell.
37+
# • You start uvicorn manually in a VS Code terminal so you can
38+
# see its output, restart it freely, and attach a debugger.
39+
# • If the app crashed, "sleep infinity" keeps the container up
40+
# so you can still open a terminal and investigate.
41+
command: "sleep infinity"
42+
43+
# The production healthcheck curls /health on uvicorn, but uvicorn
44+
# is not running (we used "sleep infinity" above). A failing health-
45+
# check would block other services. This override replaces the check
46+
# with a trivially-passing command ("true") so the container is
47+
# always considered healthy in the dev environment.
48+
healthcheck:
49+
test: ["CMD", "true"]
50+
interval: 10s
51+
timeout: 5s
52+
retries: 1
53+
54+
# The base Compose file does not publish backend ports to the host
55+
# (traffic flows through the Nginx reverse proxy in production).
56+
# The dev container forwards port 8000 via devcontainer.json instead,
57+
# so we clear the ports list here to avoid conflicts.
58+
ports: []
59+
60+
volumes:
61+
# BIND MOUNT — the most important dev-container concept:
62+
# Maps your local source directory into the container at /workspace.
63+
#
64+
# Format: "host_path:container_path"
65+
# . — the devcontainer folder on your host machine
66+
# (i.e. the repo root, since docker-compose files
67+
# are resolved relative to this file's directory).
68+
# /workspace — the path inside the container (matches
69+
# workspaceFolder in devcontainer.json).
70+
#
71+
# Effect: any file you save in VS Code is IMMEDIATELY visible
72+
# inside the container — no rebuild required. Combined with
73+
# uvicorn's --reload flag, the server restarts automatically
74+
# whenever you save a Python file.
75+
- ..:/workspace
76+
77+
# ── Frontend (dev overrides) ─────────────────────────────────────────
78+
frontend:
79+
volumes:
80+
# Bind-mounts only the static assets directory into Nginx's
81+
# document root. Saving an HTML/CSS/JS file on the host is
82+
# reflected immediately in the browser on the next page refresh —
83+
# no Nginx restart or image rebuild needed.
84+
#
85+
# Nginx's default document root is /usr/share/nginx/html, so
86+
# this is where static files must live inside the container.
87+
- ../frontend/public:/usr/share/nginx/html
88+
89+
# Clear published ports for the same reason as the backend above.
90+
# The dev container forwards frontend:80 via devcontainer.json.
91+
ports: []

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copy to .env and edit before starting
2+
3+
GCS_PORT=8080
4+
LOG_LEVEL=info

0 commit comments

Comments
 (0)