├── .DS_Store ├── .github ├── CODEOWNERS └── dependabot.yml ├── .gitignore ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── client ├── .dockerignore ├── Dockerfile ├── README.md ├── astro.config.mjs ├── package-lock.json ├── package.json ├── public │ └── favicon.svg ├── src │ ├── assets │ │ ├── astro.svg │ │ └── background.svg │ ├── components │ │ ├── DogDetails.svelte │ │ ├── DogList.svelte │ │ └── Header.astro │ ├── layouts │ │ └── Layout.astro │ ├── middleware.ts │ ├── pages │ │ ├── about.astro │ │ ├── dog │ │ │ └── [id].astro │ │ └── index.astro │ └── styles │ │ └── global.css ├── svelte.config.js └── tsconfig.json ├── content ├── .DS_Store ├── 1-hour │ ├── 0-setup.md │ ├── 1-add-endpoint.md │ ├── 2-explore-project.md │ ├── 3-copilot-instructions.md │ ├── 4-add-feature.md │ ├── 5-bonus.md │ ├── README.md │ └── images │ │ ├── 0-setup-configure.png │ │ ├── 0-setup-template.png │ │ ├── copilot-agent-mode-how-it-works.png │ │ ├── copilot-chat-references.png │ │ ├── copilot-edits-history.png │ │ ├── copilot-edits-keep-undo-file.png │ │ ├── copilot-edits-keep-undo-global.png │ │ └── tail-spin-shelter-terminal-theme.png ├── GitHub-Copilot-Resources.md ├── README.md ├── full-day │ ├── 0-setup.md │ ├── 1-code-scanning.md │ ├── 2-issues.md │ ├── 3-codespaces.md │ ├── 4-testing.md │ ├── 5-context.md │ ├── 6-code.md │ ├── 7-github-flow.md │ ├── 8-deployment.md │ ├── README.md │ └── images │ │ ├── 1-code-scanning-dialog.png │ │ ├── 1-code-scanning.png │ │ ├── 1-dependabot.png │ │ ├── 1-secret-scanning.png │ │ ├── 3-open-browser.png │ │ ├── 3-reload.png │ │ ├── 3-secrets-variables.png │ │ ├── 4-select-file.png │ │ ├── 5-copilot-chat-references.png │ │ └── 7-generate-commit-message.png ├── how-github-uses-github.md └── prompts │ ├── README.md │ ├── conversion-convert-flask-to-golang.md │ ├── fun-add-dog-animation.md │ ├── fun-add-themes.md │ └── monitoring-add-logging.md ├── scripts ├── start-app.ps1 └── start-app.sh └── server ├── app.py ├── dogshelter.db ├── models ├── __init__.py ├── base.py ├── breed.py ├── breeds.csv ├── dog.py └── dogs.csv ├── requirements.txt ├── test_app.py └── utils └── seed_database.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/.DS_Store -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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: / 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 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /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/ -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 GitHub 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 | -------------------------------------------------------------------------------- /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 [MIT](./LICENSE.txt) 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 | -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /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/.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | }); -------------------------------------------------------------------------------- /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 | }, 11 | "dependencies": { 12 | "@astrojs/node": "^9.1.3", 13 | "@astrojs/svelte": "^7.0.6", 14 | "@tailwindcss/vite": "^4.0.13", 15 | "astro": "^5.4.3", 16 | "svelte": "^5.23.0", 17 | "typescript": "^5.8.2" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^22.13.11", 21 | "autoprefixer": "^10.4.21", 22 | "postcss": "^8.5.3", 23 | "tailwindcss": "^4.0.14" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /client/src/assets/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/src/assets/background.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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} -------------------------------------------------------------------------------- /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 |
66 | {#each dogs as dog (dog.id)} 67 | 71 |
72 |
73 |
74 |

{dog.name}

75 |

{dog.breed}

76 |
77 | View details 78 | 79 | 80 | 81 |
82 |
83 |
84 |
85 | {/each} 86 |
87 | {/if} 88 |
-------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 |
-------------------------------------------------------------------------------- /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 |
-------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /client/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | -------------------------------------------------------------------------------- /client/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@astrojs/svelte'; 2 | 3 | export default { 4 | preprocess: vitePreprocess(), 5 | } 6 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": [".astro/types.d.ts", "**/*"], 4 | "exclude": ["dist"] 5 | } 6 | -------------------------------------------------------------------------------- /content/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/.DS_Store -------------------------------------------------------------------------------- /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 following command: 50 | 51 | ```sh 52 | ./scripts/start-app.sh 53 | ``` 54 | 55 | The startup script will start two applications: 56 | 57 | - The backend Flask app on [localhost:5100][flask-url]. You can see a list of dogs by opening the [dogs API][dogs-api]. 58 | - The frontend Astro/Svelte app on [localhost:4321][astro-url]. You can see the [website][website-url] by opening that URL. 59 | 60 | ## Open your editor 61 | 62 | With the code cloned locally, and the site running, let's open the codebase up in VS Code. 63 | 64 | 1. Open VS Code. 65 | 2. Select **File** > **Open Folder**. 66 | 3. Navigate to the folder which contains the project you cloned earlier in this exercise. 67 | 4. With the folder highlighted, select **Open folder**. 68 | 69 | ## Summary and next steps 70 | 71 | 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]! 72 | 73 | 74 | | [← Getting started with GitHub Copilot][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | 75 | |:-----------------------------------|------------------------------------------:| 76 | 77 | [astro-url]: http://localhost:4321 78 | [dogs-api]: http://localhost:5100/api/dogs 79 | [flask-url]: http://localhost:5100 80 | [fork-repo]: https://docs.github.com/en/get-started/quickstart/fork-a-repo 81 | [required-resources]: ./README.md#required-resources 82 | [required-software]: ./README.md#required-local-installation 83 | [template-repo]: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository 84 | [walkthrough-previous]: README.md 85 | [walkthrough-next]: ./1-add-endpoint.md 86 | [website-url]: http://localhost:4321 -------------------------------------------------------------------------------- /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 `./scripts/start-app.sh`. 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/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 -------------------------------------------------------------------------------- /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 | ## Summary and next steps 100 | 101 | 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]! 102 | 103 | ## Resources 104 | 105 | - [Adding repository custom instructions for GitHub Copilot][custom-instructions] 106 | 107 | 108 | | [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Add the filter feature →][walkthrough-next] | 109 | |:-----------------------------------|------------------------------------------:| 110 | 111 | [arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions 112 | [custom-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot 113 | [type-hints]: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html 114 | [walkthrough-previous]: ./2-explore-project.md 115 | [walkthrough-next]: ./4-add-feature.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/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 [](../prompts/fun-add-themes.md) 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/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/images/0-setup-configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/0-setup-configure.png -------------------------------------------------------------------------------- /content/1-hour/images/0-setup-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/0-setup-template.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-agent-mode-how-it-works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/copilot-agent-mode-how-it-works.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-chat-references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/copilot-chat-references.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-edits-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/copilot-edits-history.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-edits-keep-undo-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/copilot-edits-keep-undo-file.png -------------------------------------------------------------------------------- /content/1-hour/images/copilot-edits-keep-undo-global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/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/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/1-hour/images/tail-spin-shelter-terminal-theme.png -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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. 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 | ## Scenario 19 | 20 | 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. 21 | 22 | ## Creating issues to manage feature requests 23 | 24 | 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. 25 | 26 | 1. Return to the repository you created at the beginning of this workshop. 27 | 1. Select the **Issues** tab. 28 | 1. Select **New issue**. 29 | 2. If prompted for type, select **Blank issue**. 30 | 3. Select **Create more** at the bottom of the page to streamline the creation process. 31 | 4. Create new issues by adding the information indicated in the table below, selecting **Submit new issue** after creating each one: 32 | 33 | | Title | Description | 34 | | ----------------------- | ------------------------------------------------------------------------------ | 35 | | Define codespace | Create the necessary definitions for the codespace to enable cloud development | 36 | | Implement testing | Create a workflow to automate testing for continuous integration | 37 | | Add filters to dog list | Add the code to allow users to filter for dogs by breed and availability | 38 | 39 | > [!TIP] 40 | > You can also save an issue by pressing Ctl - Enter (or Cmd - Return on a Mac) in the title or description fields. 41 | 42 | You've now defined all the issues for the workshop! You'll use these issues to help guide your progress through the workshop. 43 | 44 | ## Summary and next steps 45 | 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]. 46 | 47 | ## Resources 48 | - [GitHub Issues][issues-docs] 49 | - [Communicate using markdown][skills-markdown] 50 | - [GitHub Projects][projects-docs] 51 | 52 | | [← Securing the development pipeline][walkthrough-previous] | [Next: Cloud-based development with GitHub Codespaces →][walkthrough-next] | 53 | |:-----------------------------------|------------------------------------------:| 54 | 55 | [discussions]: https://github.com/features/discussions 56 | [wikis]: https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis 57 | [about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests 58 | [issues]: https://github.com/features/issues 59 | [issues-docs]: https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues 60 | [projects-docs]: https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects 61 | [skills-markdown]: https://github.com/skills/communicate-using-markdown 62 | [walkthrough-next]: 3-codespaces.md 63 | [walkthrough-previous]: 1-code-scanning.md -------------------------------------------------------------------------------- /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/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 cause 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/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 | -------------------------------------------------------------------------------- /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/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/8-deployment.md: -------------------------------------------------------------------------------- 1 | # Deploying the project to the cloud 2 | 3 | | [← GitHub flow][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | 4 | |:-----------------------------------|------------------------------------------:| 5 | 6 | The CD portion of CI/CD is continuous delivery or continuous deployment. In a nutshell, it's about taking the product you're building and putting it somewhere to be accessed by the people who need it. There's numerous ways to do this, and the process can become rather involved. We're going to focus on taking our application and deploying it to Azure. 7 | 8 | > [!NOTE] 9 | > We've taken a couple of shortcuts with the application structure to ensure things run smoothly in this workshop. 10 | 11 | ## Scenario 12 | 13 | With the prototype built, the shelter is ready to begin gathering feedback from external users. They want to deploy the project to the internet, and ensure any updates merged into main are available as quickly as possible. 14 | 15 | ## Return to main 16 | 17 | To streamline the process, we're going to work directly with the **main** branch. Let's change back to the **main** branch and obtain the updates we pushed previously. 18 | 19 | 1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. 20 | 2. Open a new terminal window by selecting Ctl+Shift+`. 21 | 3. Run the following commands to checkout the main branch and obtain the updates from the repository: 22 | 23 | ```sh 24 | git checkout main 25 | git pull 26 | ``` 27 | 28 | ## Identity management 29 | 30 | Whenever you're interacting with an external service, you of course need credentials to perform any actions. This holds true when you're creating any form of automated tasks, such as a workflow in GitHub. There are several ways to manage identities, including access tokens, shared passwords, and [Open ID Connect (OIDC)][oidc-docs], with the latter being the newest and preferred mechanism. The advantage to OIDC is it uses short-lived tokens and provides granular control over the operations which can be performed. 31 | 32 | Creating and setting up the credentials is typically a task performed by administrators. However, there are tools which can manage this for you, one of which we'll be taking advantage of! 33 | 34 | ## Asking Azure how to deploy to Azure 35 | 36 | We previously talked about [extensions for GitHub Copilot chat][extensions-copilot-chat], which allow you to interact with external services. These external services could provide access to information about your DevOps flow, database, and other resources. One such extension is the [Azure extension][azure-copilot-extension], which as the name implies allows you to interact with Azure. You can use the extension to get advice on how to deploy your application, check the status of services, and perform other operations. We'll use this extension to ask how to deploy our application. 37 | 38 | As we've done with other tasks, we don't have a specific prompt to use when talking with Azure, as part of the experience is to learn how best to interact with GitHub Copilot. The requirements for the deployment are: 39 | 40 | - Deploy the project to the cloud 41 | - Use a GitHub action to manage the deployment process 42 | 43 | 1. Open GitHub Copilot Chat. 44 | 2. Activate the Azure extension by typing `@azure`, selecting Tab then asking the extension how to perform the task you wish to perform (see the requirements above). 45 | 46 | > [!NOTE] 47 | > Since this is your first time using the extension, you will be prompted to signin to Azure. Follow the prompts as they appear. 48 | 49 | 3. You should receive a response which highlights the `azd` command, which can be used to both initialize a cloud environment and create the workflow. 50 | 51 | ## Overview of the response from Copilot 52 | 53 | The response from GitHub Copilot will likely contain instructions to use the following commands: 54 | 55 | - `azd init --from-code` to create the Azure configuration files using [bicep][bicep-docs]. 56 | - `azd auth login` to authenticate to Azure. 57 | - `azd pipeline config` to create the GitHub Workflow. 58 | 59 | [azd][azd-docs] is a commandline utility to help streamline the deployment process to Azure. We'll use it to: 60 | 61 | - generate the bicep file. 62 | - create the workflow file. 63 | - create and configure OIDC for the workflow. 64 | 65 | If you're curious about **azd** or Azure, you can always ask the extension using GitHub Copilot! 66 | 67 | ## Install azd 68 | 69 | Let's start by installing **azd**. 70 | 71 | 1. Run the command in the terminal to install **azd**: 72 | 73 | ```sh 74 | curl -fsSL https://aka.ms/install-azd.sh | bash 75 | ``` 76 | 77 | ## Create and configure the bicep file 78 | 79 | Bicep is a domain specific language (DSL) for defining Azure resources. It's dynamic, allowing you to ensure your environment is configured exactly as you need it. We're going to start by allowing **azd** create the bicep file, then make an update to ensure we have an environment variable available for the client to connect to the server. 80 | 81 | 1. Run the `init` command to create the bicep file. 82 | 83 | ```sh 84 | azd init --from-code 85 | ``` 86 | 87 | 2. Follow the prompts, accepting any defaults provided by the tool, and naming your namespace (which will be used to name the resource group and various resources in Azure) something unique. 88 | 3. Open the bicep file located at **infra**/**resources.bicep**. 89 | 4. Find the section (around line 130) which reads: 90 | 91 | ```bicep 92 | { 93 | name: 'PORT' 94 | value: '4321' 95 | } 96 | ``` 97 | 98 | 5. Create a new line below the closing `}` and add the following to create an environment variable with the URL of the newly created Flask server: 99 | 100 | ```bicep 101 | { 102 | name: 'API_SERVER_URL' 103 | value: 'https://${server.outputs.fqdn}' 104 | } 105 | ``` 106 | 107 | > [!NOTE] 108 | > While the syntax resembles JSON, it's not JSON. As a result, resist the urge to add commas to separate the values! 109 | 110 | ## Create the workflow 111 | 112 | `azd` can create and configure a workflow (or sometimes called a pipeline) for deploying your project. In particular it will: 113 | 114 | - create OIDC credentials to use for deployment. 115 | - define the YML file in the **workflows** folder. 116 | 117 | Let's let `azd` do its work! 118 | 119 | 1. Return to your terminal window, and run the following command to authenticate with `azd` 120 | 121 | ```sh 122 | azd auth login 123 | ``` 124 | 125 | 2. Follow the prompts to authenticate to Azure using the credentials you specified previously. 126 | 3. Create the pipeline by running the following command: 127 | 128 | ```sh 129 | azd pipeline config 130 | ``` 131 | 132 | 4. Follow the prompts, accepting the defaults. One of the prompts will ask if you wish to perform the deployment now - say yes! 133 | 5. Away your application goes to the cloud! 134 | 135 | ## Track the deployment and test your application 136 | 137 | The `azd pipeline config` command will create a new workflow file at **.github/workflows/azure-dev.yml**. Let's explore the workflow, track the action as it runs (this will take a few minutes), and test the application! 138 | 139 | 1. Open the workflow at **.github/workflows/azure-dev.yml**. 140 | 2. Note the `on` section, which contains the flags for `workflow_dispatch` (to support manual deployment), and `push` to automatically deploy when code is pushed to the **main** branch. 141 | 3. Note the core steps, which checkout your code, authenticate to Azure, create or update the infrastructure, then deploy the application. 142 | 4. If you have questions about what the workflow is doing, ask GitHub Copilot! 143 | 5. Navigate to your repository on GitHub. 144 | 6. Open the **Actions** tab, then the action named **.github/workflows/azure-dev.yml**. You should see the action running (the icon will be yellow under the **workflow runs** section). 145 | 7. Select the running workflow (which should be named **Configure Azure Developer Pipeline**). 146 | 8. Select the **build** step. 147 | 9. Track the deployment process, which will take about 5-10 minutes (good time for a water break!). 148 | 10. Once the process completes, expand the **Deploy Application** section. You should see the log indicating the client and server were both deployed: 149 | 150 | ``` 151 | Deploying service client 152 | Deploying service client (Building Docker image) 153 | Deploying service client (Tagging container image) 154 | Deploying service client (Tagging container image) 155 | Deploying service client (Logging into container registry) 156 | Deploying service client (Pushing container image) 157 | Deploying service client (Updating container app revision) 158 | Deploying service client (Fetching endpoints for container app service) 159 | (✓) Done: Deploying service client 160 | - Endpoint: https://client.delightfulfield-8f7ef050.westus.azurecontainerapps.io/ 161 | 162 | Deploying service server 163 | Acquiring pack cli 164 | Deploying service server (Building Docker image from source) 165 | Deploying service server (Tagging container image) 166 | Deploying service server (Tagging container image) 167 | Deploying service server (Logging into container registry) 168 | Deploying service server (Pushing container image) 169 | Deploying service server (Updating container app revision) 170 | Deploying service server (Fetching endpoints for container app service) 171 | (✓) Done: Deploying service server 172 | - Endpoint: https://server.delightfulfield-8f7ef050.westus.azurecontainerapps.io/ 173 | ``` 174 | 175 | 11. Select the Endpoint for the client. You should see your application! 176 | 177 | You've now deployed your project! 178 | 179 | ## Summary 180 | 181 | You've now created and configured a full CI/CD process. You implemented security checks, testing, and now deployment. As highlighted previously, enterprise CI/CD processes can be rather complex, but at their core they use the skills you explored during this workshop. 182 | 183 | ## Wrap-up and challenge 184 | 185 | Congratulations! You've gone through an entire DevOps process. You began by creating an issue to document the required work, then ensured everything was in place to run automatically. You performed the updates to the application, pushed everything to your repository, and merged it in! 186 | 187 | If you wish to continue exploring from here, there are a couple of tasks you could pursue: 188 | 189 | - Add more functionality to the website! There's a lot you could do, like adding on an adoption form or the ability to store images. 190 | - Migrate the database to something more powerful such as Postgres or SQL Server. 191 | 192 | Work with the workshop leaders as needed to ask questions and get guidance as you continue to build on the skills you learned today! 193 | 194 | ## Resources 195 | 196 | - [About security hardening with OpenID Connect][oidc-docs] 197 | - [Deploying with GitHub Actions][actions-deploy] 198 | - [What is the Azure Developer CLI?][azd-docs] 199 | 200 | | [← GitHub flow][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | 201 | |:-----------------------------------|------------------------------------------:| 202 | 203 | [actions-deploy]: https://docs.github.com/en/actions/use-cases-and-examples/deploying/deploying-with-github-actions 204 | [azd-docs]: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview?tabs=linux 205 | [azure-copilot-extension]: https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azure-github-copilot 206 | [bicep-docs]: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep 207 | [extensions-copilot-chat]: ./5-context.md 208 | [oidc-docs]: https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect 209 | [walkthrough-previous]: 7-github-flow.md 210 | [walkthrough-next]: ../README.md -------------------------------------------------------------------------------- /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/full-day/images/1-code-scanning-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/1-code-scanning-dialog.png -------------------------------------------------------------------------------- /content/full-day/images/1-code-scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/1-code-scanning.png -------------------------------------------------------------------------------- /content/full-day/images/1-dependabot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/1-dependabot.png -------------------------------------------------------------------------------- /content/full-day/images/1-secret-scanning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/1-secret-scanning.png -------------------------------------------------------------------------------- /content/full-day/images/3-open-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/3-open-browser.png -------------------------------------------------------------------------------- /content/full-day/images/3-reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/3-reload.png -------------------------------------------------------------------------------- /content/full-day/images/3-secrets-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/3-secrets-variables.png -------------------------------------------------------------------------------- /content/full-day/images/4-select-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/4-select-file.png -------------------------------------------------------------------------------- /content/full-day/images/5-copilot-chat-references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/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/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/content/full-day/images/7-generate-commit-message.png -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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! 🐶 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /scripts/start-app.ps1: -------------------------------------------------------------------------------- 1 | # Define color codes for PowerShell 2 | $Green = [System.ConsoleColor]::Green 3 | $DefaultColor = [System.ConsoleColor]::White 4 | 5 | # Store initial directory 6 | $InitialDir = Get-Location 7 | 8 | # Check if we're in scripts directory and navigate accordingly 9 | if ((Split-Path -Path (Get-Location) -Leaf) -eq "scripts") { 10 | Set-Location .. 11 | } 12 | 13 | Write-Host "Starting API (Flask) server..." 14 | 15 | # Create and activate virtual environment 16 | if (-not (Test-Path venv)) { 17 | python -m venv venv 18 | } 19 | if ($IsWindows) { 20 | & ./venv/Scripts/Activate.ps1 21 | } else { 22 | & bash -c "source ./venv/bin/activate" 23 | } 24 | 25 | Set-Location server -ErrorAction Stop 26 | pip install -r requirements.txt 27 | Set-Location .. 28 | $env:FLASK_DEBUG = 1 29 | $env:FLASK_PORT = 5100 30 | 31 | 32 | # Start Python server 33 | $pythonProcess = Start-Process python ` 34 | -WorkingDirectory (Join-Path $PSScriptRoot "..\server") ` 35 | -ArgumentList "app.py" ` 36 | -PassThru ` 37 | -NoNewWindow 38 | 39 | Write-Host "Starting client (Astro)..." 40 | Set-Location client -ErrorAction Stop 41 | npm install 42 | cd .. 43 | if ($IsWindows) { 44 | $npcCmd = "npm.cmd" 45 | } else { 46 | $npcCmd = "npm" 47 | } 48 | 49 | $clientProcess = Start-Process "$npcCmd" ` 50 | -WorkingDirectory (Join-Path $PSScriptRoot "..\client") ` 51 | -ArgumentList "run", "dev", "--", "--no-clearScreen" ` 52 | -PassThru ` 53 | -NoNewWindow 54 | 55 | # Sleep for 5 seconds 56 | Start-Sleep -Seconds 5 57 | 58 | # Display the server URLs 59 | Write-Host "`nServer (Flask) running at: http://localhost:5100" -ForegroundColor $Green 60 | Write-Host "Client (Astro) server running at: http://localhost:4321`n" -ForegroundColor $Green 61 | Write-Host "Ctrl+C to stop the servers" 62 | 63 | # Function to handle cleanup 64 | function Cleanup { 65 | Write-Host "Shutting down servers..." 66 | 67 | # Kill processes and their child processes 68 | if ($pythonProcess) { Stop-Process -Id $pythonProcess.Id -Force -ErrorAction SilentlyContinue } 69 | if ($clientProcess) { Stop-Process -Id $clientProcess.Id -Force -ErrorAction SilentlyContinue } 70 | 71 | # Deactivate virtual environment if it exists 72 | if (Test-Path Function:\deactivate) { 73 | deactivate 74 | } 75 | 76 | # Return to initial directory 77 | Set-Location $InitialDir 78 | exit 79 | } 80 | 81 | # Register cleanup for script termination 82 | $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action { Cleanup } 83 | 84 | try { 85 | # Keep the script running until Ctrl+C 86 | Wait-Process -Id $pythonProcess.Id 87 | } finally { 88 | Cleanup 89 | } 90 | -------------------------------------------------------------------------------- /scripts/start-app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define color codes 4 | GREEN='\033[0;32m' 5 | NC='\033[0m' # No Color 6 | 7 | # Store initial directory 8 | INITIAL_DIR=$(pwd) 9 | 10 | # Check if we're in scripts directory and navigate accordingly 11 | if [[ $(basename $(pwd)) == "scripts" ]]; then 12 | cd .. 13 | fi 14 | 15 | echo "Starting API (Flask) server..." 16 | 17 | # Check OS and use appropriate Python command 18 | if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 19 | # Windows 20 | py -m venv venv 21 | source venv/Scripts/activate || . venv/Scripts/activate 22 | else 23 | # macOS/Linux 24 | python3 -m venv venv 25 | source venv/bin/activate || . venv/bin/activate 26 | fi 27 | 28 | pip install -r server/requirements.txt 29 | cd server || { 30 | echo "Error: server directory not found" 31 | cd "$INITIAL_DIR" 32 | exit 1 33 | } 34 | export FLASK_DEBUG=1 35 | export FLASK_PORT=5100 36 | 37 | # Use appropriate Python command based on OS 38 | if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 39 | py app.py & 40 | else 41 | python3 app.py & 42 | fi 43 | 44 | # Store the Python server process ID 45 | SERVER_PID=$! 46 | 47 | echo "Starting client (Astro)..." 48 | cd ../client || { 49 | echo "Error: client directory not found" 50 | cd "$INITIAL_DIR" 51 | exit 1 52 | } 53 | npm install 54 | npm run dev -- --no-clearScreen & 55 | 56 | # Store the SvelteKit server process ID 57 | CLIENT_PID=$! 58 | 59 | # Sleep for 3 seconds 60 | sleep 5 61 | 62 | # Display the server URLs 63 | echo -e "\n${GREEN}Server (Flask) running at: http://localhost:5100${NC}" 64 | echo -e "${GREEN}Client (Astro) server running at: http://localhost:4321${NC}\n" 65 | 66 | echo "Ctl-C to stop the servers" 67 | 68 | # Function to handle script termination 69 | cleanup() { 70 | echo "Shutting down servers..." 71 | 72 | # Kill processes and their child processes 73 | if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then 74 | taskkill //F //T //PID $SERVER_PID 2>/dev/null 75 | taskkill //F //T //PID $CLIENT_PID 2>/dev/null 76 | else 77 | # Send SIGTERM first to allow graceful shutdown 78 | kill -TERM $SERVER_PID 2>/dev/null 79 | kill -TERM $CLIENT_PID 2>/dev/null 80 | 81 | # Wait briefly for graceful shutdown 82 | sleep 2 83 | 84 | # Then force kill if still running 85 | if ps -p $SERVER_PID > /dev/null 2>&1; then 86 | pkill -P $SERVER_PID 2>/dev/null 87 | kill -9 $SERVER_PID 2>/dev/null 88 | fi 89 | 90 | if ps -p $CLIENT_PID > /dev/null 2>&1; then 91 | pkill -P $CLIENT_PID 2>/dev/null 92 | kill -9 $CLIENT_PID 2>/dev/null 93 | fi 94 | fi 95 | 96 | # Deactivate virtual environment if active 97 | if [[ -n "${VIRTUAL_ENV}" ]]; then 98 | deactivate 99 | fi 100 | 101 | # Return to initial directory 102 | cd "$INITIAL_DIR" 103 | exit 0 104 | } 105 | 106 | # Trap multiple signals 107 | trap cleanup SIGINT SIGTERM SIGQUIT EXIT 108 | 109 | # Keep the script running 110 | wait -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /server/dogshelter.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/github-samples/pets-workshop/06e4b8bd06dbe8f4aa5c01d934cc90dd496e89f2/server/dogshelter.db -------------------------------------------------------------------------------- /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() -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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'' -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /server/models/dogs.csv: -------------------------------------------------------------------------------- 1 | Name,Description,Age,Gender 2 | Max,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",1,Male 3 | Bella,"Energetic and curious, always exploring and eager to meet new friends. Loves outdoor adventures and keeps you on your toes.",9,Male 4 | Charlie,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",15,Male 5 | Lucy,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",5,Male 6 | Cooper,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,8,Male 7 | Daisy,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",15,Female 8 | Buddy,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",2,Male 9 | Luna,"Shy but loving, may take time to warm up but becomes deeply attached once comfortable. A loyal and sweet companion.",5,Male 10 | Rocky,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",4,Male 11 | Sadie,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",12,Male 12 | Milo,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",4,Female 13 | Molly,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",9,Female 14 | Bear,"Energetic and curious, always exploring and eager to meet new friends. Loves outdoor adventures and keeps you on your toes.",13,Male 15 | Bailey,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",7,Male 16 | Duke,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",13,Male 17 | Maggie,"Shy but loving, may take time to warm up but becomes deeply attached once comfortable. A loyal and sweet companion.",10,Female 18 | Tucker,"Energetic and curious, always exploring and eager to meet new friends. Loves outdoor adventures and keeps you on your toes.",2,Male 19 | Chloe,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,9,Female 20 | Jack,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",8,Female 21 | Sophie,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",14,Male 22 | Oliver,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",10,Male 23 | Zoey,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",13,Male 24 | Bentley,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",13,Male 25 | Lily,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",6,Female 26 | Jake,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",8,Male 27 | Stella,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",6,Male 28 | Toby,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",9,Male 29 | Penny,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",15,Male 30 | Zeus,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",12,Female 31 | Gracie,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",14,Female 32 | Harley,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",3,Female 33 | Roxy,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",4,Female 34 | Leo,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",15,Male 35 | Abby,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",5,Male 36 | Jax,"Energetic and curious, always exploring and eager to meet new friends. Loves outdoor adventures and keeps you on your toes.",4,Male 37 | Rosie,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",2,Male 38 | Finn,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",9,Male 39 | Ruby,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",10,Male 40 | Winston,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",3,Male 41 | Ellie,"Shy but loving, may take time to warm up but becomes deeply attached once comfortable. A loyal and sweet companion.",9,Female 42 | Murphy,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",2,Male 43 | Lola,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",7,Female 44 | Louie,"Shy but loving, may take time to warm up but becomes deeply attached once comfortable. A loyal and sweet companion.",2,Female 45 | Mia,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",4,Female 46 | Henry,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",8,Female 47 | Nala,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",7,Male 48 | Oscar,"Shy but loving, may take time to warm up but becomes deeply attached once comfortable. A loyal and sweet companion.",7,Male 49 | Pepper,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",9,Female 50 | Sam,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",1,Female 51 | Ginger,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,2,Male 52 | Scout,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",1,Female 53 | Olive,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",10,Male 54 | Dexter,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",3,Male 55 | Harper,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",8,Male 56 | Blue,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",5,Female 57 | Emma,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",6,Female 58 | Buster,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",1,Male 59 | Annie,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",3,Male 60 | Lucky,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",1,Female 61 | Coco,"Energetic and curious, always exploring and eager to meet new friends. Loves outdoor adventures and keeps you on your toes.",5,Female 62 | Moose,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,3,Female 63 | Belle,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",8,Female 64 | Thor,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",10,Male 65 | Sasha,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",1,Male 66 | Gus,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",10,Female 67 | Izzy,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",5,Female 68 | George,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",2,Male 69 | Remi,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",9,Female 70 | Boomer,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",5,Female 71 | Ella,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",7,Female 72 | Sammy,"Shy but loving, may take time to warm up but becomes deeply attached once comfortable. A loyal and sweet companion.",8,Male 73 | Willow,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",15,Female 74 | Bruno,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",6,Female 75 | Piper,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,6,Male 76 | Shadow,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",10,Female 77 | Riley,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",3,Male 78 | Chance,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",5,Male 79 | Nova,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,5,Female 80 | Tank,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",7,Male 81 | Minnie,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,9,Female 82 | Rusty,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",3,Male 83 | Millie,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",13,Female 84 | Frankie,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",14,Male 85 | Hazel,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",5,Male 86 | Apollo,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",14,Male 87 | Holly,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",9,Male 88 | Otis,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",8,Female 89 | Marley,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",13,Male 90 | Ace,"Alert and brave, always ready to protect their home and loved ones. Despite their bravery, they have a soft side for cuddles.",8,Female 91 | Honey,"Gentle and affectionate, always seeking cuddles and attention. Loves to relax but will never say no to a good walk.",10,Female 92 | Chester,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",7,Female 93 | Zoe,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",15,Female 94 | Hunter,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",8,Male 95 | Callie,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",14,Male 96 | Prince,"Active and fun-loving, always ready to chase a ball or go for a run. Great for owners with an active lifestyle.",1,Male 97 | Missy,"Mischievous but sweet, often found getting into funny trouble around the house. Full of personality and love.",7,Female 98 | Koda,"Loyal and protective, always watching over their loved ones. A perfect companion for someone looking for a devoted friend.",5,Female 99 | Delilah,A friendly and playful dog who loves spending time with people and other animals. Always ready for a game of fetch and enjoys long walks.,8,Female 100 | Ollie,"Calm and loving, this dog prefers lounging by your side but still enjoys occasional playtime. Very affectionate and great with families.",2,Male 101 | Layla,"Smart and attentive, always ready to learn new tricks and please their owner. A quick learner who thrives on mental stimulation.",7,Male 102 | -------------------------------------------------------------------------------- /server/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | sqlalchemy 3 | flask_sqlalchemy 4 | flask-cors -------------------------------------------------------------------------------- /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() -------------------------------------------------------------------------------- /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() --------------------------------------------------------------------------------