├── client ├── .dockerignore ├── src │ ├── styles │ │ └── global.css │ ├── pages │ │ ├── index.astro │ │ ├── dog │ │ │ └── [id].astro │ │ └── about.astro │ ├── assets │ │ ├── background.svg │ │ └── astro.svg │ ├── layouts │ │ └── Layout.astro │ ├── middleware.ts │ └── components │ │ ├── Header.astro │ │ ├── DogDetails.svelte │ │ └── DogList.svelte ├── svelte.config.js ├── tsconfig.json ├── Dockerfile ├── astro.config.mjs ├── public │ └── favicon.svg ├── package.json ├── run-tests.sh ├── e2e-tests │ ├── about.spec.ts │ ├── README.md │ ├── api-integration.spec.ts │ ├── dog-details.spec.ts │ └── homepage.spec.ts ├── playwright.config.ts └── README.md ├── .DS_Store ├── server ├── requirements.txt ├── dogshelter.db ├── models │ ├── __init__.py │ ├── base.py │ ├── breed.py │ ├── breeds.csv │ └── dog.py ├── app.py ├── test_app.py └── utils │ └── seed_database.py ├── content ├── .DS_Store ├── full-day │ ├── images │ │ ├── 3-reload.png │ │ ├── 1-dependabot.png │ │ ├── 3-open-browser.png │ │ ├── 4-select-file.png │ │ ├── 1-code-scanning.png │ │ ├── 1-secret-scanning.png │ │ ├── 3-secrets-variables.png │ │ ├── 1-code-scanning-dialog.png │ │ ├── 5-copilot-chat-references.png │ │ └── 7-generate-commit-message.png │ ├── 0-setup.md │ ├── README.md │ ├── 2-issues.md │ ├── 7-github-flow.md │ ├── 1-code-scanning.md │ ├── 6-code.md │ ├── 4-testing.md │ ├── 3-codespaces.md │ └── 5-context.md ├── 1-hour │ ├── images │ │ ├── 0-setup-template.png │ │ ├── 0-setup-configure.png │ │ ├── copilot-edits-history.png │ │ ├── copilot-chat-references.png │ │ ├── copilot-edits-keep-undo-file.png │ │ ├── copilot-agent-mode-how-it-works.png │ │ ├── copilot-edits-keep-undo-global.png │ │ └── tail-spin-shelter-terminal-theme.png │ ├── 2-explore-project.md │ ├── README.md │ ├── 0-setup.md │ ├── 5-bonus.md │ ├── 1-add-endpoint.md │ ├── 4-add-feature.md │ └── 3-copilot-instructions.md ├── README.md ├── prompts │ ├── fun-add-dog-animation.md │ ├── conversion-convert-flask-to-golang.md │ ├── monitoring-add-logging.md │ ├── README.md │ └── fun-add-themes.md └── GitHub-Copilot-Resources.md ├── .github ├── CODEOWNERS ├── dependabot.yml └── instructions │ └── bicep-code-best-practices.instructions.md ├── .vscode └── settings.json ├── .gitignore ├── SUPPORT.md ├── scripts ├── seed-database.sh ├── seed-database.ps1 ├── start-app.sh ├── start-app.ps1 ├── common.sh └── common.ps1 ├── README.md ├── LICENSE ├── SECURITY.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md /client/.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist -------------------------------------------------------------------------------- /client/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/.DS_Store -------------------------------------------------------------------------------- /server/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | sqlalchemy 3 | flask_sqlalchemy 4 | flask-cors -------------------------------------------------------------------------------- /content/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/.DS_Store -------------------------------------------------------------------------------- /server/dogshelter.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/server/dogshelter.db -------------------------------------------------------------------------------- /content/full-day/images/3-reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/3-reload.png -------------------------------------------------------------------------------- /client/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@astrojs/svelte'; 2 | 3 | export default { 4 | preprocess: vitePreprocess(), 5 | } 6 | -------------------------------------------------------------------------------- /content/1-hour/images/0-setup-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/0-setup-template.png -------------------------------------------------------------------------------- /content/full-day/images/1-dependabot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/1-dependabot.png -------------------------------------------------------------------------------- /content/full-day/images/3-open-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/3-open-browser.png -------------------------------------------------------------------------------- /content/full-day/images/4-select-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/4-select-file.png -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": [".astro/types.d.ts", "**/*"], 4 | "exclude": ["dist"] 5 | } 6 | -------------------------------------------------------------------------------- /content/1-hour/images/0-setup-configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/0-setup-configure.png -------------------------------------------------------------------------------- /content/full-day/images/1-code-scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/1-code-scanning.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-edits-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/copilot-edits-history.png -------------------------------------------------------------------------------- /content/full-day/images/1-secret-scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/1-secret-scanning.png -------------------------------------------------------------------------------- /content/full-day/images/3-secrets-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/3-secrets-variables.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-chat-references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/copilot-chat-references.png -------------------------------------------------------------------------------- /content/full-day/images/1-code-scanning-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/1-code-scanning-dialog.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-edits-keep-undo-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/copilot-edits-keep-undo-file.png -------------------------------------------------------------------------------- /content/full-day/images/5-copilot-chat-references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/5-copilot-chat-references.png -------------------------------------------------------------------------------- /content/full-day/images/7-generate-commit-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/full-day/images/7-generate-commit-message.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-agent-mode-how-it-works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/copilot-agent-mode-how-it-works.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-edits-keep-undo-global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/copilot-edits-keep-undo-global.png -------------------------------------------------------------------------------- /content/1-hour/images/tail-spin-shelter-terminal-theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/HEAD/content/1-hour/images/tail-spin-shelter-terminal-theme.png -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts AS runtime 2 | WORKDIR /app 3 | 4 | COPY . . 5 | 6 | RUN npm install 7 | RUN npm run build 8 | 9 | ENV HOST=0.0.0.0 10 | ENV PORT=4321 11 | EXPOSE 4321 12 | CMD node ./dist/server/entry.mjs -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # For more information, see [docs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax) 2 | 3 | # This repository is maintained by: 4 | * @geektrainer @peckjon @chrisreddington 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "CODEOWNERS", 4 | "codespace", 5 | "Codespaces", 6 | "containerapp", 7 | "creds", 8 | "devcontainer", 9 | "devcontainers", 10 | "prebuild" 11 | ], 12 | "github.copilot.nextEditSuggestions.enabled": true, 13 | } -------------------------------------------------------------------------------- /server/models/__init__.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | 5 | # Import models after db is defined to avoid circular imports 6 | from .breed import Breed 7 | from .dog import Dog 8 | 9 | # Initialize function to be called from app.py 10 | def init_db(app): 11 | db.init_app(app) 12 | 13 | # Create tables when initializing 14 | with app.app_context(): 15 | db.create_all() -------------------------------------------------------------------------------- /client/astro.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { defineConfig } from 'astro/config'; 3 | import tailwindcss from '@tailwindcss/vite'; 4 | import svelte from '@astrojs/svelte'; 5 | 6 | import node from '@astrojs/node'; 7 | 8 | // https://astro.build/config 9 | export default defineConfig({ 10 | output: 'server', 11 | integrations: [ 12 | svelte(), 13 | ], 14 | vite: { 15 | plugins: [tailwindcss(), svelte()] 16 | }, 17 | 18 | adapter: node({ 19 | mode: 'standalone' 20 | }), 21 | }); -------------------------------------------------------------------------------- /content/README.md: -------------------------------------------------------------------------------- 1 | # Pets workshop 2 | 3 | This repository contains two workshops: 4 | 5 | - a [one hour](./1-hour/README.md) workshop focused on GitHub Copilot. 6 | - a [full-day](./full-day/README.md) workshop which covers a full day-in-the-life of a developer using GitHub for their DevOps processes. 7 | 8 | Both workshops are built around a fictional dog shelter, where you are a volunteer helping them build out their website. 9 | 10 | ## Get started 11 | 12 | To get started, you choose the option above based on the event you're attending, or as indicated by your workshop mentor. 13 | -------------------------------------------------------------------------------- /client/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | import DogList from '../components/DogList.svelte'; 4 | import "../styles/global.css"; 5 | --- 6 | 7 | 8 |
9 |
10 |

Welcome to Tailspin Shelter

11 |

Find your perfect companion from our wonderful selection of dogs looking for their forever homes.

12 |
13 | 14 | 15 |
16 |
17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # build output 4 | dist/ 5 | 6 | # generated types 7 | .astro/ 8 | 9 | # dependencies 10 | node_modules/ 11 | 12 | # logs 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | pnpm-debug.log* 17 | 18 | # environment variables 19 | .env 20 | .env.production 21 | 22 | # macOS-specific files 23 | .DS_Store 24 | 25 | # jetbrains setting folder 26 | .idea/ 27 | 28 | # python 29 | venv 30 | .venv 31 | __pycache__/ 32 | *.py[cod] 33 | *$py.class 34 | 35 | # flask 36 | instance/ 37 | .webassets-cache 38 | flask_session/ 39 | .coverage 40 | htmlcov/ 41 | 42 | # playwright 43 | client/test-results/ 44 | client/playwright-report/ -------------------------------------------------------------------------------- /server/models/base.py: -------------------------------------------------------------------------------- 1 | # filepath: server/models/base.py 2 | from . import db 3 | 4 | class BaseModel(db.Model): 5 | __abstract__ = True 6 | 7 | @staticmethod 8 | def validate_string_length(field_name, value, min_length=2, allow_none=False): 9 | if value is None: 10 | if allow_none: 11 | return value 12 | else: 13 | raise ValueError(f"{field_name} cannot be empty") 14 | 15 | if not isinstance(value, str): 16 | raise ValueError(f"{field_name} must be a string") 17 | 18 | if len(value.strip()) < min_length: 19 | raise ValueError(f"{field_name} must be at least {min_length} characters") 20 | 21 | return value -------------------------------------------------------------------------------- /content/prompts/fun-add-dog-animation.md: -------------------------------------------------------------------------------- 1 | # Puppy Cursor Follower 2 | 3 | Add an adorable cartoon dog to the bottom-right corner of the website that follows the user's cursor with its eyes, similar to the classic XEyes program from X11. 4 | 5 | ## Requirements: 6 | - The dog should be cute and cartoony with expressive eyes 7 | - Eyes should smoothly track the cursor position across the entire screen 8 | - Position the dog in the bottom-right corner as a fixed element (sticky positioning) 9 | - Dog should remain visible even when the page is scrolled 10 | - Add a slight head tilt or ear wiggle on mouse clicks for extra charm 11 | - Optional: Make the dog occasionally blink or perform a random animation 12 | 13 | Let's make browsing fun again with this interactive canine companion! 🐶 -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. 6 | 7 | For help or questions about using this project, please [file an issue](/issues) 8 | 9 | **TODO: REPO MAINTAINERS** Please include one of the following statements file: 10 | 11 | - **GitHub for Startups Workshops** is under active development and maintained by GitHub staff **AND THE COMMUNITY**. We will do our best to respond to support, feature requests, and community questions in a timely manner. 12 | 13 | ## GitHub Support Policy 14 | 15 | Support for this project is limited to the resources listed above. -------------------------------------------------------------------------------- /client/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /scripts/seed-database.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | source "$SCRIPT_DIR/common.sh" 5 | 6 | INITIAL_DIR=$(pwd) 7 | 8 | PROJECT_ROOT=$(get_project_root) 9 | 10 | setup_virtual_env "$PROJECT_ROOT" || { 11 | echo "Error: Failed to setup virtual environment" 12 | cd "$INITIAL_DIR" 13 | exit 1 14 | } 15 | 16 | install_python_deps "$PROJECT_ROOT" || { 17 | echo "Error: Failed to install Python dependencies" 18 | cd "$INITIAL_DIR" 19 | exit 1 20 | } 21 | 22 | navigate_to_server "$PROJECT_ROOT" "$INITIAL_DIR" || { 23 | exit 1 24 | } 25 | 26 | echo "Seeding database (idempotent)..." 27 | run_python_script "utils/seed_database.py" || { 28 | echo "Error: Failed to run seed database script" 29 | cd "$INITIAL_DIR" 30 | exit 1 31 | } 32 | 33 | # Return to initial directory 34 | cd "$INITIAL_DIR" 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pets workshop 2 | 3 | This repository contains the project for two guided workshops to explore various GitHub features. The project is a website for a fictional dog shelter, with a [Flask](https://flask.palletsprojects.com/en/stable/) backend using [SQLAlchemy](https://www.sqlalchemy.org/) and [Astro](https://astro.build/) frontend using [Svelte](https://svelte.dev/) for dynamic pages. 4 | 5 | ## Getting started 6 | 7 | > **[Get started learning about development with GitHub!](./content/README.md)** 8 | 9 | ## License 10 | 11 | This project is licensed under the terms of the MIT open source license. Please refer to the [LICENSE](./LICENSE) for the full terms. 12 | 13 | ## Maintainers 14 | 15 | You can find the list of maintainers in [CODEOWNERS](./.github/CODEOWNERS). 16 | 17 | ## Support 18 | 19 | This project is provided as-is, and may be updated over time. If you have questions, please open an issue. 20 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dog-shelter", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "build": "astro build", 8 | "preview": "astro preview", 9 | "astro": "astro", 10 | "test:e2e": "playwright test", 11 | "test:e2e:ui": "playwright test --ui", 12 | "test:e2e:debug": "playwright test --debug", 13 | "test:e2e:headed": "playwright test --headed", 14 | "test:e2e:chromium": "playwright test --project=chromium" 15 | }, 16 | "dependencies": { 17 | "@astrojs/node": "^9.1.3", 18 | "@astrojs/svelte": "^7.0.6", 19 | "@tailwindcss/vite": "^4.0.13", 20 | "astro": "^5.4.3", 21 | "svelte": "^5.23.0", 22 | "typescript": "^5.8.2" 23 | }, 24 | "devDependencies": { 25 | "@playwright/test": "^1.49.1", 26 | "@types/node": "^22.13.11", 27 | "autoprefixer": "^10.4.21", 28 | "postcss": "^8.5.3", 29 | "tailwindcss": "^4.0.14" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/models/breed.py: -------------------------------------------------------------------------------- 1 | # filepath: server/models/breed.py 2 | from . import db 3 | from .base import BaseModel 4 | from sqlalchemy.orm import validates, relationship 5 | 6 | class Breed(BaseModel): 7 | __tablename__ = 'breeds' 8 | 9 | id = db.Column(db.Integer, primary_key=True) 10 | name = db.Column(db.String(100), nullable=False, unique=True) 11 | description = db.Column(db.Text) 12 | 13 | # Relationship with Dog model 14 | dogs = relationship('Dog', backref='breed_info', lazy=True) 15 | 16 | @validates('name') 17 | def validate_name(self, key, name): 18 | return self.validate_string_length('Breed name', name, min_length=2) 19 | 20 | @validates('description') 21 | def validate_description(self, key, description): 22 | return self.validate_string_length('Description', description, min_length=10, allow_none=True) 23 | 24 | def __repr__(self): 25 | return f'' -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # This is the dependabot configuration file that automates dependency updates 2 | # Updates section configures how dependabot should handle dependency updates: 3 | # - Monitors NPM dependencies in the root directory 4 | # - Checks for updates weekly 5 | # - Groups updates based on their type (dev grouped by minor/patch or prod grouped by patch) 6 | # 7 | # Learn more at https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#configuration-options-for-the-dependabotyml-file 8 | version: 2 9 | updates: 10 | - package-ecosystem: npm 11 | directory: /client 12 | schedule: 13 | interval: weekly 14 | groups: 15 | npm-development: 16 | dependency-type: development 17 | update-types: 18 | - minor 19 | - patch 20 | npm-production: 21 | dependency-type: production 22 | update-types: 23 | - patch 24 | -------------------------------------------------------------------------------- /scripts/seed-database.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | # Source common utilities 4 | . "$PSScriptRoot/common.ps1" 5 | 6 | # Get project root 7 | $ProjectRoot = Get-ProjectRoot 8 | 9 | # Initialize virtual environment 10 | if (-not (Initialize-VirtualEnvironment -ProjectRoot $ProjectRoot)) { 11 | Write-Error "Failed to initialize virtual environment" 12 | exit 1 13 | } 14 | 15 | # Install Python dependencies 16 | if (-not (Install-PythonDependencies -ProjectRoot $ProjectRoot)) { 17 | Write-Warning "Failed to install Python dependencies" 18 | } 19 | 20 | # Run seed database script 21 | try { 22 | $seedScript = Join-Path $ProjectRoot "server/utils/seed_database.py" 23 | Write-Host "Seeding database (idempotent)..." 24 | 25 | if (-not (Invoke-PythonScript -ScriptPath $seedScript)) { 26 | Write-Warning "Seeding failed or script not found" 27 | exit 1 28 | } 29 | } catch { 30 | Write-Warning "Seeding failed: $_" 31 | exit 1 32 | } 33 | -------------------------------------------------------------------------------- /client/src/pages/dog/[id].astro: -------------------------------------------------------------------------------- 1 | --- 2 | export const prerender = false; 3 | import Layout from '../../layouts/Layout.astro'; 4 | import DogDetails from '../../components/DogDetails.svelte'; 5 | 6 | const { id } = Astro.params; 7 | const dogId = parseInt(id || '0'); 8 | 9 | const props = { dogId }; 10 | --- 11 | 12 | 13 |
14 | 15 | 16 | 24 |
25 |
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright GitHub, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /content/prompts/conversion-convert-flask-to-golang.md: -------------------------------------------------------------------------------- 1 | # Flask to Go Server Migration Project 2 | 3 | ## Objective 4 | 5 | Convert the existing Python Flask server implementation to a Go-based server with identical functionality and API endpoints. The Go implementation should maintain the same request handling, routes, data processing, and response formats as the original Flask server. 6 | 7 | The Python Flask is stored in #folder:server 8 | 9 | ## Requirements 10 | 1. Create a functionally equivalent Go server implementation 11 | 2. Match all existing API endpoints, query parameters, and HTTP methods 12 | 3. Preserve all current data processing logic and response formats 13 | 4. Implement the same error handling and status codes 14 | 5. Maintain any authentication mechanisms present in the Flask implementation 15 | 6. Use only the Go standard library where possible, with minimal external dependencies 16 | 7. Include appropriate comments explaining the code and any implementation decisions 17 | 18 | ## Deliverables 19 | 1. Complete Go source code organized in a folder named `go_server` 20 | 2. A main.go file with server initialization and configuration 21 | 3. Separate handler files for different API endpoint groups 22 | 4. Any utility or helper functions required 23 | 5. A README.md with setup and usage instructions 24 | 25 | -------------------------------------------------------------------------------- /client/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple test script that starts the app and runs Playwright tests 4 | set -e 5 | 6 | echo "Starting application servers in background..." 7 | 8 | # Store current directory 9 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 | cd "$SCRIPT_DIR/.." 11 | 12 | # Start the application using the existing script 13 | ./scripts/start-app.sh & 14 | APP_PID=$! 15 | 16 | # Function to cleanup 17 | cleanup() { 18 | echo "Cleaning up..." 19 | kill $APP_PID 2>/dev/null || true 20 | pkill -f "python.*app.py" 2>/dev/null || true 21 | pkill -f "npm.*dev" 2>/dev/null || true 22 | wait $APP_PID 2>/dev/null || true 23 | } 24 | 25 | # Trap signals to ensure cleanup 26 | trap cleanup EXIT INT TERM 27 | 28 | echo "Waiting for servers to start..." 29 | sleep 10 30 | 31 | # Check if servers are running 32 | for i in {1..30}; do 33 | if curl -s http://localhost:4321 > /dev/null && curl -s http://localhost:5100/api/dogs > /dev/null; then 34 | echo "Servers are ready!" 35 | break 36 | fi 37 | if [ $i -eq 30 ]; then 38 | echo "Servers failed to start" 39 | exit 1 40 | fi 41 | echo "Waiting for servers... ($i/30)" 42 | sleep 2 43 | done 44 | 45 | echo "Running Playwright tests..." 46 | cd client 47 | npx playwright test "$@" 48 | 49 | echo "Tests completed!" -------------------------------------------------------------------------------- /client/e2e-tests/about.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('About Page', () => { 4 | test('should load about page and display content', async ({ page }) => { 5 | await page.goto('/about'); 6 | 7 | // Check that the page title is correct 8 | await expect(page).toHaveTitle(/About - Tailspin Shelter/); 9 | 10 | // Check that the main heading is visible 11 | await expect(page.getByRole('heading', { name: 'About Tailspin Shelter' })).toBeVisible(); 12 | 13 | // Check that content is visible 14 | await expect(page.getByText('Nestled in the heart of Seattle')).toBeVisible(); 15 | await expect(page.getByText('The name "Tailspin" reflects')).toBeVisible(); 16 | 17 | // Check the fictional organization note 18 | await expect(page.getByText('Tailspin Shelter is a fictional organization')).toBeVisible(); 19 | }); 20 | 21 | test('should navigate back to homepage from about page', async ({ page }) => { 22 | await page.goto('/about'); 23 | 24 | // Click the "Back to Dogs" button 25 | await page.getByRole('link', { name: 'Back to Dogs' }).click(); 26 | 27 | // Should be redirected to homepage 28 | await expect(page).toHaveURL('/'); 29 | await expect(page.getByRole('heading', { name: 'Welcome to Tailspin Shelter' })).toBeVisible(); 30 | }); 31 | }); -------------------------------------------------------------------------------- /content/prompts/monitoring-add-logging.md: -------------------------------------------------------------------------------- 1 | Add logging commands to server application which is written in python 2 | 3 | The Python Flask is stored in #folder:server 4 | 5 | Create a standardized logging system for the Python Flask with the following requirements: 6 | 7 | 1. LOGGING LEVELS: Implement five distinct logging levels (DEBUG, INFO, WARNING, ERROR, CRITICAL) with clear usage guidelines for each. 8 | 9 | 2. FORMAT CONSISTENCY: Define a consistent log entry format including: 10 | - Timestamp (ISO 8601 format: YYYY-MM-DD HH:MM:SS.mmm) 11 | - Log level 12 | - Module/component name 13 | - Thread ID (where applicable) 14 | - Message content 15 | 16 | 3. CONFIGURATION: Provide a configuration system that allows: 17 | - Setting global minimum log level 18 | - Per-module logging levels 19 | - Multiple output destinations (console, file, external service) 20 | - Log rotation settings for file outputs 21 | 22 | 4. CODE EXAMPLES: Include example implementations showing: 23 | - Proper logger initialization 24 | - Correct usage of each log level 25 | - Error/exception logging with stack traces 26 | - Context-enriched logging 27 | 28 | 5. PERFORMANCE CONSIDERATIONS: Address how to optimize logging for production environments. 29 | 30 | The solution should be maintainable, follow industry best practices, and minimize performance impact. 31 | -------------------------------------------------------------------------------- /client/src/assets/background.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | /** 4 | * @see https://playwright.dev/docs/test-configuration 5 | */ 6 | export default defineConfig({ 7 | testDir: './e2e-tests', 8 | /* Run tests in files in parallel */ 9 | fullyParallel: true, 10 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 11 | forbidOnly: !!process.env.CI, 12 | /* Retry on CI only */ 13 | retries: process.env.CI ? 2 : 0, 14 | /* Opt out of parallel tests on CI. */ 15 | workers: process.env.CI ? 1 : undefined, 16 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 17 | reporter: 'html', 18 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 19 | use: { 20 | /* Base URL to use in actions like `await page.goto('/')`. */ 21 | baseURL: 'http://localhost:4321', 22 | 23 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 24 | trace: 'on-first-retry', 25 | }, 26 | 27 | /* Configure projects for major browsers */ 28 | projects: [ 29 | { 30 | name: 'chromium', 31 | use: { ...devices['Desktop Chrome'] }, 32 | }, 33 | ], 34 | 35 | /* Run your local dev server before starting the tests */ 36 | webServer: { 37 | command: 'cd .. && ./scripts/start-app.sh', 38 | url: 'http://localhost:4321', 39 | reuseExistingServer: !process.env.CI, 40 | timeout: 120 * 1000, 41 | }, 42 | }); -------------------------------------------------------------------------------- /server/models/breeds.csv: -------------------------------------------------------------------------------- 1 | Breed,Description 2 | Labrador Retriever,"America's favorite family dog, friendly and easy-going." 3 | French Bulldog,"Small, adaptable, and great for urban living." 4 | Golden Retriever,"Family-friendly, smart, and easy to train." 5 | German Shepherd,"Intelligent, loyal, used in police/military work, but also great pets." 6 | Bulldog (English Bulldog),"Laid-back, gentle, and great for apartments." 7 | Poodle,"Smart, hypoallergenic, comes in multiple sizes." 8 | Beagle,"Compact, friendly, and good with kids — but need exercise!" 9 | Rottweiler,"Strong, loyal, great family protector." 10 | Yorkshire Terrier (Yorkie),"Tiny but confident, popular in cities." 11 | Dachshund,"Small, bold, and fun — often called 'wiener dogs.'" 12 | Pembroke Welsh Corgi,"Small herding dog, smart and full of personality." 13 | Boxer,"Playful, protective, and great with kids." 14 | Shih Tzu,"Small, affectionate lap dogs." 15 | Chihuahua,"Tiny and portable, popular with city dwellers." 16 | Siberian Husky,"Beautiful and energetic, but needs lots of exercise — growing in popularity." 17 | Australian Shepherd,"Super smart, active, great for outdoor-loving families." 18 | Doberman Pinscher,"Protective, loyal, and surprisingly gentle family dogs when trained." 19 | Miniature Schnauzer,"Small, smart, hypoallergenic, with a spunky attitude." 20 | American Staffordshire Terrier,"Strong, muscular, and affectionate — often misunderstood but great companions." 21 | Pit Bull,"Loyal, strong, and loving; needs responsible ownership and training." 22 | -------------------------------------------------------------------------------- /content/prompts/README.md: -------------------------------------------------------------------------------- 1 | # Pets Workshop Prompts 2 | 3 | This directory contains various prompts designed for different aspects of development and enhancement of the Pets Workshop project. These prompts are meant for illustration purposes only. 4 | 5 | ## Prompt Overview 6 | 7 | ### Interface and User Experience 8 | 9 | - **[fun-add-themes](./fun-add-themes.md)**: Adds a theme selector dropdown that allows users to switch between multiple visual themes including 80s Retro, Terminal Classic, Hand-Sketched, Steampunk, and Fantasy Realm. Enhances user customization and visual appeal. 10 | 11 | - **[fun-add-dog-animation](./fun-add-dog-animation.md)**: Implements an interactive cartoon dog animation in the bottom-right corner of the website that follows the user's cursor with its eyes. The dog remains visible while scrolling and has extra animations on mouse clicks, adding a playful element to the user experience. 12 | 13 | ### Backend Development 14 | 15 | - **[conversion-convert-flask-to-golang](./conversion-convert-flask-to-golang.md)**: Provides instructions for migrating the existing Python Flask server to a Go-based implementation while maintaining identical functionality, API endpoints, and response formats. The goal is to create a functionally equivalent server using Go's standard library. 16 | 17 | - **[monitoring-add-logging](./monitoring-add-logging.md)**: Details requirements for implementing a comprehensive logging system in the Python Flask server with multiple logging levels, consistent formatting, configuration options, and performance considerations. This improves monitoring, debugging, and operational visibility. 18 | -------------------------------------------------------------------------------- /content/prompts/fun-add-themes.md: -------------------------------------------------------------------------------- 1 | # 🎨 Theme-tastic Interface Enhancement! 2 | 3 | ## 🎯 Your Mission 4 | Transform our boring interface into a playground of visual delights! Let users express themselves through awesome themes. 5 | 6 | ## 🔍 Key Requirements 7 | 1. **Theme Selector Dropdown** 8 | - Position: ↗️ Top-right corner of the screen 9 | - Behavior: Interface instantly refreshes when a new theme is selected 10 | - Default label: "Default" (our current look) 11 | 12 | ## 🌈 Required Themes 13 | Add these fabulous theme options: 14 | 15 | * **80s Retro** 🕹️ 16 | - Think neon colors, bold patterns, geometric shapes 17 | - Inspiration: Miami Vice, arcade games, synthwave 18 | 19 | * **Terminal Classic** 💻 20 | - Nostalgic VT100 green-on-black terminal look 21 | - Features: Monospace fonts, scan lines, command prompt aesthetic 22 | 23 | * **Hand-Sketched** ✏️ 24 | - UI elements that appear hand-drawn with a playful, creative feel 25 | - Think: Doodles, sketch lines, paper texture backgrounds 26 | 27 | * **Steampunk** ⚙️ 28 | - Brass, gears, leather, and Victorian-era aesthetics mixed with futuristic elements 29 | - Inspiration: Jules Verne, The League of Extraordinary Gentlemen, Bioshock Infinite 30 | 31 | * **Fantasy Realm** 🧙 32 | - Mystical forests, glowing runes, and enchanted elements 33 | - Inspiration: Lord of the Rings, Dungeons & Dragons, Skyrim 34 | 35 | 36 | ## 🚀 Bonus Points 37 | - Add subtle animations for theme transitions 38 | - Include a small preview of each theme in the dropdown 39 | - Make sure all themes maintain accessibility standards 40 | 41 | -------------------------------------------------------------------------------- /client/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Header from "../components/Header.astro" 3 | import "../styles/global.css" 4 | 5 | interface Props { 6 | title?: string; 7 | } 8 | 9 | const { title = "Tailspin Shelter" } = Astro.props; 10 | --- 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {title} 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 | 34 | 35 | 50 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Thanks for helping make GitHub safe for everyone. 2 | 3 | ## Security 4 | 5 | GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub). 6 | 7 | Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation. 8 | 9 | ## Reporting Security Issues 10 | 11 | If you believe you have found a security vulnerability in any GitHub-owned repository, please report it to us through coordinated disclosure. 12 | 13 | **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** 14 | 15 | Instead, please send an email to opensource-security[@]github.com. 16 | 17 | Please include as much of the information listed below as you can to help us better understand and resolve the issue: 18 | 19 | * The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting) 20 | * Full paths of source file(s) related to the manifestation of the issue 21 | * The location of the affected source code (tag/branch/commit or direct URL) 22 | * Any special configuration required to reproduce the issue 23 | * Step-by-step instructions to reproduce the issue 24 | * Proof-of-concept or exploit code (if possible) 25 | * Impact of the issue, including how an attacker might exploit the issue 26 | 27 | This information will help us triage your report more quickly. 28 | 29 | ## Policy 30 | 31 | See [GitHub's Safe Harbor Policy](https://docs.github.com/en/github/site-policy/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms) -------------------------------------------------------------------------------- /client/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { defineMiddleware } from "astro:middleware"; 2 | 3 | // Get server URL from environment variable with fallback for local development 4 | const API_SERVER_URL = process.env.API_SERVER_URL || 'http://localhost:5100'; 5 | 6 | // Middleware to handle API requests 7 | export const onRequest = defineMiddleware(async (context, next) => { 8 | console.log('Request URL:', context.request.url); 9 | 10 | // Guard clause: if not an API request, pass through to regular Astro handling 11 | if (!context.request.url.includes('/api/')) { 12 | return await next(); 13 | } 14 | 15 | // API request handling 16 | console.log('Forwarding request to server:', API_SERVER_URL); 17 | 18 | const url = new URL(context.request.url); 19 | const apiPath = url.pathname + url.search; 20 | 21 | // Create a new request to the backend server 22 | const serverRequest = new Request(`${API_SERVER_URL}${apiPath}`, { 23 | method: context.request.method, 24 | headers: context.request.headers, 25 | body: context.request.method !== 'GET' && context.request.method !== 'HEAD' ? 26 | await context.request.clone().arrayBuffer() : undefined, 27 | }); 28 | 29 | try { 30 | // Forward the request to the API server 31 | const response = await fetch(serverRequest); 32 | const data = await response.arrayBuffer(); 33 | 34 | // Return the response from the API server 35 | return new Response(data, { 36 | status: response.status, 37 | statusText: response.statusText, 38 | headers: response.headers, 39 | }); 40 | } catch (error) { 41 | console.error('Error forwarding request to API:', error); 42 | return new Response(JSON.stringify({ error: 'Failed to reach API server' }), { 43 | status: 502, 44 | headers: { 'Content-Type': 'application/json' } 45 | }); 46 | } 47 | }); -------------------------------------------------------------------------------- /client/e2e-tests/README.md: -------------------------------------------------------------------------------- 1 | # End-to-End Tests for Tailspin Shelter 2 | 3 | This directory contains Playwright end-to-end tests for the Tailspin Shelter website. 4 | 5 | ## Test Files 6 | 7 | - `homepage.spec.ts` - Tests for the main homepage functionality 8 | - `about.spec.ts` - Tests for the about page 9 | - `dog-details.spec.ts` - Tests for individual dog detail pages 10 | - `api-integration.spec.ts` - Tests for API integration and error handling 11 | 12 | ## Running Tests 13 | 14 | ### Prerequisites 15 | 16 | Make sure you have installed dependencies: 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | ### Running Tests 22 | 23 | ```bash 24 | # Run all tests 25 | npm run test:e2e 26 | 27 | # Run tests with UI mode (for debugging) 28 | npm run test:e2e:ui 29 | 30 | # Run tests in headed mode (see browser) 31 | npm run test:e2e:headed 32 | 33 | # Debug tests 34 | npm run test:e2e:debug 35 | ``` 36 | 37 | ## Test Coverage 38 | 39 | The tests cover the following core functionality: 40 | 41 | ### Homepage Tests 42 | - Page loads with correct title and content 43 | - Dog list displays properly 44 | - Loading states work correctly 45 | - Error handling for API failures 46 | 47 | ### About Page Tests 48 | - About page content displays correctly 49 | - Navigation back to homepage works 50 | 51 | ### Dog Details Tests 52 | - Navigation from homepage to dog details 53 | - Navigation back from dog details to homepage 54 | - Handling of invalid dog IDs 55 | 56 | ### API Integration Tests 57 | - Successful API responses 58 | - Empty dog list handling 59 | - Network error handling 60 | 61 | ## Configuration 62 | 63 | Tests are configured in `../playwright.config.ts` and automatically start the application servers using the existing `scripts/start-app.sh` script before running tests. 64 | 65 | The tests run against: 66 | - Client (Astro): http://localhost:4321 67 | - Server (Flask): http://localhost:5100 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | [fork]: https://github.com/github/startups-content/fork 4 | [pr]: https://github.com/github/startups-content/compare 5 | [code-of-conduct]: CODE_OF_CONDUCT.md 6 | 7 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. 8 | 9 | Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE.md). 10 | 11 | Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms. 12 | 13 | ## Prerequisites for running and testing code 14 | 15 | These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process. 16 | 17 | 1. install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) 18 | 1. [install golangci-lint](https://golangci-lint.run/usage/install/#local-installation) 19 | 20 | ## Submitting a pull request 21 | 22 | 1. [Fork][fork] and clone the repository 23 | 2. Make your change 24 | 3. Push to your fork and [submit a pull request][pr] 25 | 4. Pat your self on the back and wait for your pull request to be reviewed and merged. 26 | 27 | - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. 28 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 29 | 30 | ## Resources 31 | 32 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 33 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 34 | - [GitHub Help](https://help.github.com) -------------------------------------------------------------------------------- /scripts/start-app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | source "$SCRIPT_DIR/common.sh" 5 | 6 | # Store initial directory 7 | INITIAL_DIR=$(pwd) 8 | 9 | # Navigate to project root if needed 10 | ensure_project_root 11 | 12 | echo "Starting API (Flask) server..." 13 | 14 | # Get project root and setup virtual environment 15 | PROJECT_ROOT=$(get_project_root) 16 | 17 | # Setup virtual environment 18 | setup_virtual_env "$PROJECT_ROOT" || { 19 | echo "Error: Failed to setup virtual environment" 20 | cd "$INITIAL_DIR" 21 | exit 1 22 | } 23 | 24 | # Install Python dependencies 25 | install_python_deps "$PROJECT_ROOT" || { 26 | echo "Error: Failed to install Python dependencies" 27 | cd "$INITIAL_DIR" 28 | exit 1 29 | } 30 | 31 | # Navigate to server directory and setup Flask environment 32 | navigate_to_server "$PROJECT_ROOT" "$INITIAL_DIR" || { 33 | exit 1 34 | } 35 | 36 | setup_flask_env 37 | 38 | # Start Python server in background 39 | python_cmd=$(get_python_command) 40 | $python_cmd app.py & 41 | SERVER_PID=$! 42 | 43 | echo "Starting client (Astro)..." 44 | cd ../client || { 45 | echo "Error: client directory not found" 46 | cd "$INITIAL_DIR" 47 | exit 1 48 | } 49 | npm install 50 | npm run dev -- --no-clearScreen & 51 | 52 | # Store the SvelteKit server process ID 53 | CLIENT_PID=$! 54 | 55 | # Sleep for 3 seconds 56 | sleep 5 57 | 58 | # Display the server URLs 59 | echo "" 60 | print_success "Server (Flask) running at: http://localhost:5100" 61 | print_success "Client (Astro) server running at: http://localhost:4321" 62 | echo "" 63 | 64 | echo "Ctl-C to stop the servers" 65 | 66 | # Function to handle script termination 67 | cleanup() { 68 | cleanup_processes "$SERVER_PID" "$CLIENT_PID" "$INITIAL_DIR" 69 | exit 0 70 | } 71 | 72 | # Trap multiple signals 73 | trap cleanup SIGINT SIGTERM SIGQUIT EXIT 74 | 75 | # Keep the script running 76 | wait -------------------------------------------------------------------------------- /client/e2e-tests/api-integration.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('API Integration', () => { 4 | test('should fetch dogs from API', async ({ page }) => { 5 | // Mock successful API response 6 | await page.route('/api/dogs', route => { 7 | route.fulfill({ 8 | status: 200, 9 | contentType: 'application/json', 10 | body: JSON.stringify([ 11 | { id: 1, name: 'Buddy', breed: 'Golden Retriever' }, 12 | { id: 2, name: 'Luna', breed: 'Husky' }, 13 | { id: 3, name: 'Max', breed: 'Labrador' } 14 | ]) 15 | }); 16 | }); 17 | 18 | await page.goto('/'); 19 | 20 | // Check that mocked dogs are displayed 21 | await expect(page.getByText('Buddy')).toBeVisible(); 22 | await expect(page.getByText('Golden Retriever')).toBeVisible(); 23 | await expect(page.getByText('Luna')).toBeVisible(); 24 | await expect(page.getByText('Husky')).toBeVisible(); 25 | await expect(page.getByText('Max')).toBeVisible(); 26 | await expect(page.getByText('Labrador')).toBeVisible(); 27 | }); 28 | 29 | test('should handle empty dog list', async ({ page }) => { 30 | // Mock empty API response 31 | await page.route('/api/dogs', route => { 32 | route.fulfill({ 33 | status: 200, 34 | contentType: 'application/json', 35 | body: JSON.stringify([]) 36 | }); 37 | }); 38 | 39 | await page.goto('/'); 40 | 41 | // Check that empty state message is displayed 42 | await expect(page.getByText('No dogs available at the moment')).toBeVisible(); 43 | }); 44 | 45 | test('should handle network errors', async ({ page }) => { 46 | // Mock network error 47 | await page.route('/api/dogs', route => { 48 | route.abort('failed'); 49 | }); 50 | 51 | await page.goto('/'); 52 | 53 | // Check that error message is displayed 54 | await expect(page.getByText(/Error:/)).toBeVisible({ timeout: 10000 }); 55 | }); 56 | }); -------------------------------------------------------------------------------- /client/src/components/Header.astro: -------------------------------------------------------------------------------- 1 | --- 2 | // Import any necessary dependencies 3 | --- 4 | 5 |
6 |
7 |
8 |
9 | 14 | 20 |
21 |

Tailspin Shelter

22 |
23 |
24 |
25 | 26 | -------------------------------------------------------------------------------- /client/e2e-tests/dog-details.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('Dog Details', () => { 4 | test('should navigate to dog details from homepage', async ({ page }) => { 5 | await page.goto('/'); 6 | 7 | // Wait for dogs to load 8 | await page.waitForSelector('.grid a[href^="/dog/"]', { timeout: 10000 }); 9 | 10 | // Get the first dog link 11 | const firstDogLink = page.locator('.grid a[href^="/dog/"]').first(); 12 | 13 | // Get the dog name for verification 14 | const dogName = await firstDogLink.locator('h3').textContent(); 15 | 16 | // Click on the first dog 17 | await firstDogLink.click(); 18 | 19 | // Should be on a dog details page 20 | await expect(page.url()).toMatch(/\/dog\/\d+/); 21 | 22 | // Check that the page title is correct 23 | await expect(page).toHaveTitle(/Dog Details - Tailspin Shelter/); 24 | 25 | // Check for back button 26 | await expect(page.getByRole('link', { name: 'Back to All Dogs' })).toBeVisible(); 27 | }); 28 | 29 | test('should navigate back to homepage from dog details', async ({ page }) => { 30 | // Go directly to a dog details page (assuming dog with ID 1 exists) 31 | await page.goto('/dog/1'); 32 | 33 | // Click the back button 34 | await page.getByRole('link', { name: 'Back to All Dogs' }).click(); 35 | 36 | // Should be redirected to homepage 37 | await expect(page).toHaveURL('/'); 38 | await expect(page.getByRole('heading', { name: 'Welcome to Tailspin Shelter' })).toBeVisible(); 39 | }); 40 | 41 | test('should handle invalid dog ID gracefully', async ({ page }) => { 42 | // Go to a dog page with an invalid ID 43 | await page.goto('/dog/99999'); 44 | 45 | // The page should still load (even if no dog is found) 46 | await expect(page).toHaveTitle(/Dog Details - Tailspin Shelter/); 47 | 48 | // Back button should still be available 49 | await expect(page.getByRole('link', { name: 'Back to All Dogs' })).toBeVisible(); 50 | }); 51 | }); -------------------------------------------------------------------------------- /server/models/dog.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from enum import Enum 3 | from . import db 4 | from .base import BaseModel 5 | from sqlalchemy.orm import validates, relationship 6 | 7 | # Define an Enum for dog status 8 | class AdoptionStatus(Enum): 9 | AVAILABLE = 'Available' 10 | ADOPTED = 'Adopted' 11 | PENDING = 'Pending' 12 | 13 | class Dog(BaseModel): 14 | __tablename__ = 'dogs' 15 | 16 | id = db.Column(db.Integer, primary_key=True) 17 | name = db.Column(db.String(100), nullable=False) 18 | breed_id = db.Column(db.Integer, db.ForeignKey('breeds.id')) 19 | age = db.Column(db.Integer) 20 | gender = db.Column(db.String(10)) 21 | description = db.Column(db.Text) 22 | 23 | # Adoption status 24 | status = db.Column(db.Enum(AdoptionStatus), default=AdoptionStatus.AVAILABLE) 25 | intake_date = db.Column(db.DateTime, default=datetime.now) 26 | adoption_date = db.Column(db.DateTime, nullable=True) 27 | 28 | @validates('name') 29 | def validate_name(self, key, name): 30 | return self.validate_string_length('Dog name', name, min_length=2) 31 | 32 | @validates('gender') 33 | def validate_gender(self, key, gender): 34 | if gender not in ['Male', 'Female', 'Unknown']: 35 | raise ValueError("Gender must be 'Male', 'Female', or 'Unknown'") 36 | return gender 37 | 38 | @validates('description') 39 | def validate_description(self, key, description): 40 | if description is not None: 41 | return self.validate_string_length('Description', description, min_length=10, allow_none=True) 42 | return description 43 | 44 | def __repr__(self): 45 | return f'' 46 | 47 | def to_dict(self): 48 | return { 49 | 'id': self.id, 50 | 'name': self.name, 51 | 'breed': self.breed.name if self.breed else None, 52 | 'age': self.age, 53 | 'gender': self.gender, 54 | 'description': self.description, 55 | 'status': self.status.name if self.status else 'UNKNOWN' 56 | } -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Astro Starter Kit: Basics 2 | 3 | ```sh 4 | npm create astro@latest -- --template basics 5 | ``` 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics) 8 | [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics) 9 | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json) 10 | 11 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 12 | 13 | ![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554) 14 | 15 | ## 🚀 Project Structure 16 | 17 | Inside of your Astro project, you'll see the following folders and files: 18 | 19 | ```text 20 | / 21 | ├── public/ 22 | │ └── favicon.svg 23 | ├── src/ 24 | │ ├── layouts/ 25 | │ │ └── Layout.astro 26 | │ └── pages/ 27 | │ └── index.astro 28 | └── package.json 29 | ``` 30 | 31 | To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/). 32 | 33 | ## 🧞 Commands 34 | 35 | All commands are run from the root of the project, from a terminal: 36 | 37 | | Command | Action | 38 | | :------------------------ | :----------------------------------------------- | 39 | | `npm install` | Installs dependencies | 40 | | `npm run dev` | Starts local dev server at `localhost:4321` | 41 | | `npm run build` | Build your production site to `./dist/` | 42 | | `npm run preview` | Preview your build locally, before deploying | 43 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 44 | | `npm run astro -- --help` | Get help using the Astro CLI | 45 | 46 | ## 👀 Want to learn more? 47 | 48 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). 49 | -------------------------------------------------------------------------------- /server/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Dict, List, Any, Optional 3 | from flask import Flask, jsonify, Response 4 | from models import init_db, db, Dog, Breed 5 | 6 | # Get the server directory path 7 | base_dir: str = os.path.abspath(os.path.dirname(__file__)) 8 | 9 | app: Flask = Flask(__name__) 10 | app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(base_dir, "dogshelter.db")}' 11 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 12 | 13 | # Initialize the database with the app 14 | init_db(app) 15 | 16 | @app.route('/api/dogs', methods=['GET']) 17 | def get_dogs() -> Response: 18 | query = db.session.query( 19 | Dog.id, 20 | Dog.name, 21 | Breed.name.label('breed') 22 | ).join(Breed, Dog.breed_id == Breed.id) 23 | 24 | dogs_query = query.all() 25 | 26 | # Convert the result to a list of dictionaries 27 | dogs_list: List[Dict[str, Any]] = [ 28 | { 29 | 'id': dog.id, 30 | 'name': dog.name, 31 | 'breed': dog.breed 32 | } 33 | for dog in dogs_query 34 | ] 35 | 36 | return jsonify(dogs_list) 37 | 38 | @app.route('/api/dogs/', methods=['GET']) 39 | def get_dog(id: int) -> tuple[Response, int] | Response: 40 | # Query the specific dog by ID and join with breed to get breed name 41 | dog_query = db.session.query( 42 | Dog.id, 43 | Dog.name, 44 | Breed.name.label('breed'), 45 | Dog.age, 46 | Dog.description, 47 | Dog.gender, 48 | Dog.status 49 | ).join(Breed, Dog.breed_id == Breed.id).filter(Dog.id == id).first() 50 | 51 | # Return 404 if dog not found 52 | if not dog_query: 53 | return jsonify({"error": "Dog not found"}), 404 54 | 55 | # Convert the result to a dictionary 56 | dog: Dict[str, Any] = { 57 | 'id': dog_query.id, 58 | 'name': dog_query.name, 59 | 'breed': dog_query.breed, 60 | 'age': dog_query.age, 61 | 'description': dog_query.description, 62 | 'gender': dog_query.gender, 63 | 'status': dog_query.status.name 64 | } 65 | 66 | return jsonify(dog) 67 | 68 | ## HERE 69 | 70 | if __name__ == '__main__': 71 | app.run(debug=True, port=5100) # Port 5100 to avoid macOS conflicts -------------------------------------------------------------------------------- /client/e2e-tests/homepage.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | test.describe('Tailspin Shelter Homepage', () => { 4 | test('should load homepage and display title', async ({ page }) => { 5 | await page.goto('/'); 6 | 7 | // Check that the page title is correct 8 | await expect(page).toHaveTitle(/Tailspin Shelter - Find Your Forever Friend/); 9 | 10 | // Check that the main heading is visible 11 | await expect(page.getByRole('heading', { name: 'Welcome to Tailspin Shelter' })).toBeVisible(); 12 | 13 | // Check that the description is visible 14 | await expect(page.getByText('Find your perfect companion from our wonderful selection')).toBeVisible(); 15 | }); 16 | 17 | test('should display dog list section', async ({ page }) => { 18 | await page.goto('/'); 19 | 20 | // Check that the "Available Dogs" heading is visible 21 | await expect(page.getByRole('heading', { name: 'Available Dogs' })).toBeVisible(); 22 | 23 | // Wait for dogs to load (either loading state, error, or actual dogs) 24 | await page.waitForSelector('.grid', { timeout: 10000 }); 25 | }); 26 | 27 | test('should show loading state initially', async ({ page }) => { 28 | await page.goto('/'); 29 | 30 | // Check that loading animation is shown initially 31 | // Look for the loading skeleton cards 32 | const loadingElements = page.locator('.animate-pulse').first(); 33 | 34 | // Either loading should be visible initially, or dogs should load quickly 35 | try { 36 | await expect(loadingElements).toBeVisible({ timeout: 2000 }); 37 | } catch { 38 | // If loading finishes too quickly, that's fine - check for dog content instead 39 | await expect(page.locator('.grid')).toBeVisible(); 40 | } 41 | }); 42 | 43 | test('should handle API errors gracefully', async ({ page }) => { 44 | // Intercept the API call and make it fail 45 | await page.route('/api/dogs', route => { 46 | route.fulfill({ 47 | status: 500, 48 | contentType: 'application/json', 49 | body: JSON.stringify({ error: 'Internal Server Error' }) 50 | }); 51 | }); 52 | 53 | await page.goto('/'); 54 | 55 | // Check that error message is displayed 56 | await expect(page.getByText(/Failed to fetch data/)).toBeVisible({ timeout: 10000 }); 57 | }); 58 | }); -------------------------------------------------------------------------------- /.github/instructions/bicep-code-best-practices.instructions.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: 'Infrastructure as Code with Bicep' 3 | applyTo: '**/*.bicep' 4 | --- 5 | 6 | ## Naming Conventions 7 | 8 | - When writing Bicep code, use lowerCamelCase for all names (variables, parameters, resources) 9 | - Use resource type descriptive symbolic names (e.g., 'storageAccount' not 'storageAccountName') 10 | - Avoid using 'name' in a symbolic name as it represents the resource, not the resource's name 11 | - Avoid distinguishing variables and parameters by the use of suffixes 12 | 13 | ## Structure and Declaration 14 | 15 | - Always declare parameters at the top of files with @description decorators 16 | - Use latest stable API versions for all resources 17 | - Use descriptive @description decorators for all parameters 18 | - Specify minimum and maximum character length for naming parameters 19 | 20 | ## Parameters 21 | 22 | - Set default values that are safe for test environments (use low-cost pricing tiers) 23 | - Use @allowed decorator sparingly to avoid blocking valid deployments 24 | - Use parameters for settings that change between deployments 25 | 26 | ## Variables 27 | 28 | - Variables automatically infer type from the resolved value 29 | - Use variables to contain complex expressions instead of embedding them directly in resource properties 30 | 31 | ## Resource References 32 | 33 | - Use symbolic names for resource references instead of reference() or resourceId() functions 34 | - Create resource dependencies through symbolic names (resourceA.id) not explicit dependsOn 35 | - For accessing properties from other resources, use the 'existing' keyword instead of passing values through outputs 36 | 37 | ## Resource Names 38 | 39 | - Use template expressions with uniqueString() to create meaningful and unique resource names 40 | - Add prefixes to uniqueString() results since some resources don't allow names starting with numbers 41 | 42 | ## Child Resources 43 | 44 | - Avoid excessive nesting of child resources 45 | - Use parent property or nesting instead of constructing resource names for child resources 46 | 47 | ## Security 48 | 49 | - Never include secrets or keys in outputs 50 | - Use resource properties directly in outputs (e.g., storageAccount.properties.primaryEndpoints) 51 | 52 | ## Documentation 53 | 54 | - Include helpful // comments within your Bicep files to improve readability -------------------------------------------------------------------------------- /content/full-day/0-setup.md: -------------------------------------------------------------------------------- 1 | # Workshop setup 2 | 3 | | [← Modern DevOps with GitHub][walkthrough-previous] | [Next: Enable Code Scanning →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | To complete this workshop you will need to create a repository with a copy of the contents of this repository. While this can be done by [forking a repository][fork-repo], the goal of a fork is to eventually merge code back into the original (or upstream) source. In our case we want a separate copy as we don't intend to merge our changes. This is accomplished through the use of a [template repository][template-repo]. Template repositories are a great way to provide starters for your organization, ensuring consistency across projects. 7 | 8 | The repository for this workshop is configured as a template, so we can use it to create your repository. 9 | 10 | ## Create your repository 11 | Let's create the repository you'll use for your workshop. 12 | 13 | 1. Navigate to [the repository root][repo-root] 14 | 2. Select **Use this template** > **Create a new repository** 15 | ![Screenshot of Use this template dropdown](../1-hour/images/0-setup-template.png) 16 | 3. Under **Owner**, select the name of your GitHub handle, or the owner specified by your workshop leader. 17 | 4. Under **Repository**, set the name to **pets-workshop**, or the name specified by your workshop leader. 18 | 5. Ensure **Public** is selected for the visibility, or the value indicated by your workshop leader. 19 | 6. Select **Create repository from template**. 20 | ![Screenshot of configured template creation dialog](../1-hour/images/0-setup-configure.png) 21 | 22 | In a few moments a new repository will be created from the template for this workshop! 23 | 24 | ## Summary and next steps 25 | You've now created the repository you'll use for this workshop! Next let's [enable Code Scanning][walkthrough-next] to secure the code we write. 26 | 27 | | [← Modern DevOps with GitHub][walkthrough-previous] | [Next: Enable Code Scanning →][walkthrough-next] | 28 | |:-----------------------------------|------------------------------------------:| 29 | 30 | [fork-repo]: https://docs.github.com/en/get-started/quickstart/fork-a-repo 31 | [template-repo]: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository 32 | [repo-root]: / 33 | [walkthrough-previous]: README.md 34 | [walkthrough-next]: 1-code-scanning.md 35 | -------------------------------------------------------------------------------- /client/src/pages/about.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | import "../styles/global.css"; 4 | // about page 5 | --- 6 | 7 | 8 |
9 |
10 |

About Tailspin Shelter

11 | 12 |
13 |

14 | Nestled in the heart of Seattle, Tailspin Shelter is a warm, welcoming haven for dogs of all breeds and backgrounds. Founded in 2015 by a small group of dog lovers and rescue advocates, our mission is to give every dog a second chance at a happy, healthy life. Whether it's finding forever homes, providing medical care, or offering behavioral training, our dedicated team works tirelessly to ensure each dog's journey has a joyful destination. Inspired by Seattle's vibrant and compassionate community, we've grown from a small foster network into a full-service shelter and adoption center. 15 |

16 | 17 |

18 | The name "Tailspin" reflects both the whirlwind of emotions dogs often experience when they first arrive — and the joyful, spinning tails we see when they find their perfect match. For us, a "tailspin" is a reminder of the transformations we witness daily: from uncertainty and fear to trust, love, and boundless energy. It's this journey that fuels our work and reminds us why we do what we do. Every wagging tail is a testament to resilience and hope. 19 |

20 |
21 | 22 |
23 |

24 | Note: Tailspin Shelter is a fictional organization, created for this workshop. 25 |

26 |
27 | 28 | 36 |
37 |
38 |
-------------------------------------------------------------------------------- /scripts/start-app.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | . "$PSScriptRoot/common.ps1" 4 | 5 | # Store initial directory 6 | $InitialDir = Get-Location 7 | 8 | # Navigate to project root if needed 9 | Set-ProjectRoot 10 | 11 | Write-Host "Starting API (Flask) server..." 12 | 13 | # Get project root and initialize virtual environment 14 | $ProjectRoot = Get-ProjectRoot 15 | 16 | if (-not (Initialize-VirtualEnvironment -ProjectRoot $ProjectRoot)) { 17 | Write-Error "Failed to initialize virtual environment" 18 | Set-Location $InitialDir 19 | exit 1 20 | } 21 | 22 | # Install Python dependencies 23 | if (-not (Install-PythonDependencies -ProjectRoot $ProjectRoot)) { 24 | Write-Warning "Failed to install Python dependencies" 25 | Set-Location $InitialDir 26 | exit 1 27 | } 28 | 29 | # Setup Flask environment 30 | Set-FlaskEnvironment 31 | 32 | 33 | # Start Python server 34 | $serverWorkingDir = Join-Path $ProjectRoot "server" 35 | $pythonProcess = Start-ManagedProcess -FilePath "python" -WorkingDirectory $serverWorkingDir -ArgumentList @("app.py") -ProcessName "Flask server" 36 | 37 | if (-not $pythonProcess) { 38 | Write-Error "Failed to start Flask server" 39 | Set-Location $InitialDir 40 | exit 1 41 | } 42 | 43 | Write-Host "Starting client (Astro)..." 44 | 45 | # Install Node.js dependencies and start client 46 | $clientDir = Join-Path $ProjectRoot "client" 47 | if (-not (Install-NodeDependencies -Directory $clientDir)) { 48 | Write-Warning "Failed to install Node.js dependencies" 49 | } 50 | 51 | $npmCmd = Get-NpmCommand 52 | $clientProcess = Start-ManagedProcess -FilePath $npmCmd -WorkingDirectory $clientDir -ArgumentList @("run", "dev", "--", "--no-clearScreen") -ProcessName "Astro client" 53 | 54 | if (-not $clientProcess) { 55 | Write-Error "Failed to start Astro client" 56 | Stop-ManagedProcesses -Processes @($pythonProcess) -InitialDirectory $InitialDir 57 | exit 1 58 | } 59 | 60 | # Sleep for 5 seconds 61 | Start-Sleep -Seconds 5 62 | 63 | # Display the server URLs 64 | Write-Host "" 65 | Write-Success "Server (Flask) running at: http://localhost:5100" 66 | Write-Success "Client (Astro) server running at: http://localhost:4321" 67 | Write-Host "" 68 | Write-Host "Ctrl+C to stop the servers" 69 | 70 | # Function to handle cleanup 71 | function Cleanup { 72 | Stop-ManagedProcesses -Processes @($pythonProcess, $clientProcess) -InitialDirectory $InitialDir 73 | exit 74 | } 75 | 76 | # Register cleanup for script termination 77 | $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Cleanup } 78 | 79 | try { 80 | # Keep the script running until Ctrl+C 81 | Wait-Process -Id $pythonProcess.Id 82 | } finally { 83 | Cleanup 84 | } 85 | -------------------------------------------------------------------------------- /client/src/assets/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at opensource@github.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /server/test_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import patch, MagicMock 3 | import json 4 | from app import app # Changed from relative import to absolute import 5 | 6 | # filepath: server/test_app.py 7 | class TestApp(unittest.TestCase): 8 | def setUp(self): 9 | # Create a test client using Flask's test client 10 | self.app = app.test_client() 11 | self.app.testing = True 12 | # Turn off database initialization for tests 13 | app.config['TESTING'] = True 14 | 15 | def _create_mock_dog(self, dog_id, name, breed): 16 | """Helper method to create a mock dog with standard attributes""" 17 | dog = MagicMock(spec=['to_dict', 'id', 'name', 'breed']) 18 | dog.id = dog_id 19 | dog.name = name 20 | dog.breed = breed 21 | dog.to_dict.return_value = {'id': dog_id, 'name': name, 'breed': breed} 22 | return dog 23 | 24 | def _setup_query_mock(self, mock_query, dogs): 25 | """Helper method to configure the query mock""" 26 | mock_query_instance = MagicMock() 27 | mock_query.return_value = mock_query_instance 28 | mock_query_instance.join.return_value = mock_query_instance 29 | mock_query_instance.all.return_value = dogs 30 | return mock_query_instance 31 | 32 | @patch('app.db.session.query') 33 | def test_get_dogs_success(self, mock_query): 34 | """Test successful retrieval of multiple dogs""" 35 | # Arrange 36 | dog1 = self._create_mock_dog(1, "Buddy", "Labrador") 37 | dog2 = self._create_mock_dog(2, "Max", "German Shepherd") 38 | mock_dogs = [dog1, dog2] 39 | 40 | self._setup_query_mock(mock_query, mock_dogs) 41 | 42 | # Act 43 | response = self.app.get('/api/dogs') 44 | 45 | # Assert 46 | self.assertEqual(response.status_code, 200) 47 | 48 | data = json.loads(response.data) 49 | self.assertEqual(len(data), 2) 50 | 51 | # Verify first dog 52 | self.assertEqual(data[0]['id'], 1) 53 | self.assertEqual(data[0]['name'], "Buddy") 54 | self.assertEqual(data[0]['breed'], "Labrador") 55 | 56 | # Verify second dog 57 | self.assertEqual(data[1]['id'], 2) 58 | self.assertEqual(data[1]['name'], "Max") 59 | self.assertEqual(data[1]['breed'], "German Shepherd") 60 | 61 | # Verify query was called 62 | mock_query.assert_called_once() 63 | 64 | @patch('app.db.session.query') 65 | def test_get_dogs_empty(self, mock_query): 66 | """Test retrieval when no dogs are available""" 67 | # Arrange 68 | self._setup_query_mock(mock_query, []) 69 | 70 | # Act 71 | response = self.app.get('/api/dogs') 72 | 73 | # Assert 74 | self.assertEqual(response.status_code, 200) 75 | data = json.loads(response.data) 76 | self.assertEqual(data, []) 77 | 78 | @patch('app.db.session.query') 79 | def test_get_dogs_structure(self, mock_query): 80 | """Test the response structure for a single dog""" 81 | # Arrange 82 | dog = self._create_mock_dog(1, "Buddy", "Labrador") 83 | self._setup_query_mock(mock_query, [dog]) 84 | 85 | # Act 86 | response = self.app.get('/api/dogs') 87 | 88 | # Assert 89 | data = json.loads(response.data) 90 | self.assertTrue(isinstance(data, list)) 91 | self.assertEqual(len(data), 1) 92 | self.assertEqual(set(data[0].keys()), {'id', 'name', 'breed'}) 93 | 94 | 95 | if __name__ == '__main__': 96 | unittest.main() -------------------------------------------------------------------------------- /client/src/components/DogDetails.svelte: -------------------------------------------------------------------------------- 1 | 50 | 51 | {#if loading} 52 |
53 |
54 |
55 |
56 |
57 |
58 | {:else if error} 59 |
60 | {error} 61 |
62 | {:else if dogData} 63 |
64 |
65 |
66 |

{dogData.name}

67 | {#if dogData.status === 'AVAILABLE'} 68 | Available 69 | {:else if dogData.status === 'PENDING'} 70 | Pending Adoption 71 | {:else} 72 | Adopted 73 | {/if} 74 |
75 | 76 |
77 |
78 |

Breed: {dogData.breed}

79 |
80 |
81 |

Age: {dogData.age} {dogData.age === 1 ? 'year' : 'years'}

82 |
83 |
84 |

Gender: {dogData.gender}

85 |
86 |
87 | 88 |

About {dogData.name}

89 |

{dogData.description}

90 |
91 |
92 | {:else} 93 |
94 |

No dog information available

95 |
96 | {/if} -------------------------------------------------------------------------------- /content/full-day/README.md: -------------------------------------------------------------------------------- 1 | # Modern DevOps with GitHub 2 | 3 | | [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | [DevOps][devops] is a [portmanteau][portmanteau] of **development** and **operations**. At its core is a desire to bring development practices more inline with operations, and operations practices more inline with development. This fosters better communication and collaboration between teams, breaks down barriers, and gives everyone an investment in ensuring customers are delighted by the software we ship. 7 | 8 | This workshop is built to help guide you through some of the most common DevOps tasks on GitHub. You'll explore: 9 | 10 | - Managing projects with [GitHub Issues][github-issues] 11 | - Creating a development environment with [GitHub Codespaces][github-codespaces] 12 | - Using [GitHub Copilot][github-copilot] as your AI pair programmer 13 | - Securing the development pipeline with [GitHub Advanced Security][github-security] 14 | - Automating tasks and CI/CD with [GitHub Actions][github-actions] 15 | 16 | ## Prerequisites 17 | 18 | The application for the workshop uses is built primarily with Python (Flask and SQLAlchemy) and Astro (using Tailwind and Svelte). While experience with these frameworks and languages is helpful, you'll be using Copilot to help you understand the project and generate the code. As a result, as long as you are familiar with programming you'll be able to complete the exercises! 19 | 20 | ## Required resources 21 | 22 | To complete this workshop, you will need the following: 23 | 24 | - A [GitHub account][github-signup] 25 | - Access to [GitHub Copilot][github-copilot] 26 | 27 | ## Getting started 28 | 29 | Ready to get started? Let's go! The workshop scenario imagines you as a developer volunteering your time for a pet adoption center. You will work through the process of creating a development environment, creating code, enabling security, and automating processes. 30 | 31 | 0. [Setup your environment][walkthrough-next] for the workshop 32 | 1. [Enable Code Scanning][code-scanning] to ensure new code is secure 33 | 2. [Create an issue][issues] to document a feature request 34 | 3. [Create a codespace][codespaces] to start writing code 35 | 4. [Implement testing][testing] to supplement continuous integration 36 | 5. [Provide Copilot context][context] to generate quality code suggestions 37 | 6. [Add features to your app][code] with GitHub Copilot 38 | 7. [Use the GitHub flow][github-flow] to incorporate changes into your codebase 39 | 8. [Deploy your application][deployment] to Azure to make your application available to users 40 | 41 | ## Check out these resources to dive in and learn more 42 | Check out the resources in [**GitHub-Copilot-Resources.md**][GitHub-Copilot-Resources]. 43 | 44 | This resource list has been carefully curated to help you to learn more about GitHub Copilot, how to use it effectively, what is coming in the future and more. There are even YouTube playlists that include the latest videos from the GitHub Developer Relations team and others from GitHub. 45 | 46 | | [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | 47 | |:-----------------------------------|------------------------------------------:| 48 | 49 | [code]: ./6-code.md 50 | [code-scanning]: ./1-code-scanning.md 51 | [codespaces]: ./3-codespaces.md 52 | [context]: ./5-context.md 53 | [deployment]: ./8-deployment.md 54 | [devops]: https://en.wikipedia.org/wiki/DevOps 55 | [github-actions]: https://github.com/features/actions 56 | [github-codespaces]: https://github.com/features/codespaces 57 | [github-copilot]: https://github.com/features/copilot 58 | [github-flow]: ./7-github-flow.md 59 | [github-issues]: https://github.com/features/issues 60 | [github-security]: https://github.com/features/security 61 | [github-signup]: https://github.com/join 62 | [issues]: ./2-issues.md 63 | [portmanteau]: https://www.merriam-webster.com/dictionary/portmanteau 64 | [testing]: ./4-testing.md 65 | [walkthrough-next]: ./0-setup.md 66 | [walkthrough-previous]: ../README.md 67 | [GitHub-Copilot-Resources]: ../GitHub-Copilot-Resources.md 68 | -------------------------------------------------------------------------------- /content/1-hour/2-explore-project.md: -------------------------------------------------------------------------------- 1 | # Helping GitHub Copilot understand context 2 | 3 | | [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Providing custom instructions →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | The key to success when coding (and much of life) is context. Before we add code to a codebase, we want to understand the rules and structures already in place. When working with an AI coding assistant such as GitHub Copilot the same concept applies - the quality of suggestion is directly proportional to the context Copilot has. Let's use this opportunity to both explore the project we've been given and how to interact with Copilot to ensure it has the context it needs to do its best work. 7 | 8 | ## Scenario 9 | 10 | Before adding new functionality to the website, you want to explore the existing structure to determine where the updates need to be made. 11 | 12 | ## Chat participants and extensions 13 | 14 | GitHub Copilot Chat has a set of available [chat participants][chat-participants] and [extensions][copilot-extensions] available to you to both provide instructions to GitHub Copilot and access external services. Chat participants are helpers which work inside your IDE and have access to your project, while extensions can call external services and provide information to you without having to open separate tools. We're going to focus on one core chat participant - `@workspace`. 15 | 16 | `@workspace` creates an index of your project and allows you to ask questions about what you're currently working on, to find resources inside the project, or add it to the context. It's best to use this when the entirety of your project should be considered or you're not entirely sure where you should start looking. In our current scenario, since we want to ask questions about our project, `@workspace` is the perfect tool for the job. 17 | 18 | > [!NOTE] 19 | > This exercise doesn't provide specific prompts to type, as part of the learning experience is to discover how to interact with Copilot. Feel free to talk in natural language, describing what you're looking for or need to accomplish. 20 | 21 | 1. Return to your IDE with the project open. 22 | 2. Close any tabs you may have open in your IDE to ensure the context for Copilot chat is empty. 23 | 3. Open GitHub Copilot Chat. 24 | 4. Select the `+` icon towards the top of Copilot chat to begin a new chat. 25 | 5. Type `@workspace` in the chat prompt window and hit tab to select or activate it, then continue by asking Copilot about your project. You can ask what technologies are in use, what the project does, where functionality resides, etc. 26 | 6. Spend a few minutes exploring to find the answers to the following questions: 27 | - Where's the database the project uses? 28 | - What files are involved in listing dogs? 29 | 30 | ## Summary and next steps 31 | 32 | You've explored context in GitHub Copilot, which is key to generating quality suggestions. You saw how you can use chat participants to help guide GitHub Copilot, and how with natural language you can explore the project. Let's see how we can provide even more context to Copilot chat through the use of [Copilot instructions][walkthrough-next]. 33 | 34 | ## Resources 35 | 36 | - [Copilot Chat cookbook][copilot-cookbook] 37 | - [Use Copilot Chat in VS Code][copilot-chat-vscode] 38 | - [Copilot extensions marketplace][copilot-marketplace] 39 | 40 | | [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Providing custom instructions →][walkthrough-next] | 41 | |:-----------------------------------|------------------------------------------:| 42 | 43 | [chat-participants]: https://code.visualstudio.com/docs/copilot/copilot-chat#_chat-participants 44 | [copilot-chat-vscode]: https://code.visualstudio.com/docs/copilot/copilot-chat 45 | [copilot-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook 46 | [copilot-extensions]: https://docs.github.com/en/copilot/using-github-copilot/using-extensions-to-integrate-external-tools-with-copilot-chat 47 | [copilot-marketplace]: https://github.com/marketplace?type=apps&copilot_app=true 48 | [walkthrough-previous]: ./1-add-endpoint.md 49 | [walkthrough-next]: ./3-copilot-instructions.md -------------------------------------------------------------------------------- /client/src/components/DogList.svelte: -------------------------------------------------------------------------------- 1 | 34 | 35 |
36 |

Available Dogs

37 | 38 | {#if loading} 39 | 40 |
41 | {#each Array(6) as _, i} 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | {/each} 52 |
53 | {:else if error} 54 | 55 |
56 |

{error}

57 |
58 | {:else if dogs.length === 0} 59 | 60 |
61 |

No dogs available at the moment.

62 |
63 | {:else} 64 | 65 | 87 | {/if} 88 |
-------------------------------------------------------------------------------- /content/1-hour/README.md: -------------------------------------------------------------------------------- 1 | # Getting started with GitHub Copilot 2 | 3 | | [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | Built to be your AI pair programmer, [GitHub Copilot][copilot] helps you generate code and focus on what's important. Through the use of code completion you can create code from comments, and functions from just a signature. With Copilot chat you can ask questions about your codebase, create new files and update existing ones, and even perform operations which update files across your entire codebase. 7 | 8 | As with any tool, there are a set of skills which need to be acquired, which is the purpose of this (roughly) one hour workshop. You'll explore the most common workloads available to you by exploring and updating an existing application to add functionality. 9 | 10 | ## Prerequisites 11 | 12 | The application for the workshop uses is built primarily with Python (Flask and SQLAlchemy) and Astro (using Tailwind and Svelte). While experience with these frameworks and languages is helpful, you'll be using Copilot to help you understand the project and generate the code. As a result, as long as you are familiar with programming you'll be able to complete the exercises! 13 | 14 | > [!NOTE] 15 | > When in doubt, you can always highlight a block of code you're unfamiliar with and ask GitHub Copilot chat for an explanation! 16 | 17 | ## Required resources 18 | 19 | To complete this workshop, you will need the following: 20 | 21 | - A [GitHub account][github-account]. 22 | - Access to [GitHub Copilot][copilot] (which is available for free for individuals!) 23 | 24 | ## Required local installation 25 | 26 | You will also need the following available and installed locally: 27 | 28 | ### Code editor 29 | 30 | - [Visual Studio Code][vscode-link]. 31 | - [Copilot extension installed in your IDE][copilot-extension]. 32 | 33 | ### Local services 34 | 35 | - A recent [Node.js runtime][nodejs-link]. 36 | - A recent version of [Python][python-link]. 37 | - For Windows, you can install [Python via the Windows store](https://apps.microsoft.com/detail/9pjpw5ldxlz5?hl=en-US&gl=US). 38 | - The [git CLI][git-link]. 39 | - A shell capable of running BASH commands. 40 | 41 | > [!NOTE] 42 | > Linux and macOS are able to run BASH commands without additional configuration. For Windows, you will need either [Windows Subsystem for Linux (WS)][windows-subsystem-linux] or the BASH shell available via [git][git-link]. 43 | 44 | ## Getting started 45 | 46 | Ready to get started? Let's go! The workshop scenario imagines you as a developer volunteering your time for a pet adoption center. You've been asked to add a filter to the website to allow people to limit their search results by breed and adoption status. You'll work over the next 5 exercises to perform the tasks! 47 | 48 | 0. [Clone the repository and start the app][walkthrough-next] for the workshop. 49 | 1. [Add an endpoint to the server][stage-1] to list all breeds. 50 | 2. [Explore the project][stage-2] to get a better understanding of what needs to be done. 51 | 3. [Create custom instructions][stage-3] to ensure Copilot chat has additional context. 52 | 4. [Add the new feature][stage-4] to the website, and ensure it works! 53 | 54 | ## Check out these resources to dive in and learn more 55 | Check out the resources in [**GitHub-Copilot-Resources.md**][GitHub-Copilot-Resources]. 56 | 57 | This resource list has been carefully curated to help you to learn more about GitHub Copilot, how to use it effectively, what is coming in the future and more. There are even YouTube playlists that include the latest videos from the GitHub Developer Relations team and others from GitHub. 58 | 59 | | [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | 60 | |:-----------------------------------|------------------------------------------:| 61 | 62 | [copilot]: https://github.com/features/copilot 63 | [copilot-extension]: https://docs.github.com/en/copilot/managing-copilot/configure-personal-settings/installing-the-github-copilot-extension-in-your-environment 64 | [git-link]: https://git-scm.com/ 65 | [github-account]: https://github.com/join 66 | [nodejs-link]: https://nodejs.org/en 67 | [python-link]: https://www.python.org/ 68 | [stage-1]: ./1-add-endpoint.md 69 | [stage-2]: ./2-explore-project.md 70 | [stage-3]: ./3-copilot-instructions.md 71 | [stage-4]: ./4-add-feature.md 72 | [walkthrough-previous]: ../README.md 73 | [walkthrough-next]: ./0-setup.md 74 | [windows-python-link]: https://apps.microsoft.com/detail/9pjpw5ldxlz5 75 | [windows-subsystem-linux]: https://learn.microsoft.com/en-us/windows/wsl/about 76 | [vscode-link]: https://code.visualstudio.com/ 77 | [GitHub-Copilot-Resources]: ../GitHub-Copilot-Resources.md 78 | -------------------------------------------------------------------------------- /content/1-hour/0-setup.md: -------------------------------------------------------------------------------- 1 | # Workshop setup 2 | 3 | | [← Getting started with GitHub Copilot][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | To complete this workshop you will need to create a repository with a copy of the contents of this repository. While this can be done by [forking a repository][fork-repo], the goal of a fork is to eventually merge code back into the original (or upstream) source. In our case we want a separate copy as we don't intend to merge our changes. This is accomplished through the use of a [template repository][template-repo]. Template repositories are a great way to provide starters for your organization, ensuring consistency across projects. 7 | 8 | The repository for this workshop is configured as a template, so we can use it to create your repository. 9 | 10 | > [!IMPORTANT] 11 | > Ensure you have the [requisite software][required-software] and [requisite resources][required-resources] setup. 12 | 13 | ## Create your repository 14 | 15 | Let's create the repository you'll use for your workshop. 16 | 17 | 1. Navigate to [the repository root](/) 18 | 2. Select **Use this template** > **Create a new repository** 19 | 20 | ![Screenshot of Use this template dropdown](images/0-setup-template.png) 21 | 22 | 3. Under **Owner**, select the name of your GitHub handle, or the owner specified by your workshop leader. 23 | 4. Under **Repository**, set the name to **pets-workshop**, or the name specified by your workshop leader. 24 | 5. Ensure **Public** is selected for the visibility, or the value indicated by your workshop leader. 25 | 6. Select **Create repository from template**. 26 | 27 | ![Screenshot of configured template creation dialog](images/0-setup-configure.png) 28 | 29 | In a few moments a new repository will be created from the template for this workshop! 30 | 31 | ## Clone the repository and start the app 32 | 33 | With the repository created, it's now time to clone the repository locally. We'll do this from a shell capable of running BASH commands. 34 | 35 | 1. Copy the URL for the repository you just created in the prior set. 36 | 2. Open your terminal or command shell. 37 | 3. Run the following command to clone the repository locally (changing directories to a parent directory as appropriate): 38 | 39 | ```sh 40 | git clone 41 | ``` 42 | 43 | 4. Change directories into the cloned repository by running the following command: 44 | 45 | ```sh 46 | cd 47 | ``` 48 | 49 | 5. Start the application by running the script appropriate for your operating system: 50 | 51 | - macOS / Linux: 52 | 53 | ```sh 54 | ./scripts/start-app.sh 55 | ``` 56 | 57 | - Windows (PowerShell): 58 | 59 | ```powershell 60 | ./scripts/start-app.ps1 61 | ``` 62 | 63 | If you encounter execution policy warnings on Windows, run PowerShell as an administrator or execute the script with an explicit bypass, for example: 64 | 65 | ```powershell 66 | powershell -ExecutionPolicy Bypass -File .\scripts\start-app.ps1 67 | ``` 68 | 69 | The startup script will start two applications: 70 | 71 | - The backend Flask app on [localhost:5100][flask-url]. You can see a list of dogs by opening the [dogs API][dogs-api]. 72 | - The frontend Astro/Svelte app on [localhost:4321][astro-url]. You can see the [website][website-url] by opening that URL. 73 | 74 | ## Open your editor 75 | 76 | With the code cloned locally, and the site running, let's open the codebase up in VS Code. 77 | 78 | 1. Open VS Code. 79 | 2. Select **File** > **Open Folder**. 80 | 3. Navigate to the folder which contains the project you cloned earlier in this exercise. 81 | 4. With the folder highlighted, select **Open folder**. 82 | 83 | ## Summary and next steps 84 | 85 | You've now cloned the repository you'll use for this workshop and have your IDE setup! Next let's [add a new endpoint to the server][walkthrough-next]! 86 | 87 | 88 | | [← Getting started with GitHub Copilot][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | 89 | |:-----------------------------------|------------------------------------------:| 90 | 91 | [astro-url]: http://localhost:4321 92 | [dogs-api]: http://localhost:5100/api/dogs 93 | [flask-url]: http://localhost:5100 94 | [fork-repo]: https://docs.github.com/en/get-started/quickstart/fork-a-repo 95 | [required-resources]: ./README.md#required-resources 96 | [required-software]: ./README.md#required-local-installation 97 | [template-repo]: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository 98 | [walkthrough-previous]: README.md 99 | [walkthrough-next]: ./1-add-endpoint.md 100 | [website-url]: http://localhost:4321 101 | -------------------------------------------------------------------------------- /scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Color codes 4 | export GREEN='\033[0;32m' 5 | export NC='\033[0m' # No Color 6 | 7 | # Get the project root directory 8 | get_project_root() { 9 | local index="${1:-0}" 10 | local script_dir="$(cd "$(dirname "${BASH_SOURCE[$index]}")" && pwd)" 11 | echo "$script_dir/.." 12 | } 13 | 14 | # Get the appropriate Python command based on OS 15 | get_python_command() { 16 | if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 17 | echo "py" 18 | else 19 | echo "python3" 20 | fi 21 | } 22 | 23 | # Create and activate virtual environment 24 | setup_virtual_env() { 25 | local project_root="$1" 26 | local python_cmd=$(get_python_command) 27 | 28 | cd "$project_root" || { 29 | echo "Error: Could not navigate to project root: $project_root" 30 | return 1 31 | } 32 | 33 | # Create virtual environment if it doesn't exist 34 | if [[ ! -d "venv" ]]; then 35 | echo "Creating virtual environment..." 36 | $python_cmd -m venv venv 37 | fi 38 | 39 | # Activate virtual environment 40 | if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 41 | source venv/Scripts/activate || . venv/Scripts/activate 42 | else 43 | source venv/bin/activate || . venv/bin/activate 44 | fi 45 | 46 | if [[ $? -ne 0 ]]; then 47 | echo "Error: Could not activate virtual environment" 48 | return 1 49 | fi 50 | 51 | echo "Virtual environment activated" 52 | return 0 53 | } 54 | 55 | # Install Python dependencies 56 | install_python_deps() { 57 | local project_root="$1" 58 | 59 | if [[ -f "$project_root/server/requirements.txt" ]]; then 60 | echo "Installing Python dependencies..." 61 | pip install -r "$project_root/server/requirements.txt" 62 | return $? 63 | else 64 | echo "Warning: requirements.txt not found at $project_root/server/requirements.txt" 65 | return 1 66 | fi 67 | } 68 | 69 | # Navigate to server directory safely 70 | navigate_to_server() { 71 | local project_root="$1" 72 | local initial_dir="$2" 73 | 74 | cd "$project_root/server" || { 75 | echo "Error: server directory not found" 76 | if [[ -n "$initial_dir" ]]; then 77 | cd "$initial_dir" 78 | fi 79 | return 1 80 | } 81 | return 0 82 | } 83 | 84 | # Run Python script with appropriate command 85 | run_python_script() { 86 | local script_path="$1" 87 | local python_cmd=$(get_python_command) 88 | 89 | if [[ -f "$script_path" ]]; then 90 | echo "Running Python script: $script_path" 91 | $python_cmd "$script_path" 92 | return $? 93 | else 94 | echo "Error: Python script not found: $script_path" 95 | return 1 96 | fi 97 | } 98 | 99 | # Cleanup function for process management 100 | cleanup_processes() { 101 | local server_pid="$1" 102 | local client_pid="$2" 103 | local initial_dir="$3" 104 | 105 | echo "Shutting down servers..." 106 | 107 | # Kill processes and their child processes 108 | if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 109 | [[ -n "$server_pid" ]] && taskkill //F //T //PID $server_pid 2>/dev/null 110 | [[ -n "$client_pid" ]] && taskkill //F //T //PID $client_pid 2>/dev/null 111 | else 112 | # Send SIGTERM first to allow graceful shutdown 113 | [[ -n "$server_pid" ]] && kill -TERM $server_pid 2>/dev/null 114 | [[ -n "$client_pid" ]] && kill -TERM $client_pid 2>/dev/null 115 | 116 | # Wait briefly for graceful shutdown 117 | sleep 2 118 | 119 | # Then force kill if still running 120 | if [[ -n "$server_pid" ]] && ps -p $server_pid > /dev/null 2>&1; then 121 | pkill -P $server_pid 2>/dev/null 122 | kill -9 $server_pid 2>/dev/null 123 | fi 124 | 125 | if [[ -n "$client_pid" ]] && ps -p $client_pid > /dev/null 2>&1; then 126 | pkill -P $client_pid 2>/dev/null 127 | kill -9 $client_pid 2>/dev/null 128 | fi 129 | fi 130 | 131 | # Deactivate virtual environment if active 132 | if [[ -n "${VIRTUAL_ENV}" ]]; then 133 | deactivate 134 | fi 135 | 136 | # Return to initial directory 137 | if [[ -n "$initial_dir" ]]; then 138 | cd "$initial_dir" 139 | fi 140 | } 141 | 142 | # Setup common environment variables for Flask 143 | setup_flask_env() { 144 | export FLASK_DEBUG=1 145 | export FLASK_PORT=5100 146 | } 147 | 148 | # Print colored message 149 | print_success() { 150 | local message="$1" 151 | echo -e "${GREEN}${message}${NC}" 152 | } 153 | 154 | # Check if running from scripts directory and navigate to project root 155 | ensure_project_root() { 156 | if [[ $(basename $(pwd)) == "scripts" ]]; then 157 | cd .. 158 | fi 159 | } -------------------------------------------------------------------------------- /server/utils/seed_database.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | import sys 4 | import random 5 | from datetime import datetime, timedelta 6 | from collections import defaultdict 7 | 8 | # Add the parent directory to sys.path to allow importing from models 9 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | from flask import Flask 12 | from models import init_db, db, Breed, Dog 13 | from models.dog import AdoptionStatus 14 | 15 | def create_app(): 16 | """Create and configure Flask app for database operations""" 17 | app = Flask(__name__) 18 | 19 | # Get the server directory path (one level up from utils) 20 | server_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 21 | 22 | app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(server_dir, "dogshelter.db")}' 23 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 24 | 25 | # Initialize the database with the app 26 | init_db(app) 27 | 28 | return app 29 | 30 | def create_breeds(): 31 | """Seed the database with breeds from the CSV file""" 32 | app = create_app() 33 | 34 | # Path to the CSV file 35 | csv_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 36 | 'models', 'breeds.csv') 37 | 38 | with app.app_context(): 39 | # Check if breeds already exist 40 | existing_breeds = Breed.query.count() 41 | if existing_breeds > 0: 42 | print(f"Database already contains {existing_breeds} breeds. Skipping seed.") 43 | return 44 | 45 | # Read the CSV file and add breeds to the database 46 | with open(csv_path, 'r') as file: 47 | csv_reader = csv.DictReader(file) 48 | for row in csv_reader: 49 | breed = Breed(name=row['Breed'], description=row['Description']) 50 | db.session.add(breed) 51 | 52 | # Commit the changes 53 | db.session.commit() 54 | 55 | # Verify the seeding 56 | breed_count = Breed.query.count() 57 | print(f"Successfully seeded {breed_count} breeds to the database.") 58 | 59 | def create_dogs(): 60 | """Seed the database with dogs from the CSV file, ensuring at least 3 dogs per breed""" 61 | app = create_app() 62 | 63 | # Path to the CSV file 64 | csv_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 65 | 'models', 'dogs.csv') 66 | 67 | with app.app_context(): 68 | # Check if dogs already exist 69 | existing_dogs = Dog.query.count() 70 | if existing_dogs > 0: 71 | print(f"Database already contains {existing_dogs} dogs. Skipping seed.") 72 | return 73 | 74 | # Get all breeds from the database 75 | breeds = Breed.query.all() 76 | if not breeds: 77 | print("No breeds found in database. Please seed breeds first.") 78 | return 79 | 80 | # Track how many dogs are assigned to each breed 81 | breed_counts = defaultdict(int) 82 | 83 | # Read the CSV file 84 | dogs_data = [] 85 | with open(csv_path, 'r') as file: 86 | csv_reader = csv.DictReader(file) 87 | for row in csv_reader: 88 | dogs_data.append(row) 89 | 90 | def create_dog(dog_info, breed_id): 91 | """Helper function to create a dog with consistent attributes""" 92 | dog = Dog( 93 | name=dog_info['Name'], 94 | description=dog_info['Description'], 95 | breed_id=breed_id, 96 | age=int(dog_info['Age']), 97 | gender=dog_info['Gender'], 98 | status=random.choice(list(AdoptionStatus)), 99 | intake_date=datetime.now() - timedelta(days=random.randint(1, 365)) 100 | ) 101 | db.session.add(dog) 102 | breed_counts[breed_id] += 1 103 | return dog 104 | 105 | # First pass: assign at least 3 dogs to each breed 106 | for breed in breeds: 107 | # Get 3 random dogs that haven't been assigned yet 108 | for _ in range(3): 109 | if not dogs_data: 110 | break 111 | 112 | dog_info = random.choice(dogs_data) 113 | dogs_data.remove(dog_info) 114 | 115 | create_dog(dog_info, breed.id) 116 | 117 | # Second pass: assign remaining dogs randomly 118 | for dog_info in dogs_data: 119 | breed = random.choice(breeds) 120 | create_dog(dog_info, breed.id) 121 | 122 | # Commit all the changes 123 | db.session.commit() 124 | 125 | # Verify the seeding 126 | dog_count = Dog.query.count() 127 | print(f"Successfully seeded {dog_count} dogs to the database.") 128 | 129 | # Print distribution of dogs across breeds 130 | for breed in breeds: 131 | count = breed_counts[breed.id] 132 | print(f"Breed '{breed.name}': {count} dogs") 133 | 134 | def seed_database(): 135 | """Run all seeding functions in the correct order""" 136 | create_breeds() 137 | create_dogs() 138 | 139 | if __name__ == '__main__': 140 | seed_database() -------------------------------------------------------------------------------- /scripts/common.ps1: -------------------------------------------------------------------------------- 1 | # Define color codes for PowerShell 2 | $Script:Green = [System.ConsoleColor]::Green 3 | $Script:DefaultColor = [System.ConsoleColor]::White 4 | 5 | # Get the project root directory 6 | function Get-ProjectRoot { 7 | return (Get-Item $PSScriptRoot).Parent.FullName 8 | } 9 | 10 | # Create and activate virtual environment 11 | function Initialize-VirtualEnvironment { 12 | param( 13 | [string]$ProjectRoot 14 | ) 15 | 16 | $VenvPath = Join-Path $ProjectRoot "venv" 17 | 18 | # Create virtual environment if it doesn't exist 19 | if (-not (Test-Path $VenvPath)) { 20 | Write-Host "Creating virtual environment..." 21 | python -m venv $VenvPath 22 | } 23 | 24 | # Activate virtual environment 25 | try { 26 | if ($IsWindows) { 27 | & "$VenvPath/Scripts/Activate.ps1" 28 | } else { 29 | & bash -c "source '$VenvPath/bin/activate'" 30 | } 31 | Write-Host "Virtual environment activated" 32 | return $true 33 | } catch { 34 | Write-Warning "Failed to activate virtual environment: $_" 35 | return $false 36 | } 37 | } 38 | 39 | # Install Python dependencies 40 | function Install-PythonDependencies { 41 | param( 42 | [string]$ProjectRoot 43 | ) 44 | 45 | $RequirementsPath = Join-Path $ProjectRoot "server/requirements.txt" 46 | 47 | if (Test-Path $RequirementsPath) { 48 | Write-Host "Installing Python dependencies..." 49 | pip install -r $RequirementsPath 50 | return $LASTEXITCODE -eq 0 51 | } else { 52 | Write-Warning "Requirements file not found at $RequirementsPath; skipping pip install." 53 | return $false 54 | } 55 | } 56 | 57 | # Run Python script safely 58 | function Invoke-PythonScript { 59 | param( 60 | [string]$ScriptPath, 61 | [string]$WorkingDirectory = $null 62 | ) 63 | 64 | if (Test-Path $ScriptPath) { 65 | Write-Host "Running Python script: $ScriptPath" 66 | if ($WorkingDirectory) { 67 | $oldLocation = Get-Location 68 | Set-Location $WorkingDirectory 69 | } 70 | 71 | try { 72 | & python $ScriptPath 73 | return $LASTEXITCODE -eq 0 74 | } finally { 75 | if ($WorkingDirectory) { 76 | Set-Location $oldLocation 77 | } 78 | } 79 | } else { 80 | Write-Warning "Python script not found at $ScriptPath; skipping execution." 81 | return $false 82 | } 83 | } 84 | 85 | # Setup Flask environment variables 86 | function Set-FlaskEnvironment { 87 | $env:FLASK_DEBUG = 1 88 | $env:FLASK_PORT = 5100 89 | } 90 | 91 | # Start a process with proper error handling 92 | function Start-ManagedProcess { 93 | param( 94 | [string]$FilePath, 95 | [string]$WorkingDirectory, 96 | [string[]]$ArgumentList, 97 | [string]$ProcessName 98 | ) 99 | 100 | try { 101 | Write-Host "Starting $ProcessName..." 102 | $process = Start-Process $FilePath ` 103 | -WorkingDirectory $WorkingDirectory ` 104 | -ArgumentList $ArgumentList ` 105 | -PassThru ` 106 | -NoNewWindow 107 | 108 | return $process 109 | } catch { 110 | Write-Warning "Failed to start $ProcessName : $_" 111 | return $null 112 | } 113 | } 114 | 115 | # Cleanup function for process management 116 | function Stop-ManagedProcesses { 117 | param( 118 | [System.Diagnostics.Process[]]$Processes, 119 | [string]$InitialDirectory 120 | ) 121 | 122 | Write-Host "Shutting down servers..." 123 | 124 | foreach ($process in $Processes) { 125 | if ($process -and -not $process.HasExited) { 126 | try { 127 | Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue 128 | } catch { 129 | Write-Warning "Failed to stop process $($process.Id): $_" 130 | } 131 | } 132 | } 133 | 134 | # Deactivate virtual environment if it exists 135 | if (Test-Path Function:\deactivate) { 136 | deactivate 137 | } 138 | 139 | # Return to initial directory 140 | if ($InitialDirectory) { 141 | Set-Location $InitialDirectory 142 | } 143 | } 144 | 145 | # Print colored success message 146 | function Write-Success { 147 | param([string]$Message) 148 | Write-Host $Message -ForegroundColor $Script:Green 149 | } 150 | 151 | # Navigate to project root if in scripts directory 152 | function Set-ProjectRoot { 153 | if ((Split-Path -Path (Get-Location) -Leaf) -eq "scripts") { 154 | Set-Location .. 155 | } 156 | } 157 | 158 | # Get appropriate NPM command based on OS 159 | function Get-NpmCommand { 160 | if ($IsWindows) { 161 | return "npm.cmd" 162 | } else { 163 | return "npm" 164 | } 165 | } 166 | 167 | # Install Node.js dependencies 168 | function Install-NodeDependencies { 169 | param( 170 | [string]$Directory 171 | ) 172 | 173 | $oldLocation = Get-Location 174 | try { 175 | Set-Location $Directory -ErrorAction Stop 176 | Write-Host "Installing Node.js dependencies in $Directory..." 177 | npm install 178 | return $LASTEXITCODE -eq 0 179 | } catch { 180 | Write-Warning "Failed to install Node.js dependencies: $_" 181 | return $false 182 | } finally { 183 | Set-Location $oldLocation 184 | } 185 | } -------------------------------------------------------------------------------- /content/1-hour/5-bonus.md: -------------------------------------------------------------------------------- 1 | # Bonus content 2 | 3 | | [← Add the filter feature][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | ## Overview of Copilot Agent Mode 7 | 8 | With chat agent mode in Visual Studio Code, you can use natural language to define a high-level task and to start an agentic code editing session to accomplish that task. In agent mode, Copilot **autonomously** plans the work needed and determines the relevant files and context. It then makes edits to your codebase and invokes tools to accomplish the request you made. Agent mode monitors the outcome of edits and tools and iterates to resolve any issues that arise. 9 | 10 | > [!IMPORTANT] 11 | > While Copilot autonomously determines the operations necessary to complete the requested task, as the developer you are always in charge. You will work with Copilot to ensure everything is completely correctly, reading and reviewing the code. You will also want to continue to follow proper DevOps practices, including code reviews, testing, security scans, etc. 12 | 13 | Why would you use agent mode instead of edit mode? 14 | 15 | - **Edit scope**: agent mode autonomously determines the relevant context and files to edit. In edit mode, you need to specify the context yourself. 16 | - **Task complexity**: agent mode is better suited for complex tasks that require not only code edits but also the invocation of tools and terminal commands. 17 | - **Duration**: agent mode involves multiple steps to process a request, so it might take longer to get a response. For example, to determine the relevant context and files to edit, determine the plan of action, and more. 18 | - **Self-healing**: agent mode evaluates the outcome of the generated edits and might iterate multiple times to resolve intermediate issues. 19 | - **Request quota**: in agent mode, depending on the complexity of the task, one prompt might result in many requests to the backend. 20 | 21 | ### How it works 22 | 23 | ![How agent mode works](./images/copilot-agent-mode-how-it-works.png) 24 | 25 | ## Add themes to the Tailspin Shelter website 26 | 27 | In this section, you will use Copilot's agent mode to add themes to the Tailspin Shelter website. You will be able to select a theme and apply it to the website. 28 | 29 | 1. Return to your IDE with the project open. 30 | 2. Close any tabs you may have open in your IDE to ensure the context for Copilot chat is empty. 31 | 3. Select the `+` icon towards the top of Copilot chat to begin a new chat. 32 | 4. Select agent mode, by selecting `Agent` (just like you did `Edit` before) in the model selector dropdown at the bottom of the chat window. 33 | 5. Select one of models (some may not be available) `Claude 3.7 Sonnet`, `Claude 3.5 Sonnet` or `GPT-4.1 (Preview)` 34 | 6. Navigate to the [prompt file](../prompts/fun-add-themes.md) for this task. 35 | 7. Copy the content of the prompt 36 | 8. Paste the content in the copilot prompt input 37 | 9. The agent mode will take its time, since it searches by itself the relevant files to modify, and then do multiple passes including talking with itself to refine the task at hand 38 | 10. While Agent is doing it's thing, take the opportunity to examine the content of prompt that was used. 39 | 11. When the agent is done (you no longer see any spinners and the thumb up/down icons will be visible), open a browser to see the results 40 | - Open the page at [http://localhost:4321][tailspin-shelter-website] to see the updates! 41 | - Examine the changes made to the files if you like 42 | - Was it good? If you are not happy with the results, you can refine the prompt by crafting extra prompts in the chat to improve the end results. Don't start a new session, it's an interactive process. 43 | 12. Press `Done` when you are happy with the results 44 | 45 | You _may_ have gotten something like this for the Terminal Theme (generated with claude 3.7) 46 | 47 | ![Tailspin Shelter Terminal Classic theme](images/tail-spin-shelter-terminal-theme.png) 48 | 49 | > [!IMPORTANT] 50 | > Because LLMs are probabilistic, not deterministic, the exact code generated can vary. The above is a representative example. If your code is different, that's just fine as long as it works! 51 | 52 | ## Play a bit with Copilot 53 | 54 | You've made it to the end of the one hour workshop. Congratulations! You've explored the core skills to help you get the most out of GitHub Copilot. From here you can explore various challenges on your own, and see how GitHub Copilot can support you as you continue developing. 55 | 56 | The suggestions listed here are exactly that - suggestions. You're free to come up with your own scenarios or features you think the application should have. 57 | 58 | You'll also notice there aren't step-by-step instructions here. You've already seen how you can use Copilot to aid you in development. Part of the challenge put forth with these extra suggestions is to apply what you've learned to create code! 59 | 60 | ### Some prompts to play with 61 | 62 | We have provided you some prompts in [prompts][github-prompts-path] folder, which you can use directly as inspiration for your explorations. 63 | 64 | > [!TIP] 65 | > These prompts are meant to be used as one shot, but if have prompts that can be are generic, reusable prompt are a great way to share prompts with the team members. They can be placed in a well know folder and be invoked directly in the Copilot Chat by referencing them. 66 | > Learn more about [reusable prompts in Visual Studio Code][vscode-prompts] 67 | 68 | ### Potential next steps 69 | 70 | Here's some ideas of how you could continue to grow and build upon what you've done: 71 | 72 | - Return to the API endpoints you updated previously in Flask and add unit tests. 73 | - Add paging support to the full list of dogs or any results page with more than 5 results. 74 | - Add a form to allow a user to apply to adopt a dog if the dog is available. 75 | - Add a form to allow users to register a dog they found. 76 | 77 | | [← Add the filter feature][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | 78 | |:-----------------------------------|------------------------------------------:| 79 | 80 | [walkthrough-previous]: ./4-add-feature.md 81 | [walkthrough-next]: ../README.md 82 | [tailspin-shelter-website]: http://localhost:4321 83 | [github-prompts-path]: ../prompts/ 84 | [vscode-prompts]: https://aka.ms/vscode-ghcp-prompt-snippets -------------------------------------------------------------------------------- /content/GitHub-Copilot-Resources.md: -------------------------------------------------------------------------------- 1 | # GitHub Copilot Resources 2 | 3 | Checkout the resources below to dive in and learn more about [GitHub Copilot](https://gh.io/copilot). 4 | 5 | ## Getting started 6 | 7 | New to GitHub Copilot? Start here! 8 | 9 | - [GitHub Copilot - Your AI pair programmer](https://github.com/features/copilot) - See all that GitHub Copilot can do. This feature summary highlights all that you can do with GitHub Copilot. See a comparison of what is available in each pricing plan. 10 | - [How AI can make you an awesome developer](https://github.com/orgs/community/discussions/153056) - Staying relevant in this era of AI requires not only adapting to new technologies, but also honing in on your skills. It is extremely relevant to address the elephant in the room, how AI is not going to replace us, but make us much better developers. Let’s explore five key strategies to help you stay relevant and thrive in this new era of AI-driven development. 11 | - [Essential GitHub Copilot resources for enterprise teams](https://resources.github.com/enterprise/essential-copilot-resources/) - GitHub Resources - We've gathered everything enterprise teams need to hit the ground running with GitHub Copilot. From initial setup to advanced features, this guide will walk you through the essential resources to make your Copilot implementation successful. 12 | 13 | ## Documentation 14 | 15 | [GitHub Copilot Documentation](https://docs.github.com/en/copilot) contains a robust collection of articles to help you get the most out of the tool. Some key articles to start with include: 16 | 17 | - [Prompt engineering for GitHub Copilot](https://docs.github.com/en/copilot/using-github-copilot/prompt-engineering-for-github-copilot) - A prompt is a request that you make to GitHub Copilot. For example, a question that you ask Copilot Chat, or a code snippet that you ask Copilot to complete. In addition to your prompt, Copilot uses additional context, like the code in your current file and the chat history, to generate a response. Follow the tips in this article to write prompts that generate better responses from Copilot. 18 | - [Asking GitHub Copilot questions in GitHub.com](https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/asking-github-copilot-questions-in-githubcom#asking-exploratory-questions-about-a-repository) – See how you can use GitHub Copilot Chat in GitHub.com to answer general questions about software development, or specific questions about the code, issues, security alerts, pull requests, etc. in a repository. For example: open a specific file and ask Copilot, “How could I improve this code?”. Trying to understand a new codebase? Copilot can help with that. You can ask Copilot questions to help quickly understand the structure and key components of repositories. For example, “What does the code in this repo do? What is the tech stack?. 19 | - [Copilot Chat Cookbook](https://docs.github.com/en/copilot/example-prompts-for-github-copilot-chat) - Find examples of prompts to use with GitHub Copilot Chat. 20 | - [Changing the AI model for Copilot Chat](https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/ai-models/changing-the-ai-model-for-copilot-chat) & [Changing the AI model for Copilot code completions](https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/ai-models/changing-the-ai-model-for-copilot-code-completion) - You are not limited to using the default models for Copilot chat and code completions. You can choose from a selection of other models, each with its own particular strengths. You may have a favorite model that you like to use, or you might prefer to use a particular model for inquiring about a specific subject. Here are some notable recent updates: 21 | 22 | ## Copilot in VS Code 23 | 24 | As you're exploring using VS Code in this workshop, here are some articles particular to using [GitHub Copilot in VS Code](https://code.visualstudio.com/docs/copilot/overview): 25 | 26 | - [Context for Code Completion](https://code.visualstudio.com/docs/copilot/ai-powered-suggestions#_context) - Get more out of GitHub Copilot by understanding how it uses context from multiple locations in VS Code to provide more relevant suggestions. 27 | - [Making Copilot Chat an expert in your workspace](https://code.visualstudio.com/docs/copilot/workspace-context) - Referencing @workspace in Copilot Chat lets you ask questions about your entire codebase. Based on the question, Copilot intelligently retrieves relevant files and symbols, which it then references in its answer as links and code examples. Grounded in @workspace references, Copilot Chat becomes a domain expert for tasks like: 28 | - Finding existing code in your codebase 29 | - Making plans for complex code edits 30 | - Explaining higher-level concepts in a codebase 31 | - [Best Practices / Prompt Crafting](https://code.visualstudio.com/docs/copilot/prompt-crafting) - This article covers best practices for using GitHub Copilot in Visual Studio Code by using prompt crafting and providing the right context to GitHub Copilot. 32 | 33 | ## Videos 34 | 35 | The [GitHub YouTube channel](https://www.youtube.com/@GitHub/videos) hosts many videos highlighting the latest features: 36 | 37 | - [GitHub Copilot Playlist](http://gh.io/GitHub-Copilot-on-YouTube) for **GitHub Copilot** demos and informational videos. 38 | - [GitHub for Beginners](https://www.youtube.com/playlist?list=PL0lo9MOBetEFcp4SCWinBdpml9B2U25-f) - Season 2 of **GitHub for Beginners** is focused on **GitHub Copilot**. 39 | 40 | ## Other resources 41 | 42 | Continue your journey: 43 | 44 | - [Essentials of GitHub Copilot - GitHub Resources](https://resources.github.com/learn/pathways/copilot/essentials/essentials-of-github-copilot/) - In this learning pathway module, we’ll cover the most common questions about GitHub Copilot, and we’ll hear from engineering leaders at the top organizations about how they use GitHub Copilot to accelerate the pace of software development and deliver more value to their customers. This has resources for developers and leaders. 45 | - [GitHub Copilot product updates](https://github.blog/changelog/label/copilot) - We are continually adding capabilities and improving GitHub Copilot. Check out the **GitHub Changelog** to stay up to date on everything we ship. 46 | - [The GitHub Blog](https://github.blog/tag/github-copilot) Be sure to check out the most recent GitHub Copilot related blog posts. 47 | - [GitHub Copilot Discussions](https://github.com/orgs/community/discussions/categories/copilot) - Share your feedback, feature suggestions, etc. via **GitHub public feedback discussions** and influence what we’re building. 48 | -------------------------------------------------------------------------------- /content/full-day/2-issues.md: -------------------------------------------------------------------------------- 1 | # Project management with GitHub Issues 2 | 3 | | [← Securing the development pipeline][walkthrough-previous] | [Next: Cloud-based development with GitHub Codespaces →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | "URL or it didn't happen" is a common mantra at GitHub, which is used to highlight the importance of documenting the development process. Feature requests should have a history; who made the request, what was the rationale, who was involved in the process, what decisions were made, why were they made, was the feature implemented, how was it implemented... All of this information helps provide context to both drive future decisions and avoid repeating old mistakes. 7 | 8 | GitHub provides various features to enable collaboration and project management, including [GitHub Discussions][discussions], [wikis][wikis], [pull requests][about-prs] and [GitHub Issues][issues]. Each of these can help your organization drive the creation process. We're going to focus on GitHub Issues, which is the foundation of project management on GitHub. Issues can also be linked to [milestones](https://docs.github.com/issues/using-labels-and-milestones-to-track-work/about-milestones) and [Projects](https://docs.github.com/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects), helping organize them into a broad roadmap. 9 | 10 | At their core, issues document some form of an action. They can be a request for a feature, a bug report, or another operation taken by the team. There's no prescribed methodology for using GitHub Issues, allowing your team to determine the best way to manage and drive your projects. A common flow teams will implement on issues is: 11 | 12 | 1. File an issue to request a new feature or file a bug report. 13 | 1. Discuss the issue, and determine the correct people and mechanism to resolve the request. 14 | 1. Create a pull request with a proposed implementation of the request. 15 | 1. Further discuss and review the pull request. 16 | 1. Once everyone is satisfied and has signed off, merge the pull request and close the issue. 17 | 18 | Issues can sometimes seem too big, or often we experience 'scope-creep' in a task. Issues can be broken down into [sub-issues](https://docs.github.com/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues) that allow smaller issues to be linked together, especially when working on dependencies. For example, you might have a feature request that includes a list of subtasks that make the task a bit too large for your sprint or for the work required. Using sub-issues allows you to cleanly define all dependent tasks into more manageable items of work. 19 | 20 | To further track work in an issue, the right-hand sidebar of any issue is where you can manage metadata and organizational details. You can assign the issue to yourself or various team members, apply labels for categorization (i.e "bug", "enhancement", "documentation"), allowing you to link your issue to a milestone, connect it to a GitHub Project board, or mark it as part of an epic/initiative. If there are linked discussions or pull requests you can track them from this panel. This side panel makes it easier to triage issues and keep them aligned with the project's workflow. 21 | 22 | GitHub Issues also come with some very handy shortcuts and productivity hacks: 23 | 24 | - Typing `#` in a comment or description lets you reference another issue or pull request by number. 25 | - Use Markdown to format text, add checklists (- [ ]), code snippets, or images. 26 | - Pressing `g` then `i` quickly takes you to the Issues tab from anywhere in a repository. 27 | - Typing `@username` mentions someone, notifying them directly. 28 | - Filter issues in the search bar with queries like `is:open label:bug assignee:@me` to quickly find relevant ones. 29 | 30 | ## Scenario 31 | 32 | The shelter wants to begin pushing new features to the website. They want to start by displaying the hours for the current day on the landing page. There's also a need to make updates to help support development and DevOps for both current and future updates. You want to track these updates to document the work being done. You'll do this by creating issues in the repository. 33 | 34 | ## Creating issues to manage feature requests 35 | 36 | Our project needs two main updates. We want to make the updates to support development for our project, and add a new component to the website to display the shelter's hours. Let's create the issues for each of these. In the next few exercises we'll begin making the appropriate updates to our project to resolve these requests. 37 | 38 | 1. Return to the repository you created at the beginning of this workshop. 39 | 1. Select the **Issues** tab. 40 | 1. Select **New issue**. 41 | 2. If prompted for type, select **Blank issue**. 42 | 3. Select **Create more** at the bottom of the page to streamline the creation process. 43 | 4. Create new issues by adding the information indicated in the table below, selecting **Submit new issue** after creating each one: 44 | 45 | | Title | Description | 46 | | ----------------------- | ------------------------------------------------------------------------------ | 47 | | Define codespace | Create the necessary definitions for the codespace to enable cloud development | 48 | | Implement testing | Create a workflow to automate testing for continuous integration | 49 | | Add filters to dog list | Add the code to allow users to filter for dogs by breed and availability | 50 | 51 | > [!TIP] 52 | > You can also save an issue by pressing Ctl - Enter (or Cmd - Return on a Mac) in the title or description fields. 53 | 54 | You've now defined all the issues for the workshop! You'll use these issues to help guide your progress through the workshop. 55 | 56 | ## Summary and next steps 57 | GitHub Issues are the core to project management on GitHub. Their flexibility allows your organization to determine the best course of action to support your development lifecycle's methodology. With your issues created, it's time to turn your attention to the first big change to the project, [defining a codespace][walkthrough-next]. 58 | 59 | ## Resources 60 | - [GitHub Issues][issues-docs] 61 | - [Communicate using markdown][skills-markdown] 62 | - [GitHub Projects][projects-docs] 63 | 64 | | [← Securing the development pipeline][walkthrough-previous] | [Next: Cloud-based development with GitHub Codespaces →][walkthrough-next] | 65 | |:-----------------------------------|------------------------------------------:| 66 | 67 | [discussions]: https://github.com/features/discussions 68 | [wikis]: https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis 69 | [about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests 70 | [issues]: https://github.com/features/issues 71 | [issues-docs]: https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues 72 | [projects-docs]: https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects 73 | [skills-markdown]: https://github.com/skills/communicate-using-markdown 74 | [walkthrough-next]: 3-codespaces.md 75 | [walkthrough-previous]: 1-code-scanning.md -------------------------------------------------------------------------------- /content/1-hour/1-add-endpoint.md: -------------------------------------------------------------------------------- 1 | # Coding with GitHub Copilot 2 | 3 | | [← Workshop setup][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | 7 | With code completions, GitHub Copilot provides suggestions in your code editor while you're coding. This can turn comments into code, generate the next line of code, and generate an entire function just from a signature. Code completion helps reduce the amount of boilerplate code and ceremony you need to type, allowing you to focus on the important aspects of what you're creating. 8 | 9 | ## Scenario 10 | 11 | It's standard to work in phases when adding functionality to an application. Given that we know we want to allow users to filter the list of dogs based on breed, we'll need to add an endpoint to provide a list of all breeds. Later we'll add the rest of the functionality, but let's focus on this part for now. 12 | 13 | The application uses a Flask app with SQLAlchemy as the backend API (in the [/server][server-code] folder), and an Astro app with Svelte as the frontend (in the [/client][client-code] folder). You will explore more of the project later; this exercise will focus solely on the Flask application. 14 | 15 | > [!NOTE] 16 | > As you begin making changes to the application, there is always a chance a breaking change could be created. If the page stops working, check the terminal window you used previously to start the application for any error messages. You can stop the app by using Ctl+C, and restart it by running the script appropriate for your operating system: `./scripts/start-app.sh` (macOS / Linux) or `./scripts/start-app.ps1` (Windows PowerShell). 17 | 18 | ## Flask routes 19 | 20 | While we won't be able to provide a full overview of [routing in Flask][flask-routing], they are defined by using the Python decorator `@app.route`. There are a couple of parameters you can provide to `@app.route`, including the path (or URL) one would use to access the route (such as **api/breeds**), and the [HTTP method(s)][http-methods] which can be used. 21 | 22 | ## Code completion 23 | 24 | Code completion predicts the next block of code you're about to type based on the context Copilot has. For code completion, this includes the file you're currently working on and any tabs open in your IDE. 25 | 26 | Code completion is best for situations where you know what you want to do, and are more than happy to just start writing code with a bit of a helping hand along the way. Suggestions will be generated based both on the code you write (say a function definition) and comments you add to your code. 27 | 28 | ## Create the breeds endpoint 29 | 30 | Let's build our new route in our Flask backend with the help of code completion. 31 | 32 | > [!IMPORTANT] 33 | > For this exercise, **DO NOT** copy and paste the code snippet provided, but rather type it manually. This will allow you to experience code completion as you would if you were coding back at your desk. You'll likely see you only have to type a few characters before GitHub Copilot begins suggesting the rest. 34 | 35 | 1. Return to your IDE with the project open. 36 | 2. Open **server/app.py**. 37 | 3. Locate the comment which reads `## HERE`, which should be at line 68. 38 | 4. Delete the comment to ensure there isn't any confusion for Copilot, and leave your cursor there. 39 | 5. Begin adding the code to create the route to return all breeds from an endpoint of **api/breeds** by typing the following: 40 | 41 | ```python 42 | @app.route('/api/breeds', methods=['GET']) 43 | ``` 44 | 45 | 6. Once you see the full function signature, select Tab to accept the code suggestion. 46 | 7. If it didn't already, code completion should then suggest the remainder of the function signature; just as before select Tab to accept the code suggestion. 47 | 48 | The code generated should look a little like this: 49 | 50 | ```python 51 | @app.route('/api/breeds', methods=['GET']) 52 | def get_breeds(): 53 | # Query all breeds 54 | breeds_query = db.session.query(Breed.id, Breed.name).all() 55 | 56 | # Convert the result to a list of dictionaries 57 | breeds_list = [ 58 | { 59 | 'id': breed.id, 60 | 'name': breed.name 61 | } 62 | for breed in breeds_query 63 | ] 64 | 65 | return jsonify(breeds_list) 66 | ``` 67 | 68 | > [!IMPORTANT] 69 | > Because LLMs are probabilistic, not deterministic, the exact code generated can vary. The above is a representative example. If your code is different, that's just fine as long as it works! 70 | 71 | 8. Add a comment to the newly created function. To do this, place your cursor inside the function (anywhere between the lines `def get_breeds...` and `return jsonify...`). Then, press Ctl+I (or cmd+I on a Mac) to open the editor inline chat. In the input box, type `/doc`. (You can optionally provide additional details, but it's not required). This will prompt GitHub Copilot to generate a documentation comment for the function. The suggested comment will appear inline in the code (highlighted in green). Click **Accept** to apply the comment to your code, or click **Close** to discard the suggestion. You just used a slash command, a shortcut to streamline a task, these commands eliminate the need for verbose prompts. 72 | 73 | 9. **Save** the file. 74 | 75 | ## Validate the endpoint 76 | 77 | With the code created and saved, let's quickly validate the endpoint to ensure it works. 78 | 79 | 1. Navigate to [http://localhost:5100/api/breeds][breeds-endpoint] to validate the route. You should see JSON displayed which contains the list of breeds! 80 | 81 | ## Summary and next steps 82 | 83 | You've added a new endpoint with the help of GitHub Copilot! You saw how Copilot predicted the next block of code you were likely looking for and provided the suggestion inline, helping save you the effort of typing it out manually. Let's start down the path of performing more complex operations by [exploring our project][walkthrough-next]. 84 | 85 | ## Resources 86 | 87 | - [Code suggestions in your IDE with GitHub Copilot][copilot-suggestions] 88 | - [Code completions with GitHub Copilot in VS Code][vscode-copilot] 89 | - [Prompt crafting][prompt-crafting] 90 | - [Inline chat][inline-chat] 91 | 92 | 93 | | [← Workshop setup][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | 94 | |:-----------------------------------|------------------------------------------:| 95 | 96 | [breeds-endpoint]: http://localhost:5100/api/breeds 97 | [client-code]: /client/ 98 | [copilot-suggestions]: https://docs.github.com/en/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot 99 | [flask-routing]: https://flask.palletsprojects.com/en/stable/quickstart/#routing 100 | [http-methods]: https://www.w3schools.com/tags/ref_httpmethods.asp 101 | [prompt-crafting]: https://code.visualstudio.com/docs/copilot/prompt-crafting 102 | [inline-chat]: https://code.visualstudio.com/docs/copilot/chat/inline-chat 103 | [server-code]: /server/ 104 | [vscode-copilot]: https://code.visualstudio.com/docs/copilot/ai-powered-suggestions 105 | [walkthrough-previous]: ./0-setup.md 106 | [walkthrough-next]: ./2-explore-project.md -------------------------------------------------------------------------------- /content/1-hour/4-add-feature.md: -------------------------------------------------------------------------------- 1 | # Add the filter feature 2 | 3 | | [← Providing custom instructions][walkthrough-previous] | [Next: Bonus content →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | We've explored how we can use GitHub Copilot to explore our project and to provide context to ensure the suggestions we receive are to the quality we expect. Now let's turn our attention to putting all this prep work into action by generating new code! We'll use GitHub Copilot to aid us in adding functionality to our website. 7 | 8 | ## Scenario 9 | 10 | The website currently lists all dogs in the database. While this was appropriate when the shelter only had a few dogs, as time has gone on the number has grown and it's difficult for people to sift through who's available to adopt. The shelter has asked you to add filters to the website to allow a user to select a breed of dog and only display dogs which are available for adoption. 11 | 12 | ## Copilot Edits 13 | 14 | Previously we utilized Copilot chat, which is great for working with an individual file or asking questions about our code. However, many updates necessitate changes to multiple files throughout a codebase. Even a seemingly basic change to a webpage likely requires updating HTML, CSS and JavaScript files. Copilot Edits allows you to modify multiple files at once. 15 | 16 | With Copilot Edits, you will add the files which need to be updated to the context. Once you provide the prompt, Copilot Edits will begin the updates across all files in the context. It also has the ability to create new files or add files to the context as it deems appropriate. 17 | 18 | ## Add the filters to the dog list page 19 | 20 | Adding the filters to the page will require updating a minimum of two files - the Flask backend and the Svelte frontend. Fortunately, Copilot Edits can update multiple files! Let's get our page updated with the help of Copilot Edits. 21 | 22 | > [!NOTE] 23 | > Because Copilot Edits works best with auto-save enabled, we'll activate it. As we'll explore a little later in this exercise, Copilot Edits provides powerful tools to undo any changes you might not wish to keep. 24 | 25 | 1. Return to your IDE with your project open. 26 | 2. Close any tabs you have open inside your IDE. 27 | 3. Enable Auto Save by selecting **File** > **Auto Save**. 28 | 4. Open GitHub Copilot Chat. 29 | 5. Switch to edit mode by selecting **Edit** in the chat mode dropdown at the bottom of Chat view (should be currently **Ask**) 30 | 6. If available, select **Claude 3.5 Sonnet** from the list of available models 31 | 7. Select **Add Context...** in the chat window. 32 | 8. Select **server/app.py** and **client/src/components/DogList.svelte** files (you need to select **Add context** for each file) 33 | > [!TIP] 34 | > If you type the file names after clicking **Add context**, they will show up in the filter. You can also drag the files or right click file in explorer and select `Copilot -> Add File to Chat`) 35 | 9. Ask Copilot to generate the update you want to the page, which is to add filters for both dog breed and if dogs are available for adoption. Use your own phrasing, ensuring the following requirements are met: 36 | - A dropdown list should be provided with all breeds 37 | - A checkbox should be available to only show available dogs 38 | - The page should automatically refresh whenever a change is made 39 | 40 | > [!NOTE] 41 | > You should use your own phrasing when generating the prompt. As highlighted previously, part of the exercise is to become comfortable creating prompts for GitHub Copilot. One key tip is it's always good to provide more guidance to ensure you get the code you are looking for. 42 | 43 | Copilot begins generating the suggestions! 44 | 45 | ## Reviewing the suggestions 46 | 47 | Unlike our prior examples where we worked with an individual file, we're now working with changes across multiple files - and maybe multiple sections of multiple files. Fortunately, Copilot Edits has functionality to help streamline this process. 48 | 49 | GitHub Copilot will propose the following changes: 50 | 51 | - Update the endpoint to list all dogs to accept parameters for breed and availability. 52 | - Update the webpage to include the dropdown list and checkbox. 53 | 54 | As the code is generated, you will notice the files are displayed using an experience similar to diff files, with the new code highlighted in green and old code highlighted in red (by default). 55 | 56 | If you open an individual file, you can keep or undo changes by using the buttons provided. 57 | 58 | ![Screenshot of keep/undo interface for an individual file](./images/copilot-edits-keep-undo-file.png) 59 | 60 | You can also keep or undo all changes made. 61 | 62 | ![Screenshot of keep/discard interface on the chat window](./images/copilot-edits-keep-undo-global.png) 63 | 64 | And 65 | 66 | 1. Review the code suggestions to ensure they behave the way you expect them to, making any necessary changes. Once you're satisfied, you can select **Keep** on the files individually or in Copilot Chat to accept all changes. 67 | 2. Open the page at [http://localhost:4321][tailspin-shelter-website] to see the updates! 68 | 3. Run the Python tests by using `python -m unittest` in the terminal as you did previously. 69 | 4. If any changes are needed, explain the required updates to GitHub Copilot and allow it to generate the new code. 70 | 71 | > [!IMPORTANT] 72 | > Working iteratively a normal aspect of coding with an AI pair programmer. You can always provide more context to ensure Copilot understands, make additional requests, or rephrase your original prompts. To aid you in working iteratively, you will notice undo and redo buttons towards the top of the Copilot Edits interface, which allow you to move back and forth across prompts. 73 | > 74 | > ![Screenshot of the undo/redo buttons](./images/copilot-edits-history.png) 75 | 76 | 5. Confirm the functionality works as expected, then select **Keep** to accept all the changes. 77 | 6. Optional: Disable Auto Save by unselecting **File** > **Auto Save**. 78 | 79 | ## Summary 80 | 81 | You've worked with GitHub Copilot to add new features to the website - the ability to filter the list of dogs. With the help of Copilot Edits, you updated multiple files across the project, and iteratively built the desired functionality. 82 | 83 | ## Workshop review 84 | 85 | Over the course of the workshop you explore the core functionality of GitHub Copilot. You saw how to use code completion to get inline suggestions, chat participants to explore your project, Copilot instructions to add context, and Copilot Edits to update multiple files. 86 | 87 | There is no one right way to use GitHub Copilot. Continue to explore and try different prompts to discover what works best for your workflow and how GitHub Copilot can aid your productivity. 88 | 89 | ## Resources 90 | 91 | - [Asking GitHub Copilot questions in your IDE][copilot-ask] 92 | - [Copilot Chat cookbook][copilot-cookbook] 93 | - [Copilot Edits][copilot-edits] 94 | 95 | | [← Providing custom instructions][walkthrough-previous] | [Next: Bonus content →][walkthrough-next] | 96 | |:-----------------------------------|------------------------------------------:| 97 | 98 | [copilot-ask]: https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-your-ide 99 | [copilot-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook 100 | [copilot-edits]: https://code.visualstudio.com/docs/copilot/copilot-edits 101 | [tailspin-shelter-website]: http://localhost:4321 102 | [walkthrough-previous]: ./3-copilot-instructions.md 103 | [walkthrough-next]: ./5-bonus.md 104 | -------------------------------------------------------------------------------- /content/full-day/7-github-flow.md: -------------------------------------------------------------------------------- 1 | # GitHub flow 2 | 3 | | [← Add new functionality][walkthrough-previous] | [Next: Deploy the application →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | The [GitHub flow][github-flow] is a lightweight, [branch-based][about-branches] workflow. It's designed to allow for free testing and exploration of ideas and novel approaches which are then reviewed and, if accepted, brought into the codebase. At a high level, the GitHub flow follows this pattern: 7 | 8 | 1. Create a branch 9 | 1. Make the desired changes 10 | 1. Create a [pull request][about-prs] 11 | 1. Review changes, gather feedback and make updates 12 | 1. Review results of automated operations such as testing for continuous integration 13 | 1. If changes are approved, merge into codebase 14 | 15 | The GitHub flow is designed to work as a cycle, where contributors continuously explore, test, review, and build upon their work and the work of others. 16 | 17 | > [!NOTE] 18 | > One key philosophy for GitHub flow is not every pull request needs to be merged. Sometimes exploration is the goal, the feature isn't one which is desired by the greater team, or wholesale changes need to be made necessitating starting over. This is part of the process, and allows for free experimentation. 19 | 20 | ## Scenario 21 | 22 | With the code changes created in the [prior exercise][code-exercise], it's time to walk through the GitHub flow to create a pull request and incorporate the updates into the codebase. While the changes have already been made (meaning we are slightly out of order from the "traditional" flow), you can still perform the steps to explore. 23 | 24 | ## Creating a branch 25 | 26 | A [branch][about-branches] is a copy of the code stored in the same repository. By using branches to test updates you have a safe space to explore while keeping all code in the same repository. 27 | 28 | There are different ways to create a branch when using [GitHub Codespaces][github-codespaces]. You can utilize the command-line to run [git](https://git-scm.com/docs/git-branch) commands. You can use the Source Control pane in your codespace to get the support of the UI for creating your branch. In our example we're going to use the command-line to create the branch. 29 | 30 | 1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. 31 | 2. Open a **terminal window** by pressing Ctl + `. 32 | 3. In the terminal window, enter the following command to create and switch to a new branch named `add-filter`: 33 | 34 | ```bash 35 | git checkout -b add-filter 36 | ``` 37 | 38 | 4. Stage all code to be committed to the new branch by entering the following command in the terminal window: 39 | 40 | ```bash 41 | git add . 42 | ``` 43 | 44 | 5. Let Copilot generate a commit message by selecting the **Quick fix** icon (represented by sparkles) and **Generate Commit Message**. 45 | 46 | ![Screenshot of the quick fix menu with Generate Commit Message selected](./images/7-generate-commit-message.png). 47 | 48 | 6. Press enter to run the command. 49 | 7. Finally, push the new branch to the repository by entering the following command in the terminal window: 50 | 51 | ```bash 52 | git push -u origin add-filter 53 | ``` 54 | 55 | ## Create the pull request to suggest updates 56 | 57 | A [pull request][about-prs] is a request to pull or incorporate new code into the existing codebase. When a pull request is made it's customary to have other team members review the code and make comments, and for [CI/CD][cicd-resources] processes to run. Once everything is completed and the code is in a stage where everyone has signed-off, it's then merged into the codebase. 58 | 59 | Pull requests can be made through the source control pane in the codespace, the repository's website, or through the command-line using the [GitHub CLI][github-cli]. In our example we're going to create the pull request in the CLI, then navigate to the website to see the pull request and the actions running, and merge the code into the codebase. 60 | 61 | 1. Return to your codespace. 62 | 1. Find the number for the [issue you created earlier][issues-exercise] titled **Add component to display hours** by entering the following command in the terminal window: 63 | 64 | ```bash 65 | gh issue list 66 | ``` 67 | 68 | 1. Create a pull request with the title **Add hours component** and body **Resolves #\**, replacing **\** with the issue number you obtained in the previous step by entering the following command in the terminal window: 69 | 70 | ```bash 71 | gh pr create -t "Add hours component" -b "Resolves #" 72 | ``` 73 | 74 | ## Explore and merge the pull request 75 | 76 | When the pull request is created, you will see a link appear to the page for the pull request. From there you can add comments, see any workflows running, and decide to close or merge the pull request. You can also see any workflows associated with the pull request run. 77 | 78 | In our scenario, we created an automated workflow for front-end tests for our application, which runs whenever a push or pull request is made to `main`. We also enabled [code scanning][security-exercise], which was set to run on the same triggers. We've just created a pull request, which will cause both of those workflows to run! 79 | 80 | Let's explore the pull request and watch the workflows run. We'll ensure the tests now run successfully and, assuming they do, merge the pull request. 81 | 82 | 1. Follow the link displayed in the terminal window by using Ctl - **Click** (or Cmd - **Click** on a Mac). 83 | 1. In the page displayed, note the workflow running the [end-to-end tests created earlier][testing-exercise] and [code scanning][security-exercise]. 84 | 1. When the workflows complete successfully, select **Merge pull request** to merge your changes into the **main** branch. 85 | 86 | Congratulations! You've now used the GitHub flow to suggest changes, perform a review, and merge those into your codebase. 87 | 88 | ## Summary and next steps 89 | 90 | The GitHub flow is a workflow for managing changes and incorporating new features into a codebase. GitHub flow gives you the freedom to explore and experiment, while ensuring all code follows a validation process before being merged. Let's get our [application deployed][walkthrough-next]. 91 | 92 | ## Resources 93 | 94 | - [GitHub flow][github-flow] 95 | - [GitHub Skills: Review pull requests][skills-review-prs] 96 | - [GitHub Skills: Release based workflow][skills-release-workflow] 97 | 98 | | [← Add new functionality][walkthrough-previous] | [Next: Deploy the application →][walkthrough-next] | 99 | |:-----------------------------------|------------------------------------------:| 100 | 101 | [about-branches]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches 102 | [about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests 103 | [cicd-resources]: https://resources.github.com/ci-cd/ 104 | [code-exercise]: ./6-code.md 105 | [github-codespaces]: https://github.com/features/codespaces 106 | [github-cli]: https://cli.github.com/ 107 | [github-flow]: https://docs.github.com/en/get-started/quickstart/github-flow 108 | [issues-exercise]: ./2-issues.md 109 | [security-exercise]: ./1-code-scanning.md 110 | [skills-review-prs]: https://github.com/skills/review-pull-requests 111 | [skills-release-workflow]: https://github.com/skills/release-based-workflow 112 | [testing-exercise]: ./4-testing.md 113 | [walkthrough-previous]: 6-code.md 114 | [walkthrough-next]: 8-deployment.md 115 | -------------------------------------------------------------------------------- /content/full-day/1-code-scanning.md: -------------------------------------------------------------------------------- 1 | # Securing the development pipeline 2 | 3 | | [← Workshop setup][walkthrough-previous] | [Next: Project management with GitHub Issues →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | Ensuring code security is imperative in today's environment. When we think about how we create code today, there's three main areas to focus on: 7 | 8 | - The code we write 9 | - The code we use through libraries and packages 10 | - The credentials needed to access services 11 | 12 | To help support developers and security teams, [GitHub Advanced Security][advanced-security] provides a suite of tools which cover these focus areas. Code Scanning will check the code you write, Dependabot ensures the libraries you use are secure, and Secret Scanning looks for any keys or tokens which are checked into code. 13 | 14 | Let's explore each of these, and enable them on our repository. We'll see them in action when we create a pull request with new code later in the workshop. 15 | 16 | ## Scenario 17 | 18 | Security is important in every application. By detecting potential vulnerabilities early, teams are able to make updates before infiltrations occur. To help secure the website, the shelter wants to update the repository to ensure insecure code and libraries are detected as early as possible. You'll enable Dependabot, secret scanning, and code scanning to meet these needs. 19 | 20 | ## Dependabot 21 | 22 | Most projects take dependencies on open source and other external libraries. While modern development would seemingly be impossible without these resources, we always need to ensure the dependencies we take are secure. [Dependabot][dependabot-quickstart] will look at the dependencies your repository has and raise alerts or even create [pull requests][about-prs] (PRs) to update your dependencies to a secure version. 23 | 24 | ### Configuring Dependabot 25 | 26 | Public repositories on GitHub automatically have Dependabot alerts. This feature will generate alerts whenever an insecure package is detected, and generate an alert. Let's configure Dependabot to create PRs to update a library's version when an insecure one is detected. 27 | 28 | 1. Navigate to the repository you created for this workshop. 29 | 1. Select the **Settings** tab. 30 | 2. On the left side, select **Code security**. 31 | 3. Locate the **Dependabot** section towards the middle of the page: 32 | 33 | ![Screenshot of the dependabot section](./images/1-dependabot.png) 34 | 35 | 4. Select **Enable** next to **Dependabot security updates** to configure Dependabot to create PRs to resolve alerts. 36 | 37 | You have now enabled Dependabot alerts and security updates! Should an insecure library be detected, you will both receive an alert, and Dependabot will create a new pull request to update the version number to a secure version of the library. 38 | 39 | > [!IMPORTANT] 40 | > After enabling Dependabot security updates you may notice new [pull requests][about-prs] created for potentially outdated packages. For this workshop you can ignore these pull requests. 41 | 42 | ## Secret scanning 43 | 44 | Many developers have checked in code with a token or username and passwords. Sometimes this is because the developer was trying to take a shortcut, sometimes it was because they didn't know the proper mechanism to secure the key, and sometimes it was done under the assumption they'll clean it up later but never do. 45 | 46 | Regardless of the reason, even seemingly innocuous tokens can create a security issue. We always want to take care to not publish tokens and keys, and detect any issues as quickly as possible. Secret scanning is built to do exactly this. When a token is detected in your source code, an alert will be raised. You can even enable push protection, ensuring any code with a [supported secret][supported-secrets] can't be pushed to your repository. 47 | 48 | ### Enabling secret scanning 49 | 50 | Let's enable Secret scanning to detect any potential keys. 51 | 52 | 1. On the same page (**Settings** > **Code security and analysis**), towards the very bottom, locate the **Secret scanning** section. 53 | 1. Next to **Receive alerts on GitHub for detected secrets, keys or other tokens**, select **Enable**. 54 | 1. Next to **Push protection**, select **Enable** to block pushes to the repository which contain a [supported secret][supported-secrets]. 55 | 56 | ![Screenshot of fully configured secret scanning](./images/1-secret-scanning.png) 57 | 58 | You've now enabled secret scanning and push protection. This helps you both block keys from being pushed to your repository and quickly detect when a key has been added to your source code. 59 | 60 | ## Code scanning 61 | 62 | There is a direct relationship between the amount of code an organization creates and potential attack vectors. We always want to check our source code for vulnerabilities. [Code scanning][about-code-scanning] checks your source code for known vulnerabilities. When an issue is detected on a pull request, a new comment is added highlighting the line of source code providing contextual information for the developer. This allows for the issue to be quickly resolved. 63 | 64 | > [!NOTE] 65 | > Code scanning is built atop [GitHub Actions][github-actions], the automation platform for GitHub. We'll explore the specifics of GitHub Actions later in this workshop and create our own workflows. 66 | 67 | ### Enabling code scanning 68 | 69 | Let's enable Code scanning to detect vulnerabilities in our source code. We're going to use the default implementation, which runs whenever code is pushed to `main` or a [pull request][about-prs] is made to `main`. It will also run on a set schedule to ensure any newly discovered potential vulnerabilities are detected. 70 | 71 | 1. On the same page (**Settings** > **Code security and analysis**), towards the very bottom, locate the **Code scanning** section. 72 | 1. Next to **CodeQL analysis**, select **Set up** > **Default**. 73 | 74 | ![Screenshot of code scanning dropdown menu](./images/1-code-scanning.png) 75 | 76 | 1. On the **CodeQL default configuration** dialog, select **Enable CodeQL**. 77 | 78 | ![Screenshot of code scanning dialog](./images/1-code-scanning-dialog.png) 79 | 80 | > [!IMPORTANT] 81 | > Your list of languages may be different 82 | 83 | A background process starts, and will configure a workflow for analyzing your code using [CodeQL and code scanning][about-code-scanning]. 84 | 85 | ## Summary and next steps 86 | 87 | In this exercise, you enabled GitHub Advanced Security. You enabled Dependabot to check the libraries your project takes dependencies on, secret scanning to look for keys and tokens, and code scanning to examine your source code. These tools help ensure your application is secure. Next it's time to [file an issue][walkthrough-next] to add feature requests. 88 | 89 | ### Additional resources 90 | 91 | - [About GitHub Advanced Security][advanced-security-docs] 92 | - [GitHub Skills: Secure your repository's supply chain][skills-supply-chain] 93 | - [GitHub Skills: Secure code game][skills-secure-code] 94 | 95 | | [← Workshop setup][walkthrough-previous] | [Next: Project management with GitHub Issues →][walkthrough-next] | 96 | |:-----------------------------------|------------------------------------------:| 97 | 98 | [advanced-security]: https://github.com/features/security 99 | [advanced-security-docs]: https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security 100 | [about-code-scanning]: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning 101 | [about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests 102 | [dependabot-quickstart]: https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide 103 | [github-actions]: https://github.com/features/actions 104 | [supported-secrets]: https://docs.github.com/en/code-security/secret-scanning/secret-scanning-patterns#supported-secrets 105 | [skills-supply-chain]: https://github.com/skills/secure-repository-supply-chain 106 | [skills-secure-code]: https://github.com/skills/secure-code-game 107 | [walkthrough-previous]: 0-setup.md 108 | [walkthrough-next]: 2-issues.md 109 | -------------------------------------------------------------------------------- /content/full-day/6-code.md: -------------------------------------------------------------------------------- 1 | # Coding with GitHub Copilot 2 | 3 | | [← Helping GitHub Copilot understand context][walkthrough-previous] | [Next: GitHub flow →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | We've explored how we can use GitHub Copilot to explore our project and to provide context to ensure the suggestions we receive are to the quality we expect. Now let's turn our attention to putting all this prep work into action by generating new code! We'll use GitHub Copilot to aid us in adding functionality to our website and generate the necessary unit tests. 7 | 8 | ## Scenario 9 | 10 | The website currently lists all dogs in the database. While this was appropriate when the shelter only had a few dogs, as time has gone on the number has grown and it's difficult for people to sift through who's available to adopt. The shelter has asked you to add filters to the website to allow a user to select a breed of dog and only display dogs which are available for adoption. 11 | 12 | ## Overview of this exercise 13 | 14 | In the next handful of steps, you will: 15 | 16 | - create a new Flask endpoint to list the breeds available. 17 | - add the associated unit test. 18 | - update the backend and frontend to display the list and add the filters as required in the scenario. 19 | 20 | ## GitHub Copilot interfaces 21 | 22 | Until now, we've primarily focused on GitHub Copilot chat. This will likely be the most common way you'll interact with GitHub Copilot. It allows you to interactively ask questions, and has an ability to perform operations across an individual and (with Copilot Edits) multiple files. You can also get support from GitHub Copilot with code completion, which provides suggestions as you code. We're going to explore each of these three capabilities. 23 | 24 | ## Create a new Flask route with Code completion 25 | 26 | Code completion predicts the next block of code you're about to type based on the context Copilot has. For code completion, this includes the file you're currently working on and any tabs open in your IDE. 27 | 28 | > [!IMPORTANT] 29 | > At this time, the Copilot instructions file is only available to Copilot chat. 30 | 31 | Code completion is best for situations where you know what you want to do, and are more than happy to just start writing code with a bit of a helping hand along the way. Suggestions will be generated based both on the code you write (say a function definition) and comments you add to your code. 32 | 33 | > [!NOTE] 34 | > One great way to provide context for GitHub Copilot is to add comments to your code. While comments describing what is done can sometimes be superfluous, it helps Copilot get a better idea of what you're building. 35 | 36 | Let's build our new route in our Flask backend with the help of code completion. 37 | 38 | 1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. 39 | 2. Open **server/app.py**. 40 | 3. Locate the section of code at the very bottom which launches the server, and put your cursor just above it. This should be line 70, and the code will be: 41 | 42 | ```python 43 | if __name__ == '__main__': 44 | app.run(debug=True, port=5100) # Port 5100 to avoid macOS conflicts 45 | ``` 46 | 47 | 4. Create the route which will call the database to find all breeds, and returns a JSON array with their names and IDs. If you begin typing `@app.route` or add a comment with the requirements like `# Route to get all breeds`, you should notice italicized text generated by GitHub Copilot. 48 | 5. Select Tab to accept the code suggestion. 49 | 6. Navigate to [http://localhost:5100/api/breeds][localhost-breeds] to validate the route. 50 | 51 | > [!NOTE] 52 | > As with the prior exercise, we don't provide specific prompts to use with Copilot, as part of the learning experience is to discover how to interact with Copilot. If you are unfamiliar with Flask or how to add routes, you can look at the routes defined above for inspiration, or ask Copilot chat for guidance! 53 | 54 | ## Generate the unit tests 55 | 56 | With the route created, we want to now add the tests to ensure the code is correct. We can use GitHub Copilot chat's slash command **/tests** to create the test for us! 57 | 58 | 1. Return to your Codespace or VS Code. 59 | 2. Highlight the code you generated in the prior step. 60 | 3. Open GitHub Copilot chat. 61 | 4. Select the `+` button to start a new chat. 62 | 5. Type **/tests** and select tab to activate the command, then press enter to run the command. GitHub Copilot will generate the tests! 63 | 6. Select the **Apply edits** button just above the generated code suggestion to apply the changes to **test_app.py**. 64 | 7. Review and validate the code, making any necessary changes. Select **Keep** once you're satisfied. 65 | > [!IMPORTANT] 66 | > GitHub Copilot, like any generative AI solution, can make mistakes. Always review the generated code, making any necessary changes to ensure it's accurate and performs as expected. 67 | 8. Open a terminal window in your codespace or VS Code by selecting Ctl+Shift+` 68 | 9. Ensure the virtual server is activated by running the terminal command `source ./venv/bin/activate` 69 | 10. Navigate to the **server** folder by running the terminal command `cd server` 70 | 11. Run the tests by running the terminal command `python -m unittest` 71 | 12. Ensure all tests pass! 72 | 73 | ## Add the filters 74 | 75 | Adding the filters to the page will require updating a minimum of three files - the Flask backend, the unit tests for our Flask backend, and the Svelte frontend. Fortunately, Copilot Edits can update multiple files! Let's get our page updated with the help of Copilot Edits. 76 | 77 | 1. Open the following files in your IDE (which we'll point Copilot chat to for context): 78 | - **server/app.py** 79 | - **server/test_app.py** 80 | - **client/src/components/DogList.svelte** 81 | 2. Open GitHub Copilot Chat. 82 | 3. Switch to edit mode by selecting **Edit** in the chat mode dropdown at the bottom of Chat view (should be currently **Ask**) 83 | 4. If available, select **Claude 3.7 Sonnet** for the model. 84 | 5. Select **Add Context...** in the chat window. 85 | 6. Select **server/app.py**, **client/src/components/DogList.svelte** and **server/test_app.py** files (you need to select **Add context** for each file) 86 | > [!TIP] 87 | > If you type the file names after clicking **Add context**, they will show up in the filter. You can also drag the files or right click file in explorer and select `Copilot -> Add File to Chat`) 88 | 7. Ask Copilot to perform the operation you want, to update the page to add the filters. It should meet the following requirements: 89 | - A dropdown list should be provided with all breeds 90 | - A checkbox should be available to only show available dogs 91 | - The page should automatically refresh whenever a change is made 92 | - Tests should be updated for any changes to the endpoint. 93 | 8. Review the code suggestions to ensure they behave the way you expect them to, making any necessary changes. Once you're satisfied, you can select **Keep** on the files individually or in Copilot Chat to accept all changes. 94 | 9. Open the page at [http://localhost:4321][localhost] to see the updates! 95 | 10. Run the Python tests by using `python -m unittest` in the terminal as you did previously. 96 | 11. If any changes are needed, explain the required updates to GitHub Copilot and allow it to generate the new code. 97 | 98 | > [!IMPORTANT] 99 | > Working iteratively a normal aspect of coding with an AI pair programmer. You can always provide more context to ensure Copilot understands, make additional requests, or rephrase your original prompts. 100 | 101 | ## Summary and next steps 102 | Congratulations! You've worked with GitHub Copilot to add new features to the website - the ability to filter the list of dogs. Let's close out by [creating a pull request with our new functionality][walkthrough-next]! 103 | 104 | ## Resources 105 | - [Asking GitHub Copilot questions in your IDE][copilot-questions] 106 | - [Copilot Edits][copilot-chat-edits] 107 | - [Copilot Chat cookbook][copilot-chat-cookbook] 108 | 109 | | [← Helping GitHub Copilot understand context][walkthrough-previous] | [Next: GitHub flow →][walkthrough-next] | 110 | |:-----------------------------------|------------------------------------------:| 111 | 112 | [copilot-chat-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook 113 | [copilot-chat-edits]: https://code.visualstudio.com/docs/copilot/copilot-edits 114 | [copilot-questions]: https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-your-ide 115 | [localhost]: http://localhost:4321 116 | [localhost-breeds]: http://localhost:5100/api/breeds 117 | [walkthrough-previous]: 5-context.md 118 | [walkthrough-next]: 7-github-flow.md 119 | -------------------------------------------------------------------------------- /content/1-hour/3-copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Providing custom instructions 2 | 3 | | [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Add the filter feature →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | There are always key pieces of information anyone generating code for your codebase needs to know - the technologies in use, coding standards to follow, project structure, etc. Since context is so important, as we've discussed, we likely want to ensure Copilot always has this information as well. Fortunately, we can provide this overview through the use of Copilot instructions. 7 | 8 | ## Scenario 9 | 10 | Before we begin larger updates to the site with the help of Copilot, we want to ensure Copilot has a good understanding of how we're building our application. As a result, we're going to add a Copilot instructions file to the repository. 11 | 12 | ## Overview of Copilot instructions 13 | 14 | Copilot instructions is a markdown file is placed in your **.github** folder. It becomes part of your project, and in turn to all contributors to your codebase. You can use this file to indicate various coding standards you wish to follow, the technologies your project uses, or anything else important for Copilot Chat to understand when generating suggestions. 15 | 16 | > [!IMPORTANT] 17 | > The *copilot-instructions.md* file is included in **every** call to GitHub Copilot Chat, and will be part of the context sent to Copilot. Because there is always a limited set of tokens an LLM can operate on, a large Copilot instructions file can obscure relevant information. As such, you should limit your Copilot instructions file to project-wide information, providing an overview of what you're building and how you're building it. If you need to provide more specific information for particular tasks, you can create [prompt files](https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot?tool=vscode#about-prompt-files). 18 | 19 | Here are some guidelines to consider when creating a Copilot instructions file: 20 | 21 | - The Copilot instructions file becomes part of the project, meaning it will apply to every developer; anything indicated in the file should be globally applicable. 22 | - The file is markdown, so you can take advantage of that fact by grouping content together to improve readability. 23 | - Provide overview of **what** you are building and **how** you are building it, including: 24 | - languages, frameworks and libraries in use. 25 | - required assets to be generated (such as unit tests) and where they should be placed. 26 | - any language specific rules such as: 27 | - utilize [type hints][type-hints] in Python. 28 | - use [arrow functions][arrow-functions] rather than the `function` keyword in TypeScript. 29 | - If you notice GitHub Copilot consistently provides an unexpected suggestion (e.g. using class components for React), add those notes to the instructions file. 30 | 31 | ## Create a Copilot instructions file 32 | 33 | Let's create a Copilot instructions file. We'll start by asking Copilot to generate a block of code, then add the instructions file, then ask the same question again to see the changes. 34 | 35 | 1. Return to your IDE with your project open. 36 | 2. Close any tabs you may have open in your IDE to ensure Copilot chat has an empty context. 37 | 3. Select the `+` icon towards the top of Copilot chat to begin a new chat. 38 | 4. Open Copilot Chat and send the following prompt: 39 | 40 | ``` 41 | Create a Python function to validate dog age. Ensure age is between 0 and 20. Throw an error if it is outside this range. 42 | ``` 43 | 44 | 5. Note the function signature is similar to `def validate_dog_age(age)` without type hints. 45 | 46 | > [!NOTE] 47 | > Because LLMs are probabilistic rather than deterministic, the exact code will vary. 48 | 49 | 6. Create a new file in the **.github** folder called **copilot-instructions.md**. 50 | 7. Add the markdown to the file necessary which provides information about the project structure and requirements: 51 | 52 | ```markdown 53 | # Dog shelter 54 | 55 | This is an application to allow people to look for dogs to adopt. It is built in a monorepo, with a Flask-based backend and Astro-based frontend. 56 | 57 | ## Backend 58 | 59 | - Built using Flask and SQLAlchemy 60 | - Use type hints 61 | 62 | ## Frontend 63 | 64 | - Built using Astro and Svelte 65 | - TypeScript should use arrow functions rather than the function keyword 66 | - Pages should be in dark mode with a modern look and feel 67 | ``` 68 | 69 | 8. **Save** the file. 70 | 71 | ## Watch the instructions file in action 72 | 73 | Whenever you make a call to Copilot chat, the references dialog indicates all files used to generate the response. Once you create a Copilot instructions file, you will see it's always included in the references section. Since you included directions to use type hints, you'll notice the code suggestions will follow this guidance. 74 | 75 | 1. Close all files currently open in VS Code or your Codespace. (This will ensure we are working with an empty context.) 76 | 2. Select the `+` icon in GitHub Copilot chat to start a new chat. 77 | 3. Send Copilot chat the same prompt you used previously: 78 | 79 | ``` 80 | Create a Python function to validate dog age. Ensure age is between 0 and 20. Throw an error if it is outside this range. 81 | ``` 82 | 83 | > [!TIP] 84 | > You can use up arrow to resend previous prompts to Copilot chat. 85 | 86 | 4. Note the references now includes the instructions file and provides information gathered from it. 87 | 88 | ![Screenshot of the chat window with the references section expanded displaying Copilot instructions in the list](./images/copilot-chat-references.png) 89 | 90 | 5. Note the resulting Python now utilizes type hints, and the function signature will resemble the following: 91 | 92 | ```python 93 | def validate_dog_age(age: int): 94 | ``` 95 | 96 | > [!NOTE] 97 | > The exact code generated will vary, but the new Python suggestion should now utilize type hints. 98 | 99 | ## Make the instructions even better 100 | 101 | While we intentionally included a starter Copilot instructions file to illustrate how powerful they are, even with minimal content, you can leverage Copilot itself to either generate comprehensive instructions or improve existing ones. 102 | 103 | ### Using Copilot to generate instructions 104 | 105 | 1. Open Copilot Chat 106 | 2. Select the `+` icon towards the top of Copilot chat to begin a new chat. 107 | 3. Click on the `Cog` icon at the top of the Chat window and select `Generate Instructions` from the menu. 108 | 4. Copilot will analyze the repository and generate a comprehensive instructions file based on the project structure, technologies, and patterns. 109 | 5. Review the generated instructions. In a real-world scenario, you would customize them with items specific to your enterprise or team requirements (such as internal coding standards, security policies, or organizational best practices). For this lab, you can use the generated instructions as-is. 110 | 111 | > [!TIP] 112 | > The [github/awesome-copilot][awesome-copilot] repository contains a curated collection of example Copilot instructions files (as well as other resources like prompts, modes, etc.) from various projects and technologies. You can use these as inspiration or starting points for your own instructions. 113 | 114 | ### Beyond copilot-instructions.md: Specialized instructions 115 | 116 | While `copilot-instructions.md` is included in every Copilot Chat interaction, you can also add more specialized instructions in the `.github/instructions` folder. These files can be: 117 | 118 | - **Automatically applied** based on file patterns (using the `applyTo` frontmatter property). For example, you can ensure all React files (*.tsx and *.jsx) have the same instructions. 119 | - **Included on demand** by adding context to the chat. This is useful for specific types of tasks, like creating a new API endpoint which might require tests and updates to a data abstraction layer. 120 | 121 | For example, this repository includes: 122 | 123 | - **bicep-code-best-practices.instructions.md** - Automatically applies when working with `*.bicep` files to ensure consistent Infrastructure as Code practices for Azure Bicep 124 | - **terraform-azure.instructions.md** - Automatically applies when working with Terraform files (`*.tf`, `*.tfvars`, etc.) to follow best practices when deploying to Azure 125 | 126 | Take some seconds to examine those files, they have been sourced from [github/awesome-copilot][awesome-copilot]. 127 | 128 | This approach keeps your main instructions file concise while providing deep, specialized guidance when needed. It's particularly useful for polyglot projects or teams working with multiple technologies and deployment targets. 129 | 130 | ## Summary and next steps 131 | 132 | Copilot instructions improves the quality of suggestions, and ensures better alignment with the desired practices you have in place. With the groundwork in place, let's [add new functionality to our website][walkthrough-next]! 133 | 134 | ## Resources 135 | 136 | - [Adding repository custom instructions for GitHub Copilot][custom-instructions] 137 | 138 | 139 | | [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Add the filter feature →][walkthrough-next] | 140 | |:-----------------------------------|------------------------------------------:| 141 | 142 | [arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions 143 | [awesome-copilot]: https://github.com/github/awesome-copilot 144 | [custom-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot 145 | [type-hints]: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html 146 | [walkthrough-previous]: ./2-explore-project.md 147 | [walkthrough-next]: ./4-add-feature.md -------------------------------------------------------------------------------- /content/full-day/4-testing.md: -------------------------------------------------------------------------------- 1 | # Continuous integration and testing 2 | 3 | | [← Cloud-based development with GitHub Codespaces][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | Chances are you've heard the abbreviation CI/CD, which stands for continuous integration and continuous delivery (or sometimes continuous deployment). CI is centered on incorporating new code into the existing codebase, and typically includes running tests and performing builds. CD focuses on the next logical step, taking the now validated code and generating the necessary outputs to be pushed to the cloud or other destinations. This is probably the most focused upon component of DevOps. 7 | 8 | CI/CD fosters a culture of rapid development, collaboration, and continuous improvement, allowing organizations to deliver software updates and new features more reliably and quickly. It ensures consistency, and allows developers to focus on writing code rather than performing manual processes. 9 | 10 | [GitHub Actions][github-actions] is an automation platform upon which you can build your CI/CD process. It can also be used to automate other tasks, such as resizing images and validating machine learning models. 11 | 12 | ## Scenario 13 | 14 | A set of unit tests exist for the Python server for the project. You want to ensure those tests are run whenever someone makes a [pull request][about-prs] (PR). To meet this requirement, you'll need to define a workflow for the project, and ensure there is a [trigger][workflow-triggers] for pull requests to main. Fortunately, [GitHub Copilot][copilot] can aid you in creating the necessary YML file! 15 | 16 | ## Exploring the test 17 | 18 | Let's take a look at the tests defined for the project. 19 | 20 | > [!NOTE] 21 | > There are only a few tests defined for this project. Many projects will have hundreds or thousands of tests to ensure reliability. 22 | 23 | 1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. 24 | 2. In **Explorer**, navigate to **server** and open **test_app.py**. 25 | 3. Open GitHub Copilot Chat and ask for an explanation of the file. 26 | 27 | > [!NOTE] 28 | > Consider using the following GitHub Copilot tips to gain an understanding of the tests: 29 | > 30 | > - `/explain` is a [slash command][copilot-slash-commands] to quickly ask for an explanation 31 | > - Highlight specific sections of the file to focus on areas you may have questions about 32 | 33 | ## Understanding workflows 34 | 35 | To ensure the tests run whenever a PR is made you'll define a workflow for the project. Workflows can perform numerous tasks, such as checking for security vulnerabilities, deploying projects, or (in our case) running unit tests. They're central to any CI/CD. 36 | 37 | Creating a YML file can be a little tricky. Fortunately, GitHub Copilot can help streamline the process! Before we work with Copilot to create the file, let's explore some core sections of a workflow: 38 | 39 | - `name`: Provides a name for the workflow, which will display in the logs. 40 | - `on`: Defines what will trigger the workflow to run. Some common triggers include `pull_request` (when a PR is made), `merge` (when code is merged into a branch), and `workflow_dispatch` (manual run). 41 | - `jobs`: Defines a series of jobs for this workflow. Each job is considered a unit of work and has a name. 42 | - **name**: Name and container for the job. 43 | - `runs-on`: Where the operations for the job will be performed. 44 | - `steps`: The operations to be performed. 45 | 46 | ## Create the workflow file 47 | 48 | Now that we have an overview of the structure of a workflow, let's ask Copilot to generate it for us! 49 | 50 | 1. Create a new folder under **.github** named **workflows**. 51 | 2. Create a new file named **server-test.yml** and ensure the file is open. 52 | 3. If prompted to install the **GitHub Actions** extension, select **Install**. 53 | 4. Open GitHub Copilot Chat. 54 | 5. Add the test file **test_app.py** to the context by using the `#` in the Chat dialog box and beginning to type **test_app.py**, and pressing enter when it's highlighted. 55 | 6. Prompt Copilot to create a GitHub Action workflow to run the tests. Use natural language to describe the workflow you're looking to create (to run the tests defined in test_app.py), and that you want it to run on merge (for when new code is pushed), when a PR is made, and on demand. 56 | 57 | > [!IMPORTANT] 58 | > A prescriptive prompt isn't provided as part of the exercise is to become comfortable interacting with GitHub Copilot. 59 | 60 | 6. Add the generated code to the new file by hovering over the suggested code and selecting the **Insert at cursor** button. The generated code should resemble the following: 61 | 62 | ```yml 63 | name: Server Tests 64 | 65 | on: 66 | push: 67 | branches: [ main ] 68 | paths: 69 | - 'server/**' 70 | pull_request: 71 | branches: [ main ] 72 | paths: 73 | - 'server/**' 74 | 75 | jobs: 76 | server-test: 77 | runs-on: ubuntu-latest 78 | 79 | steps: 80 | - uses: actions/checkout@v3 81 | 82 | - name: Set up Python 83 | uses: actions/setup-python@v4 84 | with: 85 | python-version: '3.10' 86 | 87 | - name: Install dependencies 88 | run: | 89 | python -m pip install --upgrade pip 90 | if [ -f server/requirements.txt ]; then pip install -r server/requirements.txt; fi 91 | pip install pytest 92 | 93 | - name: Run tests 94 | working-directory: ./server 95 | run: | 96 | python -m pytest test_app.py -v 97 | ``` 98 | 99 | > [!IMPORTANT] 100 | > Note, the file generated may differ from the example above. Because GitHub Copilot uses generative AI, there results will be probabilistic rather than deterministic. 101 | 102 | > [!TIP] 103 | > If you want to learn more about the workflow you just created, ask GitHub Copilot! 104 | 105 | ## Push the workflow to the repository 106 | 107 | With the workflow created, let's push it to the repository. Typically you would create a PR for any new code (which this is). To streamline the process, we're going to push straight to main as we'll be exploring pull requests and the [GitHub flow][github-flow] in a [later exercise][github-flow-exercise]. You'll start by obtaining the number of the [issue you created earlier][issues-exercise], creating a commit for the new code, then pushing it to main. 108 | 109 | > [!NOTE] 110 | > All commands are entered using the terminal window in the codespace. 111 | 112 | 1. Use the open terminal window in your codespace, or open it (if necessary) by pressing Ctl + `. 113 | 1. List all issues for the repository by entering the following command in the terminal window: 114 | 115 | ```bash 116 | gh issue list 117 | ``` 118 | 119 | 1. Note the issue number for the one titled **Implement testing**. 120 | 1. Stage all files by entering the following command in the terminal window: 121 | 122 | ```bash 123 | git add . 124 | ``` 125 | 126 | 1. Commit all changes with a message by entering the following command in the terminal window, replacing **** with the number for the **Implement testing** issue: 127 | 128 | ```bash 129 | git commit -m "Resolves #" 130 | ``` 131 | 132 | 1. Push all changes to the repository by entering the following command in the terminal window: 133 | 134 | ```bash 135 | git push 136 | ``` 137 | 138 | Congratulations! You've now implemented testing, a core component of continuous integration (CI)! 139 | 140 | ## Seeing the workflow in action 141 | 142 | Pushing the workflow definition to the repository counts as a push to `main`, meaning the workflow will be triggered. You can see the workflow in action by navigating to the **Actions** tab in your repository. 143 | 144 | 1. Return to your repository. 145 | 2. Select the **Actions** tab. 146 | 3. Select **Server test** on the left side. 147 | 4. Select the workflow run on the right side with a message of **Resolves #**, matching the commit message you used. 148 | 5. Explore the workflow run by selecting the job name 149 | 150 | You've now seen a workflow, and explore the details of a run! 151 | 152 | ## Summary and next steps 153 | 154 | Congratulations! You've implemented automated testing, a standard part of continuous integration, which is critical to successful DevOps. Automating these processes ensures consistency and reduces the workload required for developers and administrators. You have created a workflow to run tests on any new code for your codebase. Let's explore [context with GitHub Copilot chat][walkthrough-next]. 155 | 156 | ### Resources 157 | - [GitHub Actions][github-actions] 158 | - [GitHub Actions Marketplace][actions-marketplace] 159 | - [About continuous integration][about-ci] 160 | - [GitHub Skills: Test with Actions][skills-test-actions] 161 | 162 | | [← Cloud-based development with GitHub Codespaces][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | 163 | |:-----------------------------------|------------------------------------------:| 164 | 165 | [about-ci]: https://docs.github.com/en/actions/automating-builds-and-tests/about-continuous-integration 166 | [about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests 167 | [actions-marketplace]: https://github.com/marketplace?type=actions 168 | [workflow-triggers]: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows 169 | [copilot]: https://gh.io/copilot 170 | [copilot-slash-commands]: https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/github-copilot-chat-cheat-sheet 171 | [github-actions]: https://github.com/features/actions 172 | [github-flow]: https://docs.github.com/en/get-started/quickstart/github-flow 173 | [github-flow-exercise]: ./7-github-flow.md 174 | [issues-exercise]: ./2-issues.md 175 | [skills-test-actions]: https://github.com/skills/test-with-actions 176 | [walkthrough-previous]: 3-codespaces.md 177 | [walkthrough-next]: 5-context.md 178 | -------------------------------------------------------------------------------- /content/full-day/3-codespaces.md: -------------------------------------------------------------------------------- 1 | # Cloud-based development with GitHub Codespaces 2 | 3 | | [← Project management with GitHub Issues][walkthrough-previous] | [Next: Continuous integration and testing →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | One of the biggest challenges organizations face is onboarding new developers to projects. There are libraries to install, services to configure, version issues, obscure error messages... It can literally take days to get everything running before a developer is able to write their first line of code. [GitHub Codespaces][codespaces] is built to streamline this entire process. You can configure a container for development which your developers can access with just a couple of clicks from basically anywhere in the world. The container runs in the cloud, has everything already setup, and ready to go. Instead of days your developers can start writing code in seconds. 7 | 8 | GitHub Codespaces allows you to develop using the cloud-based container and Visual Studio Code in your browser window, meaning no local installation is required; you can do development with a tablet and a keyboard! You can also connect your local instance of [Visual Studio Code][vscode-codespaces]. 9 | 10 | Let's explore how to create and configure a codespaces for your project, and see how you can develop in your browser. 11 | 12 | ## Using the default container 13 | 14 | GitHub provides a [default container][github-universal-container] for all repositories. This container is based on a Linux image, and contains many popular runtimes including Node.js, Python, PHP and .NET. In many scenarios, this default container might be all you need. You also have the ability to configure a custom container for the repository, as you'll see later in this exercise. For now, let's explore how to use the default container. 15 | 16 | 1. If not already open, open your repository in your browser. 17 | 1. From the **Code** tab (suggest to open a new browser tab) in your repo, access the green **<> Code** dropdown button and from the **Codespaces** tab click **Create codespace on main**. 18 | 1. Allow the Codespace to load; it should take less than 30 seconds because we are using the default image. 19 | 20 | ## Defining a custom container 21 | 22 | One thing that's really great is the [default dev container][github-universal-container-definition] has **.NET 7**, **node**, **python**, **mvn**, and more. But what if you need other tools? Or in our case, we want don't want to have each developer install the **[GitHub Copilot Extension][copilot-extension]**; we want to have everything pre-configured from the start! 23 | 24 | Let's create our own dev container! The [dev container is configured][dev-containers-docs] by creating the Docker files Codespaces will use to create and configure the container, and providing any customizations in the `devcontainer.json` file. Customizations provided in `devcontainer.json` can include ports to open, commands to run, and extension to install in Visual Studio Code (either running locally on the desktop or in the browser). This configuration becomes part of the repository. All developers who wish to contribute can then create a new instance of the container based on the configuration you provided. 25 | 26 | 1. Access the Command Palette (F1 or clicking ☰ → View → Command Palette), then start typing **dev container**. 27 | 2. Select **Codespaces: Add Development Container Configuration Files...** . 28 | 3. Select **Create a new configuration...**. 29 | 4. Scroll down and select **Node.js & TypeScript**. 30 | 5. Select **22-bookworm (default)**. 31 | 6. Select the following features to add into your container: 32 | - **Azure CLI** 33 | - **GitHub CLI** 34 | - **Python** 35 | 36 | > [!NOTE] 37 | > You can type the name of the feature you want to filter the list. 38 | 39 | 7. Select **OK** to add the features. 40 | 8. Select **Keep defaults** to use the default configuration. 41 | 9. If you receive the prompt **File './.github/dependabot.yml' already exists, overwrite?**, select **Skip**. 42 | 43 | > [!IMPORTANT] 44 | > Your new container definition files will be created into the **.devcontainer** folder. **DO NOT** select **Rebuild Now**; we'll do that in just a moment. 45 | 46 | You have now defined the container to be used by your codespace. This contains the necessary services and tools for your code. 47 | 48 | ## Customize the extensions 49 | 50 | Creating a development environment isn't solely focused on the services. Developers rely on various extensions and plugins for their [integrated development environments (IDEs)][IDE]. To ensure consistency, you may want to define a set of extensions to automatically install. When using GitHub Codespaces and either a local instance of Visual Studio Code or the browser-based version, you can add a list of [extensions][vscode-extensions] to the **devcontainer.json** file. 51 | 52 | Before rebuilding the container, let's add **GitHub.copilot** to the list of extensions. 53 | 54 | 1. Remaining in the codespace, open **devcontainer.json** inside the **.devcontainer** folder. 55 | 2. Locate the following section: 56 | 57 | ```json 58 | "features": { 59 | "ghcr.io/devcontainers/features/github-cli:1": {}, 60 | "ghcr.io/devcontainers/features/python:1": {} 61 | } 62 | ``` 63 | 64 | 3. Add a comma (`,`) to the end of the last `}`, which should be line 10. 65 | 4. Immediately below that line, paste the following code to provide the list of extensions you wish to have for your dev container: 66 | 67 | ```json 68 | "customizations": { 69 | "vscode": { 70 | "extensions": [ 71 | "GitHub.copilot", 72 | "GitHub.copilot-chat", 73 | "ms-azuretools.vscode-azure-github-copilot", 74 | "alexcvzz.vscode-sqlite", 75 | "astro-build.astro-vscode", 76 | "svelte.svelte-vscode", 77 | "ms-python.python", 78 | "ms-python.vscode-pylance" 79 | ] 80 | } 81 | }, 82 | ``` 83 | 84 | 5. Just below the customizations, paste the following code to provide the list of ports which should be made available for development by the codespace: 85 | 86 | ```json 87 | "forwardPorts": [ 88 | 4321, 89 | 5100, 90 | 5000 91 | ], 92 | ``` 93 | 94 | 6. Just below the list of ports, add the command to run the startup script to the container definition: 95 | 96 | ```json 97 | "postStartCommand": "chmod +x /workspaces/dog-shelter/scripts/start-app.sh && /workspaces/dog-shelter/scripts/start-app.sh", 98 | ``` 99 | 100 | You've now defined a custom container! 101 | 102 | ## Use the newly defined custom container 103 | 104 | Whenever someone uses the codespace you defined they'll have an environment with Node.js and Mongo DB, and the GitHub Copilot extension installed. Let's use this container! 105 | 106 | 1. Access the Command Palette (F1 or clicking ☰ → View → Command Palette), then start typing **dev container**. 107 | 1. Type **rebuild** and select **Codespaces: Rebuild container**. 108 | 1. Select **Rebuild Container** on the dialog box. Your container now rebuilds. 109 | 110 | > [!IMPORTANT] 111 | > Rebuilding the container can take several minutes. Obviously this isn't an ideal situation for providing fast access to your developers, even if it's faster than creating everything from scratch. Fortunately you can [prebuild your codespaces][codespace-prebuild] to ensure developers can spin one up within seconds. 112 | > 113 | > You may also be prompted to reload the window as extensions install. Reload the window as prompted. 114 | 115 | ## Interacting with the repository 116 | 117 | Custom containers for GitHub Codespaces become part of the source code for the repository. Thus they are maintained through standard source control, and will follow the repository as it's forked in the future. This allows this definition to be shared across all developers contributing to the project. Let's upload our new configuration, closing the [issue you created][walkthrough-previous] for defining a development environment. 118 | 119 | > [!IMPORTANT] 120 | > For purposes of this exercise we are pushing code updates directly to `main`, our default branch. Normally you would follow the [GitHub flow][github-flow], which we will do in a [later exercise][github-flow-exercise]. 121 | 122 | 1. Open a new terminal window in the codespace by selecting Ctl + Shift + ` or clicking ☰ → View → Terminal. 123 | 2. Find the issue number for defining the codespace by entering the following command: 124 | 125 | ```bash 126 | gh issue list 127 | ``` 128 | 129 | > [!NOTE] 130 | > It will likely be #1. You'll use this number later in this exercise. 131 | 132 | 3. Stage all files, commit the changes with a message to resolve the issue, and push to main by entering the following command in the terminal window, replacing `` with the number you obtained in the previous step: 133 | 134 | ```bash 135 | git add . 136 | git commit -m "Resolves #" 137 | git push 138 | ``` 139 | > [!NOTE] 140 | > If prompted, select **Allow** to enable copy/paste for the codespace. 141 | 142 | 4. When the command completes, enter the following to list all open issues: 143 | 144 | ```bash 145 | gh issue list 146 | ``` 147 | 148 | 5. Note the issue for defining a codespace is no longer listed; you completed it and marked it as such with your pull request! 149 | 150 | 151 | ## Summary and next steps 152 | Congratulations! You have now defined a custom development environment including all services and extensions. This eliminates the initial setup hurdle normally required when contributing to a project. Let's use this codespace to [implement testing and continuous integration][walkthrough-next] for the project. 153 | 154 | ## Resources 155 | - [GitHub Codespaces][codespaces] 156 | - [Getting started with GitHub Codespaces][codespaces-docs] 157 | - [Defining dev containers][dev-containers-docs] 158 | - [GitHub Skills: Code with Codespaces][skills-codespaces] 159 | 160 | | [← Project management with GitHub Issues][walkthrough-previous] | [Next: Continuous integration and testing →][walkthrough-next] | 161 | |:-----------------------------------|------------------------------------------:| 162 | 163 | [codespaces]: https://github.com/features/codespaces 164 | [copilot-extension]: https://marketplace.visualstudio.com/items?itemName=GitHub.copilot 165 | [codespaces-docs]: https://docs.github.com/en/codespaces/overview 166 | [codespace-prebuild]: https://docs.github.com/en/codespaces/prebuilding-your-codespaces 167 | [dev-containers-docs]: https://docs.github.com/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers 168 | [github-flow]: https://docs.github.com/en/get-started/quickstart/github-flow 169 | [github-flow-exercise]: ./7-github-flow.md 170 | [github-universal-container]: https://docs.github.com/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers#using-the-default-dev-container-configuration 171 | [github-universal-container-definition]: https://github.com/devcontainers/images/blob/main/src/universal/.devcontainer/Dockerfile 172 | [IDE]: https://en.wikipedia.org/wiki/Integrated_development_environment 173 | [skills-codespaces]: https://github.com/skills/code-with-codespaces 174 | [vscode-codespaces]: https://docs.github.com/en/codespaces/developing-in-codespaces/using-github-codespaces-in-visual-studio-code 175 | [vscode-extensions]: https://code.visualstudio.com/docs/editor/extension-marketplace 176 | [walkthrough-previous]: 2-issues.md 177 | [walkthrough-next]: 4-testing.md 178 | -------------------------------------------------------------------------------- /content/full-day/5-context.md: -------------------------------------------------------------------------------- 1 | # Helping GitHub Copilot understand context 2 | 3 | | [← Implement testing][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | The key to success when coding (and much of life) is context. Before we add code to a codebase, we want to understand the rules and structures already in place. When working with an AI coding assistant such as GitHub Copilot the same concept applies - the quality of suggestion is directly proportional to the context Copilot has. Let's use this opportunity to both explore the project we've been given and how to interact with Copilot to ensure it has the context it needs to do its best work. 7 | 8 | ## Scenario 9 | 10 | Before adding new functionality to the website, you want to explore the existing structure to determine where the updates need to be made. You also want to provide Copilot some context in the form of [custom instructions][copilot-custom-instructions] so it has a better idea of how best to generate code. 11 | 12 | ## Getting started with GitHub Copilot 13 | 14 | GitHub Copilot is a cloud-based service offered for both individuals and businesses. As an individual, you can [sign up for a free account][copilot-signup] of the service. After enrolling you will typically install the extension for your IDE, which is available for [Visual Studio][copilot-vs], [Visual Studio Code][copilot-vscode], [NeoVIM][copilot-vim], the [JetBrains IDEs][copilot-jetbrains], [XCode](copilot-xcode) and [Eclipse][copilot-eclipse]. Because we'll be using the [Codespace][walkthrough-codespaces] you defined in the previous exercise, you won't need to manually install the extension - you did that when you configured the dev container! 15 | 16 | 1. If you don't already have access to GitHub Copilot, [sign up for a free trial][copilot-signup]. 17 | 2. In the [previous exercise][walkthrough-codespaces] you configured your [devcontainer][devcontainer-docs] to automatically install the extension for GitHub Copilot, so you're all set and ready to go! 18 | 19 | ## Chat participants and extensions 20 | 21 | GitHub Copilot Chat has a set of available chat participants and extensions available to you to both provide instructions to GitHub Copilot and access external services. Chat participants are helpers which work inside your IDE and have access to your project, while extensions can call external services and provide information to you without having to open separate tools. We're going to focus on one core chat participant - `@workspace`. 22 | 23 | `@workspace` creates an index of your project and allows you to ask questions about what you're currently working on, to find resources inside the project, or add it to the context. It's best to use this when the entirety of your project should be considered or you're not entirely sure where you should start looking. In our current scenario, since we want to ask questions about our project, `@workspace` is the perfect tool for the job. 24 | 25 | > [!NOTE] 26 | > This exercise doesn't provide specific prompts to type, as part of the learning experience is to discover how to interact with Copilot. Feel free to talk in natural language, describing what you're looking for or need to accomplish. 27 | 28 | 1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. 29 | 2. Open GitHub Copilot Chat. 30 | 3. Select the `+` icon towards the top to begin a new chat. 31 | 4. Type `@workspace` in the chat prompt window and hit tab to select or activate it, then continue by asking Copilot about your project. You can ask what technologies are in use, what the project does, where functionality resides, etc. 32 | 5. Spend a few minutes exploring to find the answers to the following questions: 33 | - What frameworks are currently in use? 34 | - Where's the database the project uses? 35 | - How is the frontend built? 36 | - How is the backend built? 37 | - What files are involved in listing dogs? 38 | 39 | ## Providing custom instructions 40 | 41 | Context is key to ensuring the code suggestions you receive from GitHub Copilot align with your expectations. When operating with limited information, Copilot makes assumptions about what you're looking for, and can sometimes guess incorrectly. By providing context, you allow Copilot to better align with your objectives. One great way to do this is by building a [copilot-instructions.md][copilot-custom-instructions] file. This markdown file is placed in your **.github** folder and becomes part of your project. You can use this file to indicate various coding standards you wish to follow, the technologies your project uses, or anything else important for Copilot Chat to understand when generating suggestions. 42 | 43 | > [!IMPORTANT] 44 | > The *copilot-instructions.md* file is included in **every** call to GitHub Copilot Chat, and will be part of the context sent to Copilot. Because there is always a limited set of tokens an LLM can operate on, a large set of Copilot instructions can obscure relevant information. As such, you should limit your Copilot instructions file to project-wide information, providing an overview of what you're building and how you're building it. If you need to provide more specific information for particular tasks, you can create [prompt files][copilot-prompt-files] as needed. 45 | 46 | Here are some guidelines to consider when creating a Copilot instructions file: 47 | 48 | - The Copilot instructions file becomes part of the project, meaning it will apply to every developer; anything indicated in the file should be globally applicable. 49 | - The file is markdown, so you can take advantage of that fact by grouping content together to improve readability. 50 | - Provide overview of **what** you are building and **how** you are building it, including: 51 | - languages, frameworks and libraries in use. 52 | - required assets to be generated (such as unit tests) and where they should be placed. 53 | - any language specific rules such as: 54 | - Python code should always follow PEP8 rules. 55 | - use arrow functions rather than the `function` keyword. 56 | - If you notice GitHub Copilot consistently provides an unexpected suggestion (e.g. using class components for React), add those notes to the instructions file. 57 | 58 | Let's create a Copilot instructions file. Just as before, because we want you to explore and experiment, we won't provide exact directions on what to type, but will give enough context to create one on your own. 59 | 60 | 1. Create a new file in the **.github** folder called **copilot-instructions.md**. 61 | 2. Add the markdown to the file necessary to provide information about the project structure and requirements, including: 62 | - an overview of the project itself (based on the information you gathered earlier in this exercise). 63 | - the languages and frameworks in use to create both the server and client. 64 | - unit tests are required for routes in the Flask app, and must mock the database calls. 65 | - the website should be in dark mode and have a modern look and feel. 66 | 3. Save the file! 67 | 68 | Your Copilot instructions file could resemble the following (but again - use your own words and style!): 69 | 70 | ```markdown 71 | # Dog shelter 72 | 73 | This is an application to allow people to look for dogs to adopt. It is built in a monorepo, with a Flask-based backend and Astro-based frontend. 74 | 75 | ## Backend 76 | 77 | - Built using Flask and SQLAlchemy 78 | - All routes require unit tests, which are created in *test_file.py* in the same folder as the file 79 | - When creating tests, always mock database calls 80 | 81 | ## Frontend 82 | 83 | - Built using Astro and Svelte 84 | - Pages should be in dark mode with a modern look and feel 85 | ``` 86 | 87 | ## Watch the instructions file in action 88 | 89 | Whenever you make a call to Copilot chat, the response will always include the context being used. The context can automatically include the open file (focused on any code you highlight), and individual files or folders you add by using `#file` or `#folder`. You can also include the an index of your workspace by using `@workspace`, as highlighted earlier. The references dialog is a great way to check what information Copilot was using when generating its suggestions and response. Once you create a Copilot instructions file, you will see it's always included in the references section. 90 | 91 | 1. Close all files currently open in VS Code or your Codespace. 92 | 2. Select the `+` icon in GitHub Copilot chat to start a new chat. 93 | 3. Ask Copilot chat **What are the guidelines for the flask app?** 94 | 4. Note the references now includes the instructions file and provides information gathered from it. 95 | 96 | ![Screenshot of the chat window with the references section expanded displaying Copilot instructions in the list](./images/5-copilot-chat-references.png) 97 | 98 | ## Summary and next steps 99 | 100 | Congratulations! You've explored context in GitHub Copilot, which is key to generating quality suggestions. You saw how you can use chat participants to help guide GitHub Copilot, and create a Copilot instructions file to provide an overview of what you're building and how you're building it. With this in place, it's time to turn our attention to [adding new functionality to our website][walkthrough-next]! 101 | 102 | ## Resources 103 | 104 | - [Getting started with GitHub Copilot][copilot-getting-started] 105 | - [Adding repository custom instructions for GitHub Copilot][copilot-custom-instructions] 106 | - [Adding personal custom instructions for GitHub Copilot][copilot-personal-instructions] 107 | - [Copilot Chat cookbook][copilot-chat-cookbook] 108 | - [Use Copilot Chat in VS Code][vscode-copilot-chat] 109 | 110 | | [← Implement testing][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | 111 | |:-----------------------------------|------------------------------------------:| 112 | 113 | [copilot-chat-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook 114 | [copilot-custom-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot 115 | [copilot-eclipse]: https://marketplace.eclipse.org/content/github-copilot 116 | [copilot-getting-started]: https://docs.github.com/en/copilot/getting-started-with-github-copilot 117 | [copilot-jetbrains]: https://plugins.jetbrains.com/plugin/17718-github-copilot 118 | [copilot-prompt-files]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot?tool=vscode#about-prompt-files 119 | [copilot-personal-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-personal-custom-instructions-for-github-copilot 120 | [copilot-signup]: https://github.com/github-copilot/signup 121 | [copilot-vim]: https://github.com/github/copilot.vim#getting-startedins.com/plugin/17718-github-copilot 122 | [copilot-vs]: https://marketplace.visualstudio.com/items?itemName=GitHub.copilotvs 123 | [copilot-vscode]: https://marketplace.visualstudio.com/items?itemName=GitHub.copilot 124 | [copilot-xcode]: https://github.com/github/CopilotForXcode 125 | [devcontainer-docs]: https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containersopilot/adding-personal-custom-instructions-for-github-copilot 126 | [vscode-copilot-chat]: https://code.visualstudio.com/docs/copilot/copilot-chat 127 | [walkthrough-codespaces]: ./3-codespaces.mdvisualstudio.com/docs/copilot/copilot-chat 128 | [walkthrough-next]: 6-code.md 129 | [walkthrough-previous]: 4-testing.md 130 | 131 | --------------------------------------------------------------------------------