GitHub Copilot Exercise Environment — a fully working weather service used as the substrate for a workshop teaching participants to build agentic workflows with GitHub Copilot.
See EXERCISES.md to get started with the workshop exercises.
This is not a production application. The application code is complete and all tests pass. Participants extend the project by building Copilot tooling around it — custom instructions, agents, skills, hooks, and MCP integrations — without touching application code.
- Fetches real-time weather from the OpenWeatherMap 2.5 API (free plan)
- Manages a collection of saved locations (in-memory, no database)
- Serves a static HTML/JS dashboard with current weather, 5-day forecast charts, and custom threshold-based alerts
- Provides a clean REST API with full CRUD for locations and weather queries
- Provides interactive API docs (Swagger/OpenAPI) at
/docs
| Concern | Technology |
|---|---|
| Language | Java 21 |
| Framework | Spring Boot 3.x (MVC) |
| Build | Maven |
| HTTP client | Spring RestClient |
| Validation | Jakarta Bean Validation |
| OpenAPI UI | springdoc-openapi v2 |
| Testing | JUnit 5 + Mockito + MockMvc |
| E2E | Playwright for Java |
| Formatting | Spotless + google-java-format |
| Frontend | Vanilla JS + CSS + HTML (no build step) |
| Tool | Version | Download |
|---|---|---|
| JDK | 21+ | Adoptium Temurin 21 |
| Maven | 3.9+ | maven.apache.org |
Verify your setup:
java -version # should show 21.x
mvn -version # should show 3.9.xSign up at openweathermap.org/appid and generate a free API key.
Note: Free API keys can take up to 2 hours to activate. If the app returns
401 Unauthorizederrors immediately after setup, wait and retry.
mvn install -DskipTestsmacOS / Linux:
cp .env.example .envWindows (Command Prompt):
copy .env.example .envWindows (PowerShell):
Copy-Item .env.example .envOpen .env and set OPENWEATHERMAP_API_KEY=your_key_here.
Tip: If port 3000 is already in use, change
APP_PORTin your.envfile.
mvn spring-boot:runOpen http://localhost:3000 for the dashboard.
Open http://localhost:3000/docs for the Swagger UI.
# All unit + integration tests
mvn test
# Only unit tests
mvn test -Dtest="com.example.weatherapp.unit.**"
# Only integration tests
mvn test -Dtest="com.example.weatherapp.integration.**"
# E2E tests (Playwright) — Spring Boot starts the server automatically
# First run downloads Playwright browser binaries (~200 MB)
mvn verify -Pe2e# Format code (apply)
mvn spotless:apply
# Check formatting
mvn spotless:check.
├── pom.xml
├── .env.example
├── EXERCISES.md
├── README.md
├── .github/
│ ├── copilot-instructions.md ← always-on project context
│ ├── instructions/
│ │ ├── java.instructions.md ← Java/Spring conventions (src/main/**)
│ │ ├── testing.instructions.md ← Test conventions (src/test/**)
│ │ └── frontend.instructions.md ← Frontend conventions (static/**)
│ └── agents/
│ └── teacher.agent.md ← Exercise Tutor agent
└── src/
├── main/
│ ├── java/com/example/weatherapp/
│ │ ├── WeatherAppApplication.java
│ │ ├── config/ ← AppSettings, RestClientConfig
│ │ ├── controller/ ← WeatherController, LocationController
│ │ ├── error/ ← Domain exception hierarchy
│ │ ├── exception/ ← GlobalExceptionHandler
│ │ ├── model/ ← Records, enums, DTOs, OWM response shapes
│ │ ├── repository/ ← LocationRepository
│ │ ├── service/ ← WeatherService, OpenWeatherMapClient
│ │ └── util/ ← Converters
│ └── resources/
│ ├── application.properties
│ └── static/ ← index.html, static/style.css, static/app.js
└── test/
└── java/com/example/weatherapp/
├── factories/ ← TestFactories
├── unit/ ← ConvertersTest, LocationRepositoryTest, WeatherServiceTest
├── integration/ ← WeatherApiTest, LocationsApiTest
└── e2e/ ← DashboardTest (Playwright)
| Method | Path | Query Params | Description |
|---|---|---|---|
| GET | /api/weather/current |
lat, lon, units |
Current weather for coordinates |
| GET | /api/weather/forecast |
lat, lon, days (1-5), units |
Multi-day forecast |
| GET | /api/weather/alerts |
lat, lon |
Custom threshold-based alerts |
| Method | Path | Description |
|---|---|---|
| GET | /api/locations |
List all saved locations |
| POST | /api/locations |
Save a new location |
| GET | /api/locations/:id |
Get a location by ID |
| PUT | /api/locations/:id |
Update a location |
| DELETE | /api/locations/:id |
Delete a location |
| GET | /api/locations/:id/weather |
Get weather for a saved location |
This project uses a layered instruction system:
| File | Scope | Purpose |
|---|---|---|
.github/copilot-instructions.md |
All files (always on) | Project overview, architecture, dependencies, run commands |
.github/instructions/java.instructions.md |
src/main/** |
Java/Spring coding conventions |
.github/instructions/testing.instructions.md |
src/test/** |
Testing framework, AAA pattern, factory usage, mocking strategy |
.github/instructions/frontend.instructions.md |
src/main/resources/static/** |
Vanilla JS/CSS/HTML conventions |
.github/agents/teacher.agent.md |
Agent invocation | Exercise Tutor — guides workshop participants |
- Add Docker / Compose support for easy local setup
- Add rate-limiting for OpenWeatherMap API calls
- Add location search by city name (geocoding API)
- Persist saved locations between restarts (optional database mode)
- Add unit conversion toggle on the dashboard without re-fetching