diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 60a72d79..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.github/instructions/bicep-code-best-practices.instructions.md b/.github/instructions/bicep-code-best-practices.instructions.md deleted file mode 100644 index 9b07e975..00000000 --- a/.github/instructions/bicep-code-best-practices.instructions.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -description: 'Infrastructure as Code with Bicep' -applyTo: '**/*.bicep' ---- - -## Naming Conventions - -- When writing Bicep code, use lowerCamelCase for all names (variables, parameters, resources) -- Use resource type descriptive symbolic names (e.g., 'storageAccount' not 'storageAccountName') -- Avoid using 'name' in a symbolic name as it represents the resource, not the resource's name -- Avoid distinguishing variables and parameters by the use of suffixes - -## Structure and Declaration - -- Always declare parameters at the top of files with @description decorators -- Use latest stable API versions for all resources -- Use descriptive @description decorators for all parameters -- Specify minimum and maximum character length for naming parameters - -## Parameters - -- Set default values that are safe for test environments (use low-cost pricing tiers) -- Use @allowed decorator sparingly to avoid blocking valid deployments -- Use parameters for settings that change between deployments - -## Variables - -- Variables automatically infer type from the resolved value -- Use variables to contain complex expressions instead of embedding them directly in resource properties - -## Resource References - -- Use symbolic names for resource references instead of reference() or resourceId() functions -- Create resource dependencies through symbolic names (resourceA.id) not explicit dependsOn -- For accessing properties from other resources, use the 'existing' keyword instead of passing values through outputs - -## Resource Names - -- Use template expressions with uniqueString() to create meaningful and unique resource names -- Add prefixes to uniqueString() results since some resources don't allow names starting with numbers - -## Child Resources - -- Avoid excessive nesting of child resources -- Use parent property or nesting instead of constructing resource names for child resources - -## Security - -- Never include secrets or keys in outputs -- Use resource properties directly in outputs (e.g., storageAccount.properties.primaryEndpoints) - -## Documentation - -- Include helpful // comments within your Bicep files to improve readability \ No newline at end of file diff --git a/.github/instructions/terraform-azure.instructions.md b/.github/instructions/terraform-azure.instructions.md deleted file mode 100644 index b6c48268..00000000 --- a/.github/instructions/terraform-azure.instructions.md +++ /dev/null @@ -1,254 +0,0 @@ ---- -description: 'Create or modify solutions built using Terraform on Azure.' -applyTo: '**/*.terraform, **/*.tf, **/*.tfvars, **/*.tflint.hcl, **/*.tfstate, **/*.tf.json, **/*.tfvars.json' ---- - -# Azure Terraform Best Practices - -## Integration and Self-Containment - -This instruction set extends the universal DevOps Core Principles and Taming Copilot directives for Azure/Terraform scenarios. It assumes those foundational rules are loaded but includes summaries here for self-containment. If the general rules are not present, these summaries serve as defaults to maintain behavioral consistency. - -### Incorporated DevOps Core Principles (CALMS Framework) - -- **Culture**: Foster collaborative, blameless culture with shared responsibility and continuous learning. -- **Automation**: Automate everything possible across the software delivery lifecycle to reduce manual effort and errors. -- **Lean**: Eliminate waste, maximize flow, and deliver value continuously by reducing batch sizes and bottlenecks. -- **Measurement**: Measure everything relevant (e.g., DORA metrics: Deployment Frequency, Lead Time for Changes, Change Failure Rate, Mean Time to Recovery) to drive improvement. -- **Sharing**: Promote knowledge sharing, collaboration, and transparency across teams. - -### Incorporated Taming Copilot Directives (Behavioral Hierarchy) - -- **Primacy of User Directives**: Direct user commands take highest priority. -- **Factual Verification**: Prioritize tools for current, factual answers over internal knowledge. -- **Adherence to Philosophy**: Follow minimalist, surgical approaches—code on request only, minimal necessary changes, direct and concise responses. -- **Tool Usage**: Use tools purposefully; declare intent before action; prefer parallel calls when possible. - -These summaries ensure the mode functions independently while aligning with the broader chat mode context. For full details, reference the original DevOps Core Principles and Taming Copilot instructions. - -## Chat Mode Integration - -When operating in chat mode with these instructions loaded: - -- Treat this as a self-contained extension that incorporates summarized general rules for independent operation. -- Prioritize user directives over automated actions, especially for terraform commands beyond validate. -- Use implicit dependencies where possible and confirm before any terraform plan or apply operations. -- Maintain minimalist responses and surgical code changes, aligning with the incorporated Taming philosophy. -- **Planning Files Awareness**: Always check for planning files in the `.terraform-planning-files/` folder (if present). Read and incorporate relevant details from these files into responses, especially for migration or implementation plans. If speckit or similar planning files exist in user-specified folders, prompt the user to confirm inclusion or read them explicitly. - -## 1. Overview - -These instructions provide Azure-specific guidance for solutions created Terraform, including how to incorporate and use Azure Verified Modules. - -For general Terraform conventions, see [terraform.instructions.md](terraform.instructions.md). - -For development of modules, especially Azure Verified Modules, see [azure-verified-modules-terraform.instructions.md](azure-verified-modules-terraform.instructions.md). - -## 2. Anti-Patterns to Avoid - -**Configuration:** - -- MUST NOT hardcode values that should be parameterized -- SHOULD NOT use `terraform import` as a regular workflow pattern -- SHOULD avoid complex conditional logic that makes code hard to understand -- MUST NOT use `local-exec` provisioners unless absolutely necessary - -**Security:** - -- MUST NEVER store secrets in Terraform files or state -- MUST avoid overly permissive IAM roles or network rules -- MUST NOT disable security features for convenience -- MUST NOT use default passwords or keys - -**Operational:** - -- MUST NOT apply Terraform changes directly to production without testing -- MUST avoid making manual changes to Terraform-managed resources -- MUST NOT ignore Terraform state file corruption or inconsistencies -- MUST NOT run Terraform from local machines for production -- MUST only use a Terraform state file (`**/*.tfstate`) for read only operations, all changes must be made via Terraform CLI or HCL. -- MUST only use the contents of `**/.terraform/**` (fetched modules and providers) for read only operations. - -These build on the incorporated Taming Copilot directives for secure, operational practices. - ---- - -## 3. Organize Code Cleanly - -Structure Terraform configurations with logical file separation: - -- Use `main.tf` for resources -- Use `variables.tf` for inputs -- Use `outputs.tf` for outputs -- Use `terraform.tf` for provider configurations -- Use `locals.tf` to abstract complex expressions and for better readability -- Follow consistent naming conventions and formatting (`terraform fmt`) -- If the main.tf or variables.tf files grow too large, split them into multiple files by resource type or function (e.g., `main.networking.tf`, `main.storage.tf` - move equivalent variables to `variables.networking.tf`, etc.) - -Use `snake_casing` for variables and module names. - -## 4. Use Azure Verified Modules (AVM) - -Any significant resource should use an AVM if available. AVMs are designed to be aligned to the Well Architected Framework, are supported and maintained by Microsoft helping reduce the amount of code to be maintained. Information about how to discover these is available in [Azure Verified Modules for Terraform](azure-verified-modules-terraform.instructions.md). - -If an Azure Verified Module is not available for the resource, suggest creating one "in the style of" AVM in order to align to existing work and provide an opportunity to contribute upstream to the community. - -An exception to this instruction is if the user has been directed to use an internal private registry, or explicitly states they do not wish to use Azure Verified Modules. - -This aligns with the incorporated DevOps Automation principle by leveraging pre-validated, community-maintained modules. - -## 5. Variable and Code Style Standards - -Follow AVM-aligned coding standards in solution code to maintain consistency: - -- **Variable naming**: Use snake_case for all variable names (per TFNFR4 and TFNFR16). Be descriptive and consistent with naming conventions. -- **Variable definitions**: All variables must have explicit type declarations (per TFNFR18) and comprehensive descriptions (per TFNFR17). Avoid nullable defaults for collection values (per TFNFR20) unless there's a specific need. -- **Sensitive variables**: Mark sensitive variables appropriately and avoid setting `sensitive = false` explicitly (per TFNFR22). Handle sensitive default values correctly (per TFNFR23). -- **Dynamic blocks**: Use dynamic blocks for optional nested objects where appropriate (per TFNFR12), and leverage `coalesce` or `try` functions for default values (per TFNFR13). -- **Code organization**: Consider using `locals.tf` specifically for local values (per TFNFR31) and ensure precise typing for locals (per TFNFR33). - -## 6. Secrets - -The best secret is one that does not need to be stored. e.g. use Managed Identities rather than passwords or keys. - -Use `ephemeral` secrets with write-only parameters when supported (Terraform v1.11+) to avoid storing secrets in state files. Consult module documentation for availability. - -Where secrets are required, store in Key Vault unless directed to use a different service. - -Never write secrets to local filesystems or commit to git. - -Mark sensitive values appropriately, isolate them from other attributes, and avoid outputting sensitive data unless absolutely necessary. Follow TFNFR19, TFNFR22, and TFNFR23. - -## 7. Outputs - -- **Avoid unnecessary outputs**, only use these to expose information needed by other configurations. -- Use `sensitive = true` for outputs containing secrets -- Provide clear descriptions for all outputs - -```hcl -output "resource_group_name" { - description = "Name of the created resource group" - value = azurerm_resource_group.example.name -} - -output "virtual_network_id" { - description = "ID of the virtual network" - value = azurerm_virtual_network.example.id -} -``` - -## 8. Local Values Usage - -- Use locals for computed values and complex expressions -- Improve readability by extracting repeated expressions -- Combine related values into structured locals - -```hcl -locals { - common_tags = { - Environment = var.environment - Project = var.project_name - Owner = var.owner - CreatedBy = "terraform" - } - - resource_name_prefix = "${var.project_name}-${var.environment}" - location_short = substr(var.location, 0, 3) -} -``` - -## 9. Follow recommended Terraform practices - -- **Redundant depends_on Detection**: Search and remove `depends_on` where the dependent resource is already referenced implicitly in the same resource block. Retain `depends_on` only where it is explicitly required. Never depend on module outputs. - -- **Iteration**: Use `count` for 0-1 resources, `for_each` for multiple resources. Prefer maps for stable resource addresses. Align with TFNFR7. - -- **Data sources**: Acceptable in root modules but avoid in reusable modules. Prefer explicit module parameters over data source lookups. - -- **Parameterization**: Use strongly typed variables with explicit `type` declarations (TFNFR18), comprehensive descriptions (TFNFR17), and non-nullable defaults (TFNFR20). Leverage AVM-exposed variables. - -- **Versioning**: Target latest stable Terraform and Azure provider versions. Specify versions in code and keep updated (TFFR3). - -## 10. Folder Structure - -Use a consistent folder structure for Terraform configurations. - -Use tfvars to modify environmental differences. In general, aim to keep environments similar whilst cost optimising for non-production environments. - -Antipattern - branch per environment, repository per environment, folder per environment - or similar layouts that make it hard to test the root folder logic between environments. - -Be aware of tools such as Terragrunt which may influence this design. - -A **suggested** structure is: - -```text -my-azure-app/ -├── infra/ # Terraform root module (AZD compatible) -│ ├── main.tf # Core resources -│ ├── variables.tf # Input variables -│ ├── outputs.tf # Outputs -│ ├── terraform.tf # Provider configuration -│ ├── locals.tf # Local values -│ └── environments/ # Environment-specific configurations -│ ├── dev.tfvars # Development environment -│ ├── test.tfvars # Test environment -│ └── prod.tfvars # Production environment -├── .github/workflows/ # CI/CD pipelines (if using github) -├── .azdo/ # CI/CD pipelines (suggested if using Azure DevOps) -└── README.md # Documentation -``` - -Never change the folder structure without direct agreement with the user. - -Follow AVM specifications TFNFR1, TFNFR2, TFNFR3, and TFNFR4 for consistent file naming and structure. - -## Azure-Specific Best Practices - -### Resource Naming and Tagging - -- Follow [Azure naming conventions](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming) -- Use consistent region naming and variables for multi-region deployments -- Implement consistent tagging. - -### Resource Group Strategy - -- Use existing resource groups when specified -- Create new resource groups only when necessary and with confirmation -- Use descriptive names indicating purpose and environment - -### Networking Considerations - -- Validate existing VNet/subnet IDs before creating new network resources (for example, is this solution being deployed into an existing hub & spoke landing zone) -- Use NSGs and ASGs appropriately -- Implement private endpoints for PaaS services when required, use resource firewall restrictions to restrict public access otherwise. Comment exceptions where public endpoints are required. - -### Security and Compliance - -- Use Managed Identities instead of service principals -- Implement Key Vault with appropriate RBAC. -- Enable diagnostic settings for audit trails -- Follow principle of least privilege - -## Cost Management - -- Confirm budget approval for expensive resources -- Use environment-appropriate sizing (dev vs prod) -- Ask for cost constraints if not specified - -## State Management - -- Use remote backend (Azure Storage) with state locking -- Never commit state files to source control -- Enable encryption at rest and in transit - -## Validation - -- Do an inventory of existing resources and offer to remove unused resource blocks. -- Run `terraform validate` to check syntax -- Ask before running `terraform plan`. Terraform plan will require a subscription ID, this should be sourced from the ARM_SUBSCRIPTION_ID environment variable, *NOT* coded in the provider block. -- Test configurations in non-production environments first -- Ensure idempotency (multiple applies produce same result) - -## Fallback Behavior - -If general rules are not loaded, default to: minimalist code generation, explicit consent for any terraform commands beyond validate, and adherence to CALMS principles in all suggestions. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b1ad0d7c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI + +on: + push: + branches: ["**"] + pull_request: + +jobs: + test: + name: Server tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r app/server/requirements.txt pytest + - name: Run server tests + run: python -m pytest app/server/test_app.py -v + + lint: + name: Lint server + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Lint for unsafe SQL construction + run: | + python - <<'PY' + from pathlib import Path + + target = Path("app/server/app.py") + source = target.read_text(encoding="utf-8") + needle = "SELECT id, name FROM dogs WHERE name LIKE '%" + + if needle in source and '" + name + "' in source: + print(f"{target}: search_dogs builds SQL with string concatenation") + raise SystemExit(1) + + print("No unsafe SQL string concatenation found.") + PY diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f949c76..853ef0e5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,4 +10,11 @@ "prebuild" ], "github.copilot.nextEditSuggestions.enabled": true, + "search.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.DS_Store": true, + "**/Thumbs.db": true + } } \ No newline at end of file diff --git a/DEMO-SETUP.md b/DEMO-SETUP.md new file mode 100644 index 00000000..0f091504 --- /dev/null +++ b/DEMO-SETUP.md @@ -0,0 +1,56 @@ +# Demo Setup + +This repository is a training sandbox for live GitHub Copilot demos. It deliberately contains synthetic data, planted test failures, an insecure endpoint, and a failing CI workflow. + +## Demo asset map + +| Demo asset | File | Training module | +| --- | --- | --- | +| Tutorial material removed for cleaner code search | deleted workshop tutorial directory | `@workspace` / `#codebase` context demos | +| Live-authored instructions start absent | `.github/copilot-instructions.md` intentionally missing | Custom instructions authoring | +| Unrelated instruction files removed | deleted files under `.github/instructions/` | Customization hygiene | +| Short repo overview | [README.md](README.md) | Repo summarization | +| Firmware-style code sample | [app/firmware/kennel_door_controller.c](app/firmware/kennel_door_controller.c), [app/firmware/kennel_door_controller.h](app/firmware/kennel_door_controller.h) | Legacy code comprehension | +| Notebook with committed synthetic contact data, an unexplained cell, and an orphaned cell | [notebooks/shelter_intake_analysis.ipynb](notebooks/shelter_intake_analysis.ipynb) | Notebook review and privacy demos | +| Insecure dog search endpoint | [app/server/app.py](app/server/app.py) | Security review | +| Four distinct test failures across nine tests | [app/server/test_app.py](app/server/test_app.py) | Debugging failing tests | +| CI workflow with test job and failing lint job | [.github/workflows/ci.yml](.github/workflows/ci.yml) | Pipeline debugging | +| Demo reset script and facilitator notes | [scripts/reset-demo.sh](scripts/reset-demo.sh), [scripts/README.md](scripts/README.md) | Session reset workflow | + +## Commands to run before a session + +```bash +python -m pip install -r app/server/requirements.txt pytest +python app/server/utils/seed_database.py +python app/server/app.py + +cd app/client +npm install +npm run dev +``` + +Optional checks before the audience joins: + +```bash +python -m pytest app/server/test_app.py +cd app/client && npm run test:e2e +``` + +## Commands to tag and reset the baseline + +```bash +git add -A +git commit -m "Prepare training demo baseline" +git tag demo-baseline +``` + +```bash +bash scripts/reset-demo.sh +``` + +The reset script discards tracked changes, removes untracked files, deletes `.github/copilot-instructions.md` if it exists, and hard-resets to `demo-baseline`. It refuses to run on `main` when `origin` points at `github-samples/pets-workshop`. + +## Incomplete items + +- No repo task items remain incomplete. +- The Astro build command is included above, but it was not runnable in this environment because `npm` is not installed on PATH. diff --git a/LINKS.md b/LINKS.md new file mode 100644 index 00000000..fee885be --- /dev/null +++ b/LINKS.md @@ -0,0 +1 @@ +https://code.visualstudio.com/docs/agents/reference/ai-features-cheat-sheet \ No newline at end of file diff --git a/README.md b/README.md index d0fd39c9..e4bc8fac 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,36 @@ -# Pets workshop +# Pets Workshop — Training Sandbox -This repository contains the project for three 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 an [Astro](https://astro.build/) frontend using [Tailwind CSS](https://tailwindcss.com/). +> ⚠️ **This is a training sandbox, not production code.** It is used for live GitHub Copilot demos and deliberately contains bugs, insecure code and synthetic data. Do not deploy it or reuse its code. -The available workshops are: +A small website for a fictional dog shelter: a [Flask](https://flask.palletsprojects.com/) + [SQLAlchemy](https://www.sqlalchemy.org/) backend and an [Astro](https://astro.build/) + [Tailwind CSS](https://tailwindcss.com/) frontend. -- **[One hour](./content/1-hour/README.md)** — focused on GitHub Copilot -- **[Full-day](./content/full-day/README.md)** — a full day-in-the-life of a developer using GitHub for their DevOps processes -- **[GitHub Actions](./content/github-actions/README.md)** — CI/CD pipelines from running tests to deploying to Azure +## Run the server -## Getting started +```bash +cd app/server +pip install -r requirements.txt +python utils/seed_database.py # create and seed the local SQLite database +python app.py # serves on http://localhost:5100 +``` -> **[Get started learning about development with GitHub!](./content/README.md)** +## Run the client -## License +```bash +cd app/client +npm install +npm run dev # serves on http://localhost:4321 +``` -This project is licensed under the terms of the MIT open source license. Please refer to the [LICENSE](./LICENSE) for the full terms. +## Run the tests -## Maintainers +```bash +# Server unit tests +python -m pytest app/server/test_app.py -You can find the list of maintainers in [CODEOWNERS](./.github/CODEOWNERS). +# Client end-to-end tests +cd app/client && npm run test:e2e +``` -## Support +## License -This project is provided as-is, and may be updated over time. If you have questions, please open an issue. +Licensed under the MIT license. See [LICENSE](./LICENSE). diff --git a/app/firmware/kennel_door_controller.c b/app/firmware/kennel_door_controller.c new file mode 100644 index 00000000..a3368d91 --- /dev/null +++ b/app/firmware/kennel_door_controller.c @@ -0,0 +1,151 @@ +#include "kennel_door_controller.h" + +static uint16_t dt(uint16_t a, uint16_t b) { + if (a >= b) return a - b; + return (uint16_t)(65535u - b + a + 1u); +} + +static void deb(kd_ctx_t *c, uint8_t in) { + uint8_t i; + c->raw = in; + for (i = 0; i < 5; i++) { + if (in & (1u << i)) { + if (c->cnt[i] < 4) c->cnt[i]++; + } else { + if (c->cnt[i] > 0) c->cnt[i]--; + } + if (c->cnt[i] >= 3) c->deb |= (1u << i); + else if (c->cnt[i] == 0) c->deb &= ~(1u << i); + } +} + +void kd_init(kd_ctx_t *c) { + uint8_t i; + c->st = KD_S_INIT; + c->raw = 0; + c->deb = 0; + for (i = 0; i < 5; i++) c->cnt[i] = 0; + c->t0 = 0; + c->hold = 0; + c->out = 0; + c->err = 0; + c->retries = 0; +} + +uint8_t kd_fault(const kd_ctx_t *c) { + return c->st == KD_S_FAULT ? c->err : 0; +} + +uint8_t kd_step(kd_ctx_t *c, uint8_t inputs, uint16_t now) { + uint8_t d; + deb(c, inputs); + d = c->deb; + + if (c->st != KD_S_FAULT && (d & KD_IN_OBSTRUCT) && (c->out & KD_OUT_MOTOR_CLOSE)) { + c->out = KD_OUT_MOTOR_OPEN; + c->st = KD_S_OPENING; + c->t0 = now; + c->retries++; + if (c->retries > 3) { + c->st = KD_S_FAULT; + c->err = 0x21; + c->out = KD_OUT_FAULT_LED; + } + return c->out; + } + + switch (c->st) { + case KD_S_INIT: + c->out = 0; + if (d & KD_IN_CLOSE_SW) { + c->st = KD_S_CLOSED; + c->out = KD_OUT_LATCH; + } else if (d & KD_IN_OPEN_SW) { + c->st = KD_S_OPEN; + c->hold = now; + } else { + c->out = KD_OUT_MOTOR_CLOSE; + c->st = KD_S_CLOSING; + c->t0 = now; + } + break; + + case KD_S_CLOSED: + c->out = KD_OUT_LATCH; + c->retries = 0; + if ((d & KD_IN_BUTTON) && !(d & KD_IN_LOCK)) { + c->out = KD_OUT_MOTOR_OPEN; + c->st = KD_S_OPENING; + c->t0 = now; + } + break; + + case KD_S_OPENING: + c->out = KD_OUT_MOTOR_OPEN; + if (d & KD_IN_OPEN_SW) { + c->out = 0; + c->st = KD_S_OPEN; + c->hold = now; + } else if (dt(now, c->t0) > 1200) { + if (c->retries < 2) { + c->retries++; + c->t0 = now; + } else { + c->st = KD_S_FAULT; + c->err = 0x11; + c->out = KD_OUT_FAULT_LED; + } + } + break; + + case KD_S_OPEN: + c->out = 0; + if (d & KD_IN_BUTTON) { + c->hold = now; + } else if (dt(now, c->hold) > 8000) { + if (d & KD_IN_OBSTRUCT) { + c->hold = now; + } else { + c->out = KD_OUT_MOTOR_CLOSE; + c->st = KD_S_CLOSING; + c->t0 = now; + } + } + break; + + case KD_S_CLOSING: + c->out = KD_OUT_MOTOR_CLOSE; + if (d & KD_IN_CLOSE_SW) { + c->out = KD_OUT_LATCH; + c->st = KD_S_CLOSED; + c->retries = 0; + } else if (dt(now, c->t0) > 1500) { + c->st = KD_S_FAULT; + c->err = 0x12; + c->out = KD_OUT_FAULT_LED; + } + break; + + case KD_S_HOLD: + c->out = 0; + if (!(d & KD_IN_OBSTRUCT)) { + c->out = KD_OUT_MOTOR_CLOSE; + c->st = KD_S_CLOSING; + c->t0 = now; + } + break; + + case KD_S_FAULT: + default: + c->out = KD_OUT_FAULT_LED; + if ((d & KD_IN_LOCK) && (d & KD_IN_BUTTON) && (d & KD_IN_CLOSE_SW)) { + c->err = 0; + c->retries = 0; + c->st = KD_S_CLOSED; + c->out = KD_OUT_LATCH; + } + break; + } + + return c->out; +} diff --git a/app/firmware/kennel_door_controller.h b/app/firmware/kennel_door_controller.h new file mode 100644 index 00000000..e12bef92 --- /dev/null +++ b/app/firmware/kennel_door_controller.h @@ -0,0 +1,43 @@ +#ifndef KENNEL_DOOR_CONTROLLER_H +#define KENNEL_DOOR_CONTROLLER_H + +#include + +#define KD_IN_OPEN_SW 0x01 +#define KD_IN_CLOSE_SW 0x02 +#define KD_IN_OBSTRUCT 0x04 +#define KD_IN_BUTTON 0x08 +#define KD_IN_LOCK 0x10 + +#define KD_OUT_MOTOR_OPEN 0x01 +#define KD_OUT_MOTOR_CLOSE 0x02 +#define KD_OUT_LATCH 0x04 +#define KD_OUT_FAULT_LED 0x08 + +typedef enum { + KD_S_INIT = 0, + KD_S_CLOSED, + KD_S_OPENING, + KD_S_OPEN, + KD_S_CLOSING, + KD_S_HOLD, + KD_S_FAULT +} kd_state_t; + +typedef struct { + kd_state_t st; + uint8_t raw; + uint8_t deb; + uint8_t cnt[5]; + uint16_t t0; + uint16_t hold; + uint8_t out; + uint8_t err; + uint8_t retries; +} kd_ctx_t; + +void kd_init(kd_ctx_t *c); +uint8_t kd_step(kd_ctx_t *c, uint8_t inputs, uint16_t now); +uint8_t kd_fault(const kd_ctx_t *c); + +#endif diff --git a/app/server/app.py b/app/server/app.py index c7aa30e9..f798687c 100644 --- a/app/server/app.py +++ b/app/server/app.py @@ -1,6 +1,7 @@ import os from typing import Dict, List, Any, Optional from flask import Flask, jsonify, request, Response +from sqlalchemy import text from models import init_db, db, Dog, Breed # Get the server directory path @@ -77,7 +78,16 @@ def get_dog(id: int) -> tuple[Response, int] | Response: return jsonify(dog) -## HERE +@app.route('/api/dogs/search', methods=['GET']) +def search_dogs() -> tuple[Response, int] | Response: + name = request.args.get('name', '') + sql = "SELECT id, name FROM dogs WHERE name LIKE '%" + name + "%' ORDER BY name" + try: + rows = db.session.execute(text(sql)).fetchall() + results: List[Dict[str, Any]] = [{'id': row[0], 'name': row[1]} for row in rows] + return jsonify(results) + except Exception as e: + return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(debug=True, port=5100) # Port 5100 to avoid macOS conflicts diff --git a/app/server/test_app.py b/app/server/test_app.py index 79464154..1fbb2c3f 100644 --- a/app/server/test_app.py +++ b/app/server/test_app.py @@ -5,6 +5,8 @@ # filepath: app/server/test_app.py class TestApp(unittest.TestCase): + last_search = None + def setUp(self): # Create a test client using Flask's test client self.app = app.test_client() @@ -32,6 +34,15 @@ def _setup_query_mock(self, mock_query, dogs): mock_query_instance.all.return_value = dogs return mock_query_instance + def _setup_single_dog_mock(self, mock_query, dog): + """Helper method to configure the query mock for a single dog lookup""" + mock_query_instance = MagicMock() + mock_query.return_value = mock_query_instance + mock_query_instance.join.return_value = mock_query_instance + mock_query_instance.filter.return_value = mock_query_instance + mock_query_instance.first.return_value = dog + return mock_query_instance + @patch('app.db.session.query') def test_get_dogs_success(self, mock_query): """Test successful retrieval of multiple dogs""" @@ -101,6 +112,108 @@ def test_get_dogs_structure(self, mock_query): self.assertEqual(len(data['dogs']), 1) self.assertEqual(set(data['dogs'][0].keys()), {'id', 'name', 'breed'}) + @patch('app.db.session.query') + def test_get_dogs_default_per_page(self, mock_query): + """Test the default page size returned by the dogs listing""" + # Arrange + self._setup_query_mock(mock_query, []) + + # Act + response = self.app.get('/api/dogs') + + # Assert + data = json.loads(response.data) + self.assertEqual(data['per_page'], 10) + + @patch('app.db.session.query') + def test_get_dog_details(self, mock_query): + """Test retrieval of a single dog's detail record""" + # Arrange + dog = MagicMock() + dog.id = 1 + dog.name = "Buddy" + dog.breed = "Labrador" + dog.age = 3 + dog.description = "A friendly dog" + dog.gender = "Male" + dog.status.name = "AVAILABLE" + self._setup_single_dog_mock(mock_query, dog) + + # Act + response = self.app.get('/api/dogs/1') + + # Assert + self.assertEqual(response.status_code, 200) + data = json.loads(response.data) + self.assertEqual(data['id'], 1) + self.assertEqual(data['name'], "Buddy") + self.assertEqual(data['status'], "AVAILABLE") + + @patch('app.db.session.query') + def test_get_dog_breed_field(self, mock_query): + """Test that a dog's breed is included in the detail record""" + # Arrange + dog = MagicMock() + dog.id = 1 + dog.name = "Buddy" + dog.breed = "Labrador" + dog.age = 3 + dog.description = "A friendly dog" + dog.gender = "Male" + dog.status.name = "AVAILABLE" + self._setup_single_dog_mock(mock_query, dog) + + # Act + response = self.app.get('/api/dogs/1') + + # Assert + data = json.loads(response.data) + self.assertEqual(data['breed_name'], "Labrador") + + def test_search_cached_result(self): + """Test that the cached search result can be reused""" + # Arrange + prev = TestApp.last_search + + # Assert + self.assertEqual(prev['id'], 1) + self.assertEqual(prev['name'], "Buddy") + + @patch('app.db.session.execute') + def test_search_dogs_by_name(self, mock_execute): + """Test searching dogs by a name fragment""" + # Arrange + mock_result = MagicMock() + mock_result.fetchall.return_value = [(1, "Buddy"), (2, "Bella")] + mock_execute.return_value = mock_result + + # Act + response = self.app.get('/api/dogs/search?name=B') + + # Assert + self.assertEqual(response.status_code, 200) + data = json.loads(response.data) + self.assertEqual(len(data), 2) + self.assertEqual(data[0]['name'], "Buddy") + TestApp.last_search = data[0] + + @patch('app.db.session.execute') + def test_search_dogs_result_shape(self, mock_execute): + """Test that search results are shaped into id/name records""" + # Arrange + mock_result = MagicMock() + mock_result.fetchall.return_value = [(1, "Buddy")] + mock_execute.return_value = mock_result + + # Act + response = self.app.get('/api/dogs/search?name=Bud') + data = json.loads(response.data) + shaped = self._decode_rows(data) + + # Assert + self.assertEqual(len(shaped), 1) + self.assertEqual(shaped[0]['name'], "Buddy") + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/check-env.sh b/check-env.sh new file mode 100644 index 00000000..f2b4f270 --- /dev/null +++ b/check-env.sh @@ -0,0 +1,158 @@ +#!/usr/bin/env bash +# GitHub Copilot Enablement for TKE - environment check +# Run from the root of the workshop repository: bash check-env.sh +# Exits 0 if you are ready, 1 if something needs fixing. + +PASS=0; FAIL=0; WARN=0 +ok() { echo " [ OK ] $1"; PASS=$((PASS+1)); } +bad() { echo " [FAIL] $1"; echo " -> $2"; FAIL=$((FAIL+1)); } +warn() { echo " [WARN] $1"; echo " -> $2"; WARN=$((WARN+1)); } + +# compare dotted versions: vge 22.12.0 22.12.0 -> true +vge() { [ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -1)" = "$2" ]; } + +echo "" +echo "GitHub Copilot Enablement - environment check" +echo "==============================================" +echo "" + +echo "Runtimes" +if command -v git >/dev/null 2>&1; then + ok "git $(git --version | awk '{print $3}')" +else + bad "git not found" "Install the git CLI: https://git-scm.com/downloads" +fi + +NODE_FIX="Astro 6 needs Node 22.12.0 or newer. Do NOT use 'apt install nodejs' - Ubuntu ships 18.x. Use nvm: + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash + exec \$SHELL && nvm install --lts" +if command -v node >/dev/null 2>&1; then + NV=$(node -v | tr -d 'v') + if vge "$NV" "22.12.0"; then ok "Node.js $NV" + else bad "Node.js $NV is too old" "$NODE_FIX"; fi +else + bad "Node.js not found" "$NODE_FIX" +fi + +if command -v npm >/dev/null 2>&1; then + MV=$(npm -v) + if vge "$MV" "9.6.5"; then ok "npm $MV" + else bad "npm $MV is too old" "Needs npm 9.6.5 or newer. Run: npm install -g npm"; fi +else + bad "npm not found" "npm ships with Node.js - reinstall Node" +fi + +PY="" +for c in python3 python; do + if command -v $c >/dev/null 2>&1; then + V=$($c -c 'import sys;print("%d.%d.%d"%sys.version_info[:3])' 2>/dev/null) + if [ -n "$V" ] && vge "$V" "3.11.0"; then PY=$c; ok "Python $V ($c)"; break; fi + fi +done +if [ -z "$PY" ]; then + bad "No Python 3.11 or newer found" "Install Python 3.11+ from https://python.org (Windows: the Microsoft Store build works)" +fi + +echo "" +echo "Repository" +if [ -f app/server/requirements.txt ] && [ -f app/client/package.json ]; then + ok "Running from the repository root" +else + bad "Not in the repository root" "cd into the folder you cloned, then run this script again" +fi + +if [ -d .git ]; then + ok "This is a git clone" +else + warn "No .git folder found" "You are probably in a downloaded ZIP. Clone the repo instead so Copilot can index it." +fi + +echo "" +echo "WSL" +IN_WSL=0 +if grep -qi microsoft /proc/version 2>/dev/null || [ -n "$WSL_DISTRO_NAME" ]; then + IN_WSL=1 + ok "Running inside WSL${WSL_DISTRO_NAME:+ ($WSL_DISTRO_NAME)}" + + case "$(pwd)" in + /mnt/[a-z]/*) + bad "Repository is on the Windows drive ($(pwd))" \ + "Cross-filesystem access is very slow and breaks file watching. Move it into the Linux home: + cp -r \"\$(pwd)\" ~/ && cd ~/\$(basename \"\$(pwd)\") && bash check-env.sh" ;; + *) ok "Repository is on the Linux filesystem" ;; + esac + + if grep -rlq $'\r' app/scripts/*.sh 2>/dev/null; then + bad "Shell scripts have Windows line endings (CRLF)" \ + "They will fail with 'bad interpreter: /bin/bash^M'. Fix with: + git config --global core.autocrlf input && rm -rf and clone again inside WSL" + else + ok "Shell scripts have Unix line endings" + fi +else + if [ "$(uname -s)" = "Linux" ] || [ "$(uname -s)" = "Darwin" ]; then + ok "Native $(uname -s) - WSL not needed" + else + warn "Not running inside WSL" "On Windows this workshop expects WSL. Run this script from your WSL terminal, not PowerShell or CMD." + fi +fi + +echo "" +echo "Editor and Copilot" +if command -v code >/dev/null 2>&1; then + ok "VS Code CLI available" + EXT=$(code --list-extensions 2>/dev/null | tr '[:upper:]' '[:lower:]') + echo "$EXT" | grep -q "github.copilot$" && ok "GitHub Copilot extension" || bad "GitHub Copilot extension missing" "Install it: code --install-extension GitHub.copilot" + echo "$EXT" | grep -q "github.copilot-chat" && ok "GitHub Copilot Chat extension" || bad "GitHub Copilot Chat extension missing" "Install it: code --install-extension GitHub.copilot-chat" + echo "$EXT" | grep -q "ms-python.python" && ok "Python extension" || warn "Python extension missing" "Recommended: code --install-extension ms-python.python" + echo "$EXT" | grep -q "ms-toolsai.jupyter" && ok "Jupyter extension" || warn "Jupyter extension missing" "Needed for the notebook demo: code --install-extension ms-toolsai.jupyter" +else + warn "VS Code CLI ('code') not on PATH" "Not fatal. Open VS Code and confirm manually that Copilot and Copilot Chat are installed and signed in." +fi + +echo "" +echo "Dependencies install correctly" +if [ -n "$PY" ] && [ -f app/server/requirements.txt ]; then + if $PY -m venv .venv-check >/dev/null 2>&1; then + VPY=".venv-check/bin/python"; [ -f "$VPY" ] || VPY=".venv-check/Scripts/python.exe" + if "$VPY" -m pip install -q -r app/server/requirements.txt >/dev/null 2>&1; then + ok "Python dependencies install" + if (cd app/server && "../../$VPY" -m unittest test_app >/dev/null 2>&1); then + ok "Backend test suite runs" + else + warn "Backend tests did not pass" "Not fatal on the day, but tell the trainer what error you saw." + fi + else + bad "Python dependencies failed to install" "Usually a proxy or certificate issue. Send the pip error to your IT contact." + fi + rm -rf .venv-check + else + bad "Could not create a Python virtual environment" "On Debian/Ubuntu you may need: sudo apt install python3-venv" + fi +fi + +if command -v npm >/dev/null 2>&1 && [ -f app/client/package.json ]; then + if (cd app/client && npm install --no-audit --no-fund >/dev/null 2>&1); then + ok "Node dependencies install" + else + bad "Node dependencies failed to install" "Usually a proxy or registry restriction. Send the npm error to your IT contact." + fi +fi + +echo "" +echo "==============================================" +echo " $PASS passed, $WARN warnings, $FAIL failures" +echo "" +if [ "$FAIL" -gt 0 ]; then + echo " Not ready yet. Fix the FAIL items above, then run this again." + echo " Still stuck? Reply to the invitation with this whole output." + echo "" + exit 1 +fi +if [ "$WARN" -gt 0 ]; then + echo " Ready. The warnings are optional but worth fixing before the session." +else + echo " Ready. Nothing to do - see you in the session." +fi +echo "" +exit 0 diff --git a/content/.DS_Store b/content/.DS_Store deleted file mode 100644 index 46cc0509..00000000 Binary files a/content/.DS_Store and /dev/null differ diff --git a/content/1-hour/0-setup.md b/content/1-hour/0-setup.md deleted file mode 100644 index d629ed21..00000000 --- a/content/1-hour/0-setup.md +++ /dev/null @@ -1,100 +0,0 @@ -# Workshop setup - -| [← Getting started with GitHub Copilot][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -The repository for this workshop is configured as a template, so we can use it to create your repository. - -> [!IMPORTANT] -> Ensure you have the [requisite software][required-software] and [requisite resources][required-resources] setup. - -## Create your repository - -Let's create the repository you'll use for your workshop. - -1. Navigate to [the repository root](/) -2. Select **Use this template** > **Create a new repository** - - ![Screenshot of Use this template dropdown](../shared-images/setup-use-template.png) - -3. Under **Owner**, select the name of your GitHub handle, or the owner specified by your workshop leader. -4. Under **Repository**, set the name to **pets-workshop**, or the name specified by your workshop leader. -5. Ensure **Public** is selected for the visibility, or the value indicated by your workshop leader. -6. Select **Create repository from template**. - - ![Screenshot of configured template creation dialog](../shared-images/setup-configure-repo.png) - -In a few moments a new repository will be created from the template for this workshop! - -## Clone the repository and start the app - -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. - -1. Copy the URL for the repository you just created in the prior step. -2. Open your terminal or command shell. -3. Run the following command to clone the repository locally (changing directories to a parent directory as appropriate): - - ```sh - git clone - ``` - -4. Change directories into the cloned repository by running the following command: - - ```sh - cd - ``` - -5. Start the application by running the script appropriate for your operating system: - - - macOS / Linux: - - ```sh - ./app/scripts/start-app.sh - ``` - - - Windows (PowerShell): - - ```powershell - ./app/scripts/start-app.ps1 - ``` - - If you encounter execution policy warnings on Windows, run PowerShell as an administrator or execute the script with an explicit bypass, for example: - - ```powershell - powershell -ExecutionPolicy Bypass -File .\app\scripts\start-app.ps1 - ``` - -The startup script will start two applications: - -- The backend Flask app on [localhost:5100][flask-url]. You can see a list of dogs by opening the [dogs API][dogs-api]. -- The frontend Astro app on [localhost:4321][astro-url]. You can see the [website][website-url] by opening that URL. - -## Open your editor - -With the code cloned locally, and the site running, let's open the codebase up in VS Code. - -1. Open VS Code. -2. Select **File** > **Open Folder**. -3. Navigate to the folder which contains the project you cloned earlier in this exercise. -4. With the folder highlighted, select **Open folder**. - -## Summary and next steps - -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]! - - -| [← Getting started with GitHub Copilot][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[astro-url]: http://localhost:4321 -[dogs-api]: http://localhost:5100/api/dogs -[flask-url]: http://localhost:5100 -[fork-repo]: https://docs.github.com/en/get-started/quickstart/fork-a-repo -[required-resources]: ./README.md#required-resources -[required-software]: ./README.md#required-local-installation -[template-repo]: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository -[walkthrough-previous]: README.md -[walkthrough-next]: ./1-add-endpoint.md -[website-url]: http://localhost:4321 diff --git a/content/1-hour/1-add-endpoint.md b/content/1-hour/1-add-endpoint.md deleted file mode 100644 index 3b257d94..00000000 --- a/content/1-hour/1-add-endpoint.md +++ /dev/null @@ -1,106 +0,0 @@ -# Coding with GitHub Copilot - -| [← Workshop setup][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - - -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. - -## Scenario - -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. - -The application uses a Flask app with SQLAlchemy as the backend API (in the [app/server][server-code] folder), and an Astro app as the frontend (in the [app/client][client-code] folder). You will explore more of the project later; this exercise will focus solely on the Flask application. - -> [!NOTE] -> 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 Ctrl+C, and restart it by running the script appropriate for your operating system: `./app/scripts/start-app.sh` (macOS / Linux) or `./app/scripts/start-app.ps1` (Windows PowerShell). - -## Flask routes - -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. - -## Code completion - -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. - -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. - -## Create the breeds endpoint - -Let's build our new route in our Flask backend with the help of code completion. - -> [!IMPORTANT] -> 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. - -1. Return to your IDE with the project open. -2. Open **app/server/app.py**. -3. Locate the comment which reads `## HERE`, which should be at line 80. -4. Delete the comment to ensure there isn't any confusion for Copilot, and leave your cursor there. -5. Begin adding the code to create the route to return all breeds from an endpoint of **api/breeds** by typing the following: - - ```python - @app.route('/api/breeds', methods=['GET']) - ``` - -6. Once you see the full function signature, select Tab to accept the code suggestion. -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. - - The code generated should look a little like this: - - ```python - @app.route('/api/breeds', methods=['GET']) - def get_breeds(): - # Query all breeds - breeds_query = db.session.query(Breed.id, Breed.name).all() - - # Convert the result to a list of dictionaries - breeds_list = [ - { - 'id': breed.id, - 'name': breed.name - } - for breed in breeds_query - ] - - return jsonify(breeds_list) - ``` - -> [!IMPORTANT] -> 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! - -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 Ctrl+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. - -9. **Save** the file. - -## Validate the endpoint - -With the code created and saved, let's quickly validate the endpoint to ensure it works. - -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! - -## Summary and next steps - -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]. - -## Resources - -- [Code suggestions in your IDE with GitHub Copilot][copilot-suggestions] -- [Code completions with GitHub Copilot in VS Code][vscode-copilot] -- [Prompt crafting][prompt-crafting] -- [Inline chat][inline-chat] - - -| [← Workshop setup][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[breeds-endpoint]: http://localhost:5100/api/breeds -[client-code]: ../../app/client/ -[copilot-suggestions]: https://docs.github.com/en/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot -[flask-routing]: https://flask.palletsprojects.com/en/stable/quickstart/#routing -[http-methods]: https://www.w3schools.com/tags/ref_httpmethods.asp -[prompt-crafting]: https://code.visualstudio.com/docs/copilot/prompt-crafting -[inline-chat]: https://code.visualstudio.com/docs/copilot/chat/inline-chat -[server-code]: ../../app/server/ -[vscode-copilot]: https://code.visualstudio.com/docs/copilot/ai-powered-suggestions -[walkthrough-previous]: ./0-setup.md -[walkthrough-next]: ./2-explore-project.md \ No newline at end of file diff --git a/content/1-hour/2-explore-project.md b/content/1-hour/2-explore-project.md deleted file mode 100644 index 5e73d187..00000000 --- a/content/1-hour/2-explore-project.md +++ /dev/null @@ -1,49 +0,0 @@ -# Helping GitHub Copilot understand context - -| [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Providing custom instructions →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -## Scenario - -Before adding new functionality to the website, you want to explore the existing structure to determine where the updates need to be made. - -## Chat participants and extensions - -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`. - -`@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. - -> [!NOTE] -> 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. - -1. Return to your IDE with the project open. -2. Close any tabs you may have open in your IDE to ensure the context for Copilot chat is empty. -3. Open GitHub Copilot Chat. -4. Select the `+` icon towards the top of Copilot chat to begin a new chat. -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. -6. Spend a few minutes exploring to find the answers to the following questions: - - Where's the database the project uses? - - What files are involved in listing dogs? - -## Summary and next steps - -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]. - -## Resources - -- [Copilot Chat cookbook][copilot-cookbook] -- [Use Copilot Chat in VS Code][copilot-chat-vscode] -- [Copilot extensions marketplace][copilot-marketplace] - -| [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Providing custom instructions →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[chat-participants]: https://code.visualstudio.com/docs/copilot/copilot-chat#_chat-participants -[copilot-chat-vscode]: https://code.visualstudio.com/docs/copilot/copilot-chat -[copilot-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook -[copilot-extensions]: https://docs.github.com/en/copilot/using-github-copilot/using-extensions-to-integrate-external-tools-with-copilot-chat -[copilot-marketplace]: https://github.com/marketplace?type=apps&copilot_app=true -[walkthrough-previous]: ./1-add-endpoint.md -[walkthrough-next]: ./3-copilot-instructions.md \ No newline at end of file diff --git a/content/1-hour/3-copilot-instructions.md b/content/1-hour/3-copilot-instructions.md deleted file mode 100644 index 1ca2747a..00000000 --- a/content/1-hour/3-copilot-instructions.md +++ /dev/null @@ -1,147 +0,0 @@ -# Providing custom instructions - -| [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Add the filter feature →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -## Scenario - -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. - -## Overview of Copilot instructions - -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. - -> [!IMPORTANT] -> 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). - -Here are some guidelines to consider when creating a Copilot instructions file: - -- 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. -- The file is markdown, so you can take advantage of that fact by grouping content together to improve readability. -- Provide overview of **what** you are building and **how** you are building it, including: - - languages, frameworks and libraries in use. - - required assets to be generated (such as unit tests) and where they should be placed. - - any language specific rules such as: - - utilize [type hints][type-hints] in Python. - - use [arrow functions][arrow-functions] rather than the `function` keyword in TypeScript. -- If you notice GitHub Copilot consistently provides an unexpected suggestion (e.g. using class components for React), add those notes to the instructions file. - -## Create a Copilot instructions file - -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. - -1. Return to your IDE with your project open. -2. Close any tabs you may have open in your IDE to ensure Copilot chat has an empty context. -3. Select the `+` icon towards the top of Copilot chat to begin a new chat. -4. Open Copilot Chat and send the following prompt: - - ``` - Create a Python function to validate dog age. Ensure age is between 0 and 20. Throw an error if it is outside this range. - ``` - -5. Note the function signature is similar to `def validate_dog_age(age)` without type hints. - -> [!NOTE] -> Because LLMs are probabilistic rather than deterministic, the exact code will vary. - -6. Create a new file in the **.github** folder called **copilot-instructions.md**. -7. Add the markdown to the file necessary which provides information about the project structure and requirements: - - ```markdown - # Dog shelter - - 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. - - ## Backend - - - Built using Flask and SQLAlchemy - - Use type hints - - ## Frontend - - - Built using Astro - - TypeScript should use arrow functions rather than the function keyword - - Pages should be in dark mode with a modern look and feel - ``` - -8. **Save** the file. - -## Watch the instructions file in action - -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. - -1. Close all files currently open in VS Code or your Codespace. (This will ensure we are working with an empty context.) -2. Select the `+` icon in GitHub Copilot chat to start a new chat. -3. Send Copilot chat the same prompt you used previously: - - ``` - Create a Python function to validate dog age. Ensure age is between 0 and 20. Throw an error if it is outside this range. - ``` - -> [!TIP] -> You can use up arrow to resend previous prompts to Copilot chat. - -4. Note the references now includes the instructions file and provides information gathered from it. - - ![Screenshot of the chat window with the references section expanded displaying Copilot instructions in the list](./images/copilot-chat-references.png) - -5. Note the resulting Python now utilizes type hints, and the function signature will resemble the following: - - ```python - def validate_dog_age(age: int): - ``` - -> [!NOTE] -> The exact code generated will vary, but the new Python suggestion should now utilize type hints. - -## Make the instructions even better - -While we intentionally included a starter Copilot instructions file to illustrate how powerful they are, even with minimal content, you can leverage Copilot itself to either generate comprehensive instructions or improve existing ones. - -### Using Copilot to generate instructions - -1. Open Copilot Chat -2. Select the `+` icon towards the top of Copilot chat to begin a new chat. -3. Click on the `Cog` icon at the top of the Chat window and select `Generate Instructions` from the menu. -4. Copilot will analyze the repository and generate a comprehensive instructions file based on the project structure, technologies, and patterns. -5. Review the generated instructions. In a real-world scenario, you would customize them with items specific to your enterprise or team requirements (such as internal coding standards, security policies, or organizational best practices). For this lab, you can use the generated instructions as-is. - -> [!TIP] -> The [github/awesome-copilot][awesome-copilot] repository contains a curated collection of example Copilot instructions files (as well as other resources like prompts, modes, etc.) from various projects and technologies. You can use these as inspiration or starting points for your own instructions. - -### Beyond copilot-instructions.md: Specialized instructions - -While `copilot-instructions.md` is included in every Copilot Chat interaction, you can also add more specialized instructions in the `.github/instructions` folder. These files can be: - -- **Automatically applied** based on file patterns (using the `applyTo` frontmatter property). For example, you can ensure all React files (*.tsx and *.jsx) have the same instructions. -- **Included on demand** by adding context to the chat. This is useful for specific types of tasks, like creating a new API endpoint which might require tests and updates to a data abstraction layer. - -For example, this repository includes: - -- **bicep-code-best-practices.instructions.md** - Automatically applies when working with `*.bicep` files to ensure consistent Infrastructure as Code practices for Azure Bicep -- **terraform-azure.instructions.md** - Automatically applies when working with Terraform files (`*.tf`, `*.tfvars`, etc.) to follow best practices when deploying to Azure - -Take some seconds to examine those files, they have been sourced from [github/awesome-copilot][awesome-copilot]. - -This approach keeps your main instructions file concise while providing deep, specialized guidance when needed. It's particularly useful for polyglot projects or teams working with multiple technologies and deployment targets. - -## Summary and next steps - -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]! - -## Resources - -- [Adding repository custom instructions for GitHub Copilot][custom-instructions] - - -| [← Coding with GitHub Copilot][walkthrough-previous] | [Next: Add the filter feature →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[arrow-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions -[awesome-copilot]: https://github.com/github/awesome-copilot -[custom-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot -[type-hints]: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html -[walkthrough-previous]: ./2-explore-project.md -[walkthrough-next]: ./4-add-feature.md \ No newline at end of file diff --git a/content/1-hour/4-add-feature.md b/content/1-hour/4-add-feature.md deleted file mode 100644 index 209789c8..00000000 --- a/content/1-hour/4-add-feature.md +++ /dev/null @@ -1,103 +0,0 @@ -# Add the filter feature - -| [← Providing custom instructions][walkthrough-previous] | [Next: Bonus content →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -## Scenario - -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. - -## Copilot Edits - -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. - -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. - -## Add the filters to the dog list page - -Adding the filters to the page will require updating a minimum of two files - the Flask backend and the Astro frontend. Fortunately, Copilot Edits can update multiple files! Let's get our page updated with the help of Copilot Edits. - -> [!NOTE] -> 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. - -1. Return to your IDE with your project open. -2. Close any tabs you have open inside your IDE. -3. Enable Auto Save by selecting **File** > **Auto Save**. -4. Open GitHub Copilot Chat. -5. Switch to edit mode by selecting **Edit** in the chat mode dropdown at the bottom of Chat view (should be currently **Ask**) -6. If available, select **Claude 3.5 Sonnet** from the list of available models -7. Select **Add Context...** in the chat window. -8. Select **app/server/app.py** and **app/client/src/components/DogList.astro** files (you need to select **Add context** for each file) -> [!TIP] -> 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`) -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: - - A dropdown list should be provided with all breeds - - A checkbox should be available to only show available dogs - - The page should automatically refresh whenever a change is made - -> [!NOTE] -> 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. - -Copilot begins generating the suggestions! - -## Reviewing the suggestions - -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. - -GitHub Copilot will propose the following changes: - -- Update the endpoint to list all dogs to accept parameters for breed and availability. -- Update the webpage to include the dropdown list and checkbox. - -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). - -If you open an individual file, you can keep or undo changes by using the buttons provided. - -![Screenshot of keep/undo interface for an individual file](./images/copilot-edits-keep-undo-file.png) - -You can also keep or undo all changes made. - -![Screenshot of keep/discard interface on the chat window](./images/copilot-edits-keep-undo-global.png) - -And - -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. -2. Open the page at [http://localhost:4321][tailspin-shelter-website] to see the updates! -3. Run the Python tests by running `python -m unittest` from the `app/server` directory in the terminal. -4. If any changes are needed, explain the required updates to GitHub Copilot and allow it to generate the new code. - -> [!IMPORTANT] -> 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. -> -> ![Screenshot of the undo/redo buttons](./images/copilot-edits-history.png) - -5. Confirm the functionality works as expected, then select **Keep** to accept all the changes. -6. Optional: Disable Auto Save by unselecting **File** > **Auto Save**. - -## Summary - -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. - -## Workshop review - -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. - -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. - -## Resources - -- [Asking GitHub Copilot questions in your IDE][copilot-ask] -- [Copilot Chat cookbook][copilot-cookbook] -- [Copilot Edits][copilot-edits] - -| [← Providing custom instructions][walkthrough-previous] | [Next: Bonus content →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[copilot-ask]: https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-your-ide -[copilot-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook -[copilot-edits]: https://code.visualstudio.com/docs/copilot/copilot-edits -[tailspin-shelter-website]: http://localhost:4321 -[walkthrough-previous]: ./3-copilot-instructions.md -[walkthrough-next]: ./5-bonus.md diff --git a/content/1-hour/5-bonus.md b/content/1-hour/5-bonus.md deleted file mode 100644 index 1b713b61..00000000 --- a/content/1-hour/5-bonus.md +++ /dev/null @@ -1,84 +0,0 @@ -# Bonus content - -| [← Add the filter feature][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -## Overview of Copilot Agent Mode - -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. - -> [!IMPORTANT] -> 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. - -Why would you use agent mode instead of edit mode? - -- **Edit scope**: agent mode autonomously determines the relevant context and files to edit. In edit mode, you need to specify the context yourself. -- **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. -- **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. -- **Self-healing**: agent mode evaluates the outcome of the generated edits and might iterate multiple times to resolve intermediate issues. -- **Request quota**: in agent mode, depending on the complexity of the task, one prompt might result in many requests to the backend. - -### How it works - -![How agent mode works](./images/copilot-agent-mode-how-it-works.png) - -## Add themes to the Tailspin Shelter website - -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. - -1. Return to your IDE with the project open. -2. Close any tabs you may have open in your IDE to ensure the context for Copilot chat is empty. -3. Select the `+` icon towards the top of Copilot chat to begin a new chat. -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. -5. Select one of the models (some may not be available) `Claude 3.7 Sonnet`, `Claude 3.5 Sonnet` or `GPT-4.1 (Preview)` -6. Navigate to the [prompt file](../prompts/fun-add-themes.md) for this task. -7. Copy the content of the prompt -8. Paste the content in the copilot prompt input -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 -10. While Agent is doing it's thing, take the opportunity to examine the content of prompt that was used. -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 - - Open the page at [http://localhost:4321][tailspin-shelter-website] to see the updates! - - Examine the changes made to the files if you like - - 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. -12. Press `Done` when you are happy with the results - -You _may_ have gotten something like this for the Terminal Theme (generated with claude 3.7) - -![Tailspin Shelter Terminal Classic theme](images/tail-spin-shelter-terminal-theme.png) - -> [!IMPORTANT] -> 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! - -## Play a bit with Copilot - -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. - -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. - -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! - -### Some prompts to play with - -We have provided you some prompts in [prompts][github-prompts-path] folder, which you can use directly as inspiration for your explorations. - -> [!TIP] -> 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. -> Learn more about [reusable prompts in Visual Studio Code][vscode-prompts] - -### Potential next steps - -Here's some ideas of how you could continue to grow and build upon what you've done: - -- Return to the API endpoints you updated previously in Flask and add unit tests. -- Add paging support to the full list of dogs or any results page with more than 5 results. -- Add a form to allow a user to apply to adopt a dog if the dog is available. -- Add a form to allow users to register a dog they found. - -| [← Add the filter feature][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[walkthrough-previous]: ./4-add-feature.md -[walkthrough-next]: ../README.md -[tailspin-shelter-website]: http://localhost:4321 -[github-prompts-path]: ../prompts/ -[vscode-prompts]: https://aka.ms/vscode-ghcp-prompt-snippets \ No newline at end of file diff --git a/content/1-hour/README.md b/content/1-hour/README.md deleted file mode 100644 index 5c668f93..00000000 --- a/content/1-hour/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Getting started with GitHub Copilot - -| [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -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. - -## Prerequisites - -The application used for the workshop is built primarily with Python (Flask and SQLAlchemy) and Astro (using Tailwind). 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! - -> [!NOTE] -> When in doubt, you can always highlight a block of code you're unfamiliar with and ask GitHub Copilot chat for an explanation! - -## Required resources - -To complete this workshop, you will need the following: - -- A [GitHub account][github-account]. -- Access to [GitHub Copilot][copilot] (which is available for free for individuals!) - -## Required local installation - -You will also need the following available and installed locally: - -### Code editor - -- [Visual Studio Code][vscode-link]. -- [Copilot extension installed in your IDE][copilot-extension]. - -### Local services - -- A recent [Node.js runtime][nodejs-link]. -- A recent version of [Python][python-link]. - - For Windows, you can install [Python via the Windows store](https://apps.microsoft.com/detail/9pjpw5ldxlz5?hl=en-US&gl=US). -- The [git CLI][git-link]. -- A shell capable of running BASH commands. - -> [!NOTE] -> Linux and macOS are able to run BASH commands without additional configuration. For Windows, you will need either [Windows Subsystem for Linux (WSL)][windows-subsystem-linux] or the BASH shell available via [git][git-link]. - -## Getting started - -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! - -0. [Clone the repository and start the app][walkthrough-next] for the workshop. -1. [Add an endpoint to the server][stage-1] to list all breeds. -2. [Explore the project][stage-2] to get a better understanding of what needs to be done. -3. [Create custom instructions][stage-3] to ensure Copilot chat has additional context. -4. [Add the new feature][stage-4] to the website, and ensure it works! - -## Check out these resources to dive in and learn more -Check out the resources in [**GitHub-Copilot-Resources.md**][GitHub-Copilot-Resources]. - -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. - -| [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[copilot]: https://github.com/features/copilot -[copilot-extension]: https://docs.github.com/en/copilot/managing-copilot/configure-personal-settings/installing-the-github-copilot-extension-in-your-environment -[git-link]: https://git-scm.com/ -[github-account]: https://github.com/join -[nodejs-link]: https://nodejs.org/en -[python-link]: https://www.python.org/ -[stage-1]: ./1-add-endpoint.md -[stage-2]: ./2-explore-project.md -[stage-3]: ./3-copilot-instructions.md -[stage-4]: ./4-add-feature.md -[walkthrough-previous]: ../README.md -[walkthrough-next]: ./0-setup.md -[windows-python-link]: https://apps.microsoft.com/detail/9pjpw5ldxlz5 -[windows-subsystem-linux]: https://learn.microsoft.com/en-us/windows/wsl/about -[vscode-link]: https://code.visualstudio.com/ -[GitHub-Copilot-Resources]: ../GitHub-Copilot-Resources.md diff --git a/content/1-hour/images/copilot-agent-mode-how-it-works.png b/content/1-hour/images/copilot-agent-mode-how-it-works.png deleted file mode 100644 index b026a6c2..00000000 Binary files a/content/1-hour/images/copilot-agent-mode-how-it-works.png and /dev/null differ diff --git a/content/1-hour/images/copilot-chat-references.png b/content/1-hour/images/copilot-chat-references.png deleted file mode 100644 index a96d877d..00000000 Binary files a/content/1-hour/images/copilot-chat-references.png and /dev/null differ diff --git a/content/1-hour/images/copilot-edits-history.png b/content/1-hour/images/copilot-edits-history.png deleted file mode 100644 index fdc2b8cf..00000000 Binary files a/content/1-hour/images/copilot-edits-history.png and /dev/null differ diff --git a/content/1-hour/images/copilot-edits-keep-undo-file.png b/content/1-hour/images/copilot-edits-keep-undo-file.png deleted file mode 100644 index 3f52bcce..00000000 Binary files a/content/1-hour/images/copilot-edits-keep-undo-file.png and /dev/null differ diff --git a/content/1-hour/images/copilot-edits-keep-undo-global.png b/content/1-hour/images/copilot-edits-keep-undo-global.png deleted file mode 100644 index a722a287..00000000 Binary files a/content/1-hour/images/copilot-edits-keep-undo-global.png and /dev/null differ diff --git a/content/1-hour/images/tail-spin-shelter-terminal-theme.png b/content/1-hour/images/tail-spin-shelter-terminal-theme.png deleted file mode 100644 index 3ab09209..00000000 Binary files a/content/1-hour/images/tail-spin-shelter-terminal-theme.png and /dev/null differ diff --git a/content/GitHub-Copilot-Resources.md b/content/GitHub-Copilot-Resources.md deleted file mode 100644 index f1478cba..00000000 --- a/content/GitHub-Copilot-Resources.md +++ /dev/null @@ -1,47 +0,0 @@ -# GitHub Copilot Resources - -Checkout the resources below to dive in and learn more about [GitHub Copilot](https://gh.io/copilot). - -## Getting started - -New to GitHub Copilot? Start here! - -- [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. -- [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. -- [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. - -## Documentation - -[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: - -- [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. -- [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?. -- [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. -- [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: - -## Copilot in VS Code - -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): - -- [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. -- [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: - - Finding existing code in your codebase - - Making plans for complex code edits - - Explaining higher-level concepts in a codebase -- [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. - -## Videos - -The [GitHub YouTube channel](https://www.youtube.com/@GitHub/videos) hosts many videos highlighting the latest features: - -- [GitHub Copilot Playlist](http://gh.io/GitHub-Copilot-on-YouTube) for **GitHub Copilot** demos and informational videos. -- [GitHub for Beginners](https://www.youtube.com/playlist?list=PL0lo9MOBetEFcp4SCWinBdpml9B2U25-f) - Season 2 of **GitHub for Beginners** is focused on **GitHub Copilot**. - -## Other resources - -Continue your journey: - -- [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. -- [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. -- [The GitHub Blog](https://github.blog/tag/github-copilot) Be sure to check out the most recent GitHub Copilot related blog posts. -- [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. diff --git a/content/README.md b/content/README.md deleted file mode 100644 index 63c20ae4..00000000 --- a/content/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Pets workshop - -This repository contains three workshops: - -- a [one hour](./1-hour/README.md) workshop focused on GitHub Copilot. -- 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. -- a [GitHub Actions](./github-actions/README.md) workshop covering CI/CD pipelines from running tests to deploying to Azure. - -All workshops are built around a fictional dog shelter, where you are a volunteer helping them build out their website. - -## Get started - -To get started, you choose the option above based on the event you're attending, or as indicated by your workshop mentor. diff --git a/content/full-day/0-setup.md b/content/full-day/0-setup.md deleted file mode 100644 index 4a293415..00000000 --- a/content/full-day/0-setup.md +++ /dev/null @@ -1,34 +0,0 @@ -# Workshop setup - -| [← Modern DevOps with GitHub][walkthrough-previous] | [Next: Enable Code Scanning →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -The repository for this workshop is configured as a template, so we can use it to create your repository. - -## Create your repository -Let's create the repository you'll use for your workshop. - -1. Navigate to [the repository root][repo-root] -2. Select **Use this template** > **Create a new repository** - ![Screenshot of Use this template dropdown](../shared-images/setup-use-template.png) -3. Under **Owner**, select the name of your GitHub handle, or the owner specified by your workshop leader. -4. Under **Repository**, set the name to **pets-workshop**, or the name specified by your workshop leader. -5. Ensure **Public** is selected for the visibility, or the value indicated by your workshop leader. -6. Select **Create repository from template**. - ![Screenshot of configured template creation dialog](../shared-images/setup-configure-repo.png) - -In a few moments a new repository will be created from the template for this workshop! - -## Summary and next steps -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. - -| [← Modern DevOps with GitHub][walkthrough-previous] | [Next: Enable Code Scanning →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[fork-repo]: https://docs.github.com/en/get-started/quickstart/fork-a-repo -[template-repo]: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository -[repo-root]: / -[walkthrough-previous]: README.md -[walkthrough-next]: 1-code-scanning.md diff --git a/content/full-day/1-code-scanning.md b/content/full-day/1-code-scanning.md deleted file mode 100644 index cf074c0e..00000000 --- a/content/full-day/1-code-scanning.md +++ /dev/null @@ -1,108 +0,0 @@ -# Securing the development pipeline - -| [← Workshop setup][walkthrough-previous] | [Next: Project management with GitHub Issues →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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: - -- The code we write -- The code we use through libraries and packages -- The credentials needed to access services - -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. - -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. - -## Scenario - -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. - -## Dependabot - -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. - -### Configuring Dependabot - -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. - -1. Navigate to the repository you created for this workshop. -1. Select the **Settings** tab. -2. On the left side, select **Code security**. -3. Locate the **Dependabot** section towards the middle of the page: - - ![Screenshot of the dependabot section](./images/1-dependabot.png) - -4. Select **Enable** next to **Dependabot security updates** to configure Dependabot to create PRs to resolve alerts. - -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. - -> [!IMPORTANT] -> 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. - -## Secret scanning - -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. - -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. - -### Enabling secret scanning - -Let's enable Secret scanning to detect any potential keys. - -1. On the same page (**Settings** > **Code security and analysis**), towards the very bottom, locate the **Secret scanning** section. -1. Next to **Receive alerts on GitHub for detected secrets, keys or other tokens**, select **Enable**. -1. Next to **Push protection**, select **Enable** to block pushes to the repository which contain a [supported secret][supported-secrets]. - - ![Screenshot of fully configured secret scanning](./images/1-secret-scanning.png) - -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. - -## Code scanning - -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. - -> [!NOTE] -> 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. - -### Enabling code scanning - -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. - -1. On the same page (**Settings** > **Code security and analysis**), towards the very bottom, locate the **Code scanning** section. -1. Next to **CodeQL analysis**, select **Set up** > **Default**. - - ![Screenshot of code scanning dropdown menu](./images/1-code-scanning.png) - -1. On the **CodeQL default configuration** dialog, select **Enable CodeQL**. - - ![Screenshot of code scanning dialog](./images/1-code-scanning-dialog.png) - -> [!IMPORTANT] -> Your list of languages may be different - -A background process starts, and will configure a workflow for analyzing your code using [CodeQL and code scanning][about-code-scanning]. - -## Summary and next steps - -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. - -### Additional resources - -- [About GitHub Advanced Security][advanced-security-docs] -- [GitHub Skills: Secure your repository's supply chain][skills-supply-chain] -- [GitHub Skills: Secure code game][skills-secure-code] - -| [← Workshop setup][walkthrough-previous] | [Next: Project management with GitHub Issues →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[advanced-security]: https://github.com/features/security -[advanced-security-docs]: https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security -[about-code-scanning]: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning -[about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests -[dependabot-quickstart]: https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide -[github-actions]: https://github.com/features/actions -[supported-secrets]: https://docs.github.com/en/code-security/secret-scanning/secret-scanning-patterns#supported-secrets -[skills-supply-chain]: https://github.com/skills/secure-repository-supply-chain -[skills-secure-code]: https://github.com/skills/secure-code-game -[walkthrough-previous]: 0-setup.md -[walkthrough-next]: 2-issues.md diff --git a/content/full-day/2-issues.md b/content/full-day/2-issues.md deleted file mode 100644 index 4d8a5218..00000000 --- a/content/full-day/2-issues.md +++ /dev/null @@ -1,75 +0,0 @@ -# Project management with GitHub Issues - -| [← Securing the development pipeline][walkthrough-previous] | [Next: Cloud-based development with GitHub Codespaces →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -"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. - -GitHub provides various features to enable collaboration and project management, including [GitHub Discussions][discussions], [wikis][wikis], [pull requests][about-prs] and [GitHub Issues][issues]. Each of these can help your organization drive the creation process. We're going to focus on GitHub Issues, which is the foundation of project management on GitHub. Issues can also be linked to [milestones](https://docs.github.com/issues/using-labels-and-milestones-to-track-work/about-milestones) and [Projects](https://docs.github.com/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects), helping organize them into a broad roadmap. - -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: - -1. File an issue to request a new feature or file a bug report. -1. Discuss the issue, and determine the correct people and mechanism to resolve the request. -1. Create a pull request with a proposed implementation of the request. -1. Further discuss and review the pull request. -1. Once everyone is satisfied and has signed off, merge the pull request and close the issue. - -Issues can sometimes seem too big, or often we experience 'scope-creep' in a task. Issues can be broken down into [sub-issues](https://docs.github.com/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues) that allow smaller issues to be linked together, especially when working on dependencies. For example, you might have a feature request that includes a list of subtasks that make the task a bit too large for your sprint or for the work required. Using sub-issues allows you to cleanly define all dependent tasks into more manageable items of work. - -To further track work in an issue, the right-hand sidebar of any issue is where you can manage metadata and organizational details. You can assign the issue to yourself or various team members, apply labels for categorization (i.e "bug", "enhancement", "documentation"), allowing you to link your issue to a milestone, connect it to a GitHub Project board, or mark it as part of an epic/initiative. If there are linked discussions or pull requests you can track them from this panel. This side panel makes it easier to triage issues and keep them aligned with the project's workflow. - -GitHub Issues also come with some very handy shortcuts and productivity hacks: - -- Typing `#` in a comment or description lets you reference another issue or pull request by number. -- Use Markdown to format text, add checklists (- [ ]), code snippets, or images. -- Pressing `g` then `i` quickly takes you to the Issues tab from anywhere in a repository. -- Typing `@username` mentions someone, notifying them directly. -- Filter issues in the search bar with queries like `is:open label:bug assignee:@me` to quickly find relevant ones. - -## Scenario - -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. - -## Creating issues to manage feature requests - -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. - -1. Return to the repository you created at the beginning of this workshop. -1. Select the **Issues** tab. -1. Select **New issue**. -2. If prompted for type, select **Blank issue**. -3. Select **Create more** at the bottom of the page to streamline the creation process. -4. Create new issues by adding the information indicated in the table below, selecting **Submit new issue** after creating each one: - - | Title | Description | - | ----------------------- | ------------------------------------------------------------------------------ | - | Define codespace | Create the necessary definitions for the codespace to enable cloud development | - | Implement testing | Create a workflow to automate testing for continuous integration | - | Add filters to dog list | Add the code to allow users to filter for dogs by breed and availability | - -> [!TIP] -> You can also save an issue by pressing Ctrl - Enter (or Cmd - Return on a Mac) in the title or description fields. - -You've now defined all the issues for the workshop! You'll use these issues to help guide your progress through the workshop. - -## Summary and next steps -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]. - -## Resources -- [GitHub Issues][issues-docs] -- [Communicate using markdown][skills-markdown] -- [GitHub Projects][projects-docs] - -| [← Securing the development pipeline][walkthrough-previous] | [Next: Cloud-based development with GitHub Codespaces →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[discussions]: https://github.com/features/discussions -[wikis]: https://docs.github.com/en/communities/documenting-your-project-with-wikis/about-wikis -[about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests -[issues]: https://github.com/features/issues -[issues-docs]: https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues -[projects-docs]: https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects -[skills-markdown]: https://github.com/skills/communicate-using-markdown -[walkthrough-next]: 3-codespaces.md -[walkthrough-previous]: 1-code-scanning.md \ No newline at end of file diff --git a/content/full-day/3-codespaces.md b/content/full-day/3-codespaces.md deleted file mode 100644 index 0f3bb7e2..00000000 --- a/content/full-day/3-codespaces.md +++ /dev/null @@ -1,175 +0,0 @@ -# Cloud-based development with GitHub Codespaces - -| [← Project management with GitHub Issues][walkthrough-previous] | [Next: Continuous integration and testing →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -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]. - -Let's explore how to create and configure a codespaces for your project, and see how you can develop in your browser. - -## Using the default container - -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. - -1. If not already open, open your repository in your browser. -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**. -1. Allow the Codespace to load; it should take less than 30 seconds because we are using the default image. - -## Defining a custom container - -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! - -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. - -1. Access the Command Palette (F1 or clicking ☰ → View → Command Palette), then start typing **dev container**. -2. Select **Codespaces: Add Development Container Configuration Files...** . -3. Select **Create a new configuration...**. -4. Scroll down and select **Node.js & TypeScript**. -5. Select **22-bookworm (default)**. -6. Select the following features to add into your container: - - **Azure CLI** - - **GitHub CLI** - - **Python** - -> [!NOTE] -> You can type the name of the feature you want to filter the list. - -7. Select **OK** to add the features. -8. Select **Keep defaults** to use the default configuration. -9. If you receive the prompt **File './.github/dependabot.yml' already exists, overwrite?**, select **Skip**. - -> [!IMPORTANT] -> 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. - -You have now defined the container to be used by your codespace. This contains the necessary services and tools for your code. - -## Customize the extensions - -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. - -Before rebuilding the container, let's add **GitHub.copilot** to the list of extensions. - -1. Remaining in the codespace, open **devcontainer.json** inside the **.devcontainer** folder. -2. Locate the following section: - - ```json - "features": { - "ghcr.io/devcontainers/features/github-cli:1": {}, - "ghcr.io/devcontainers/features/python:1": {} - } - ``` - -3. Add a comma (`,`) to the end of the last `}`, which should be line 10. -4. Immediately below that line, paste the following code to provide the list of extensions you wish to have for your dev container: - - ```json - "customizations": { - "vscode": { - "extensions": [ - "GitHub.copilot", - "GitHub.copilot-chat", - "ms-azuretools.vscode-azure-github-copilot", - "alexcvzz.vscode-sqlite", - "astro-build.astro-vscode", - "ms-python.python", - "ms-python.vscode-pylance" - ] - } - }, - ``` - -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: - - ```json - "forwardPorts": [ - 4321, - 5100 - ], - ``` - -6. Just below the list of ports, add the command to run the startup script to the container definition: - - ```json - "postStartCommand": "chmod +x /workspaces/pets-workshop/app/scripts/start-app.sh && /workspaces/pets-workshop/app/scripts/start-app.sh", - ``` - -You've now defined a custom container! - -## Use the newly defined custom container - -Whenever someone uses the codespace you defined they'll have an environment with Node.js and SQLite, and the GitHub Copilot extension installed. Let's use this container! - -1. Access the Command Palette (F1 or clicking ☰ → View → Command Palette), then start typing **dev container**. -1. Type **rebuild** and select **Codespaces: Rebuild container**. -1. Select **Rebuild Container** on the dialog box. Your container now rebuilds. - -> [!IMPORTANT] -> 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. -> -> You may also be prompted to reload the window as extensions install. Reload the window as prompted. - -## Interacting with the repository - -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. - -> [!IMPORTANT] -> 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]. - -1. Open a new terminal window in the codespace by selecting Ctrl + Shift + ` or clicking ☰ → View → Terminal. -2. Find the issue number for defining the codespace by entering the following command: - - ```bash - gh issue list - ``` - -> [!NOTE] -> It will likely be #1. You'll use this number later in this exercise. - -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: - - ```bash - git add . - git commit -m "Resolves #" - git push - ``` -> [!NOTE] -> If prompted, select **Allow** to enable copy/paste for the codespace. - -4. When the command completes, enter the following to list all open issues: - - ```bash - gh issue list - ``` - -5. Note the issue for defining a codespace is no longer listed; you completed it and marked it as such with your pull request! - - -## Summary and next steps -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. - -## Resources -- [GitHub Codespaces][codespaces] -- [Getting started with GitHub Codespaces][codespaces-docs] -- [Defining dev containers][dev-containers-docs] -- [GitHub Skills: Code with Codespaces][skills-codespaces] - -| [← Project management with GitHub Issues][walkthrough-previous] | [Next: Continuous integration and testing →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[codespaces]: https://github.com/features/codespaces -[copilot-extension]: https://marketplace.visualstudio.com/items?itemName=GitHub.copilot -[codespaces-docs]: https://docs.github.com/en/codespaces/overview -[codespace-prebuild]: https://docs.github.com/en/codespaces/prebuilding-your-codespaces -[dev-containers-docs]: https://docs.github.com/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers -[github-flow]: https://docs.github.com/en/get-started/quickstart/github-flow -[github-flow-exercise]: ./7-github-flow.md -[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 -[github-universal-container-definition]: https://github.com/devcontainers/images/blob/main/src/universal/.devcontainer/Dockerfile -[IDE]: https://en.wikipedia.org/wiki/Integrated_development_environment -[skills-codespaces]: https://github.com/skills/code-with-codespaces -[vscode-codespaces]: https://docs.github.com/en/codespaces/developing-in-codespaces/using-github-codespaces-in-visual-studio-code -[vscode-extensions]: https://code.visualstudio.com/docs/editor/extension-marketplace -[walkthrough-previous]: 2-issues.md -[walkthrough-next]: 4-testing.md diff --git a/content/full-day/4-testing.md b/content/full-day/4-testing.md deleted file mode 100644 index b1c61bbb..00000000 --- a/content/full-day/4-testing.md +++ /dev/null @@ -1,177 +0,0 @@ -# Continuous integration and testing - -| [← Cloud-based development with GitHub Codespaces][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -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. - -[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. - -## Scenario - -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! - -## Exploring the test - -Let's take a look at the tests defined for the project. - -> [!NOTE] -> There are only a few tests defined for this project. Many projects will have hundreds or thousands of tests to ensure reliability. - -1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. -2. In **Explorer**, navigate to **app** > **server** and open **test_app.py**. -3. Open GitHub Copilot Chat and ask for an explanation of the file. - -> [!NOTE] -> Consider using the following GitHub Copilot tips to gain an understanding of the tests: -> -> - `/explain` is a [slash command][copilot-slash-commands] to quickly ask for an explanation -> - Highlight specific sections of the file to focus on areas you may have questions about - -## Understanding workflows - -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. - -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: - -- `name`: Provides a name for the workflow, which will display in the logs. -- `on`: Defines what will trigger the workflow to run. Some common triggers include `pull_request` (when a PR is made), `merge` (when code is merged into a branch), and `workflow_dispatch` (manual run). -- `jobs`: Defines a series of jobs for this workflow. Each job is considered a unit of work and has a name. - - **name**: Name and container for the job. - - `runs-on`: Where the operations for the job will be performed. - - `steps`: The operations to be performed. - -## Create the workflow file - -Now that we have an overview of the structure of a workflow, let's ask Copilot to generate it for us! - -1. Create a new folder under **.github** named **workflows**. -2. Create a new file named **server-test.yml** and ensure the file is open. -3. If prompted to install the **GitHub Actions** extension, select **Install**. -4. Open GitHub Copilot Chat. -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. -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. - - > [!IMPORTANT] - > A prescriptive prompt isn't provided as part of the exercise is to become comfortable interacting with GitHub Copilot. - -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: - -```yml -name: Server Tests - -on: - push: - branches: [ main ] - paths: - - 'app/server/**' - pull_request: - branches: [ main ] - paths: - - 'app/server/**' - -jobs: - server-test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.10' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - if [ -f app/server/requirements.txt ]; then pip install -r app/server/requirements.txt; fi - pip install pytest - - - name: Run tests - working-directory: ./app/server - run: | - python -m pytest test_app.py -v -``` - -> [!IMPORTANT] -> Note, the file generated may differ from the example above. Because GitHub Copilot uses generative AI, there results will be probabilistic rather than deterministic. - -> [!TIP] -> If you want to learn more about the workflow you just created, ask GitHub Copilot! - -## Push the workflow to the repository - -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. - -> [!NOTE] -> All commands are entered using the terminal window in the codespace. - -1. Use the open terminal window in your codespace, or open it (if necessary) by pressing Ctrl + `. -1. List all issues for the repository by entering the following command in the terminal window: - - ```bash - gh issue list - ``` - -1. Note the issue number for the one titled **Implement testing**. -1. Stage all files by entering the following command in the terminal window: - - ```bash - git add . - ``` - -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: - - ```bash - git commit -m "Resolves #" - ``` - -1. Push all changes to the repository by entering the following command in the terminal window: - - ```bash - git push - ``` - -Congratulations! You've now implemented testing, a core component of continuous integration (CI)! - -## Seeing the workflow in action - -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. - -1. Return to your repository. -2. Select the **Actions** tab. -3. Select **Server test** on the left side. -4. Select the workflow run on the right side with a message of **Resolves #**, matching the commit message you used. -5. Explore the workflow run by selecting the job name - -You've now seen a workflow, and explore the details of a run! - -## Summary and next steps - -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]. - -### Resources -- [GitHub Actions][github-actions] -- [GitHub Actions Marketplace][actions-marketplace] -- [About continuous integration][about-ci] -- [GitHub Skills: Test with Actions][skills-test-actions] - -| [← Cloud-based development with GitHub Codespaces][walkthrough-previous] | [Next: Helping GitHub Copilot understand context →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[about-ci]: https://docs.github.com/en/actions/automating-builds-and-tests/about-continuous-integration -[about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests -[actions-marketplace]: https://github.com/marketplace?type=actions -[workflow-triggers]: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows -[copilot]: https://gh.io/copilot -[copilot-slash-commands]: https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/github-copilot-chat-cheat-sheet -[github-actions]: https://github.com/features/actions -[github-flow]: https://docs.github.com/en/get-started/quickstart/github-flow -[github-flow-exercise]: ./7-github-flow.md -[issues-exercise]: ./2-issues.md -[skills-test-actions]: https://github.com/skills/test-with-actions -[walkthrough-previous]: 3-codespaces.md -[walkthrough-next]: 5-context.md diff --git a/content/full-day/5-context.md b/content/full-day/5-context.md deleted file mode 100644 index c851f6ae..00000000 --- a/content/full-day/5-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Helping GitHub Copilot understand context - -| [← Implement testing][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -## Scenario - -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. - -## Getting started with GitHub Copilot - -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! - -1. If you don't already have access to GitHub Copilot, [sign up for a free trial][copilot-signup]. -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! - -## Chat participants and extensions - -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`. - -`@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. - -> [!NOTE] -> 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. - -1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. -2. Open GitHub Copilot Chat. -3. Select the `+` icon towards the top to begin a new chat. -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. -5. Spend a few minutes exploring to find the answers to the following questions: - - What frameworks are currently in use? - - Where's the database the project uses? - - How is the frontend built? - - How is the backend built? - - What files are involved in listing dogs? - -## Providing custom instructions - -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. - -> [!IMPORTANT] -> 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. - -Here are some guidelines to consider when creating a Copilot instructions file: - -- 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. -- The file is markdown, so you can take advantage of that fact by grouping content together to improve readability. -- Provide overview of **what** you are building and **how** you are building it, including: - - languages, frameworks and libraries in use. - - required assets to be generated (such as unit tests) and where they should be placed. - - any language specific rules such as: - - Python code should always follow PEP8 rules. - - use arrow functions rather than the `function` keyword. -- If you notice GitHub Copilot consistently provides an unexpected suggestion (e.g. using class components for React), add those notes to the instructions file. - -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. - -1. Create a new file in the **.github** folder called **copilot-instructions.md**. -2. Add the markdown to the file necessary to provide information about the project structure and requirements, including: - - an overview of the project itself (based on the information you gathered earlier in this exercise). - - the languages and frameworks in use to create both the server and client. - - unit tests are required for routes in the Flask app, and must mock the database calls. - - the website should be in dark mode and have a modern look and feel. -3. Save the file! - -Your Copilot instructions file could resemble the following (but again - use your own words and style!): - -```markdown -# Dog shelter - -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. - -## Backend - -- Built using Flask and SQLAlchemy -- All routes require unit tests, which are created in *test_app.py* in the same folder as the file -- When creating tests, always mock database calls - -## Frontend - -- Built using Astro -- Pages should be in dark mode with a modern look and feel -``` - -## Watch the instructions file in action - -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. - -1. Close all files currently open in VS Code or your Codespace. -2. Select the `+` icon in GitHub Copilot chat to start a new chat. -3. Ask Copilot chat **What are the guidelines for the flask app?** -4. Note the references now includes the instructions file and provides information gathered from it. - -![Screenshot of the chat window with the references section expanded displaying Copilot instructions in the list](./images/5-copilot-chat-references.png) - -## Summary and next steps - -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]! - -## Resources - -- [Getting started with GitHub Copilot][copilot-getting-started] -- [Adding repository custom instructions for GitHub Copilot][copilot-custom-instructions] -- [Adding personal custom instructions for GitHub Copilot][copilot-personal-instructions] -- [Copilot Chat cookbook][copilot-chat-cookbook] -- [Use Copilot Chat in VS Code][vscode-copilot-chat] - -| [← Implement testing][walkthrough-previous] | [Next: Coding with GitHub Copilot →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[copilot-chat-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook -[copilot-custom-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot -[copilot-eclipse]: https://marketplace.eclipse.org/content/github-copilot -[copilot-getting-started]: https://docs.github.com/en/copilot/getting-started-with-github-copilot -[copilot-jetbrains]: https://plugins.jetbrains.com/plugin/17718-github-copilot -[copilot-prompt-files]: https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot?tool=vscode#about-prompt-files -[copilot-personal-instructions]: https://docs.github.com/en/copilot/customizing-copilot/adding-personal-custom-instructions-for-github-copilot -[copilot-signup]: https://github.com/github-copilot/signup -[copilot-vim]: https://github.com/github/copilot.vim#getting-started -[copilot-vs]: https://marketplace.visualstudio.com/items?itemName=GitHub.copilotvs -[copilot-vscode]: https://marketplace.visualstudio.com/items?itemName=GitHub.copilot -[copilot-xcode]: https://github.com/github/CopilotForXcode -[devcontainer-docs]: https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers -[vscode-copilot-chat]: https://code.visualstudio.com/docs/copilot/copilot-chat -[walkthrough-codespaces]: ./3-codespaces.md -[walkthrough-next]: 6-code.md -[walkthrough-previous]: 4-testing.md - diff --git a/content/full-day/6-code.md b/content/full-day/6-code.md deleted file mode 100644 index 0718cf0a..00000000 --- a/content/full-day/6-code.md +++ /dev/null @@ -1,118 +0,0 @@ -# Coding with GitHub Copilot - -| [← Helping GitHub Copilot understand context][walkthrough-previous] | [Next: GitHub flow →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -## Scenario - -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. - -## Overview of this exercise - -In the next handful of steps, you will: - -- create a new Flask endpoint to list the breeds available. -- add the associated unit test. -- update the backend and frontend to display the list and add the filters as required in the scenario. - -## GitHub Copilot interfaces - -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. - -## Create a new Flask route with Code completion - -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. - -> [!IMPORTANT] -> At this time, the Copilot instructions file is only available to Copilot chat. - -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. - -> [!NOTE] -> 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. - -Let's build our new route in our Flask backend with the help of code completion. - -1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. -2. Open **app/server/app.py**. -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 82, and the code will be: - - ```python - if __name__ == '__main__': - app.run(debug=True, port=5100) # Port 5100 to avoid macOS conflicts - ``` - -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. -5. Select Tab to accept the code suggestion. -6. Navigate to [http://localhost:5100/api/breeds][localhost-breeds] to validate the route. - -> [!NOTE] -> 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! - -## Generate the unit tests - -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! - -1. Return to your Codespace or VS Code. -2. Highlight the code you generated in the prior step. -3. Open GitHub Copilot chat. -4. Select the `+` button to start a new chat. -5. Type **/tests** and select tab to activate the command, then press enter to run the command. GitHub Copilot will generate the tests! -6. Select the **Apply edits** button just above the generated code suggestion to apply the changes to **test_app.py**. -7. Review and validate the code, making any necessary changes. Select **Keep** once you're satisfied. -> [!IMPORTANT] -> 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. -8. Open a terminal window in your codespace or VS Code by selecting Ctrl+Shift+` -9. Ensure the virtual environment is activated by running the terminal command `source ./venv/bin/activate` -10. Navigate to the **app/server** folder by running the terminal command `cd app/server` -11. Run the tests by running the terminal command `python -m unittest` -12. Ensure all tests pass! - -## Add the filters - -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 Astro frontend. Fortunately, Copilot Edits can update multiple files! Let's get our page updated with the help of Copilot Edits. - -1. Open the following files in your IDE (which we'll point Copilot chat to for context): - - **app/server/app.py** - - **app/server/test_app.py** - - **app/client/src/components/DogList.astro** -2. Open GitHub Copilot Chat. -3. Switch to edit mode by selecting **Edit** in the chat mode dropdown at the bottom of Chat view (should be currently **Ask**) -4. If available, select **Claude 3.7 Sonnet** for the model. -5. Select **Add Context...** in the chat window. -6. Select **app/server/app.py**, **app/client/src/components/DogList.astro** and **app/server/test_app.py** files (you need to select **Add context** for each file) -> [!TIP] -> 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`) -7. Ask Copilot to perform the operation you want, to update the page to add the filters. It should meet the following requirements: - - A dropdown list should be provided with all breeds - - A checkbox should be available to only show available dogs - - The page should automatically refresh whenever a change is made - - Tests should be updated for any changes to the endpoint. -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. -9. Open the page at [http://localhost:4321][localhost] to see the updates! -10. Run the Python tests by using `python -m unittest` in the terminal as you did previously. -11. If any changes are needed, explain the required updates to GitHub Copilot and allow it to generate the new code. - -> [!IMPORTANT] -> 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. - -## Summary and next steps -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]! - -## Resources -- [Asking GitHub Copilot questions in your IDE][copilot-questions] -- [Copilot Edits][copilot-chat-edits] -- [Copilot Chat cookbook][copilot-chat-cookbook] - -| [← Helping GitHub Copilot understand context][walkthrough-previous] | [Next: GitHub flow →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[copilot-chat-cookbook]: https://docs.github.com/en/copilot/copilot-chat-cookbook -[copilot-chat-edits]: https://code.visualstudio.com/docs/copilot/copilot-edits -[copilot-questions]: https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/asking-github-copilot-questions-in-your-ide -[localhost]: http://localhost:4321 -[localhost-breeds]: http://localhost:5100/api/breeds -[walkthrough-previous]: 5-context.md -[walkthrough-next]: 7-github-flow.md diff --git a/content/full-day/7-github-flow.md b/content/full-day/7-github-flow.md deleted file mode 100644 index 09e5adfc..00000000 --- a/content/full-day/7-github-flow.md +++ /dev/null @@ -1,114 +0,0 @@ -# GitHub flow - -| [← Add new functionality][walkthrough-previous] | [Next: Deploy the application →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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: - -1. Create a branch -1. Make the desired changes -1. Create a [pull request][about-prs] -1. Review changes, gather feedback and make updates -1. Review results of automated operations such as testing for continuous integration -1. If changes are approved, merge into codebase - -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. - -> [!NOTE] -> 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. - -## Scenario - -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. - -## Creating a branch - -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. - -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. - -1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. -2. Open a **terminal window** by pressing Ctrl + `. -3. In the terminal window, enter the following command to create and switch to a new branch named `add-filter`: - - ```bash - git checkout -b add-filter - ``` - -4. Stage all code to be committed to the new branch by entering the following command in the terminal window: - - ```bash - git add . - ``` - -5. Let Copilot generate a commit message by selecting the **Quick fix** icon (represented by sparkles) and **Generate Commit Message**. - - ![Screenshot of the quick fix menu with Generate Commit Message selected](./images/7-generate-commit-message.png). - -6. Press enter to run the command. -7. Finally, push the new branch to the repository by entering the following command in the terminal window: - - ```bash - git push -u origin add-filter - ``` - -## Create the pull request to suggest updates - -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. - -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. - -1. Return to your codespace. -1. Find the number for the [issue you created earlier][issues-exercise] titled **Add filters to dog list** by entering the following command in the terminal window: - - ```bash - gh issue list - ``` - -1. Create a pull request with the title **Add dog list filters** and body **Resolves #\**, replacing **\** with the issue number you obtained in the previous step by entering the following command in the terminal window: - - ```bash - gh pr create -t "Add dog list filters" -b "Resolves #" - ``` - -## Explore and merge the pull request - -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. - -In our scenario, we created an automated workflow for server unit 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! - -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. - -1. Follow the link displayed in the terminal window by using Ctrl - **Click** (or Cmd - **Click** on a Mac). -1. In the page displayed, note the workflow running the [unit tests created earlier][testing-exercise] and [code scanning][security-exercise]. -1. When the workflows complete successfully, select **Merge pull request** to merge your changes into the **main** branch. - -Congratulations! You've now used the GitHub flow to suggest changes, perform a review, and merge those into your codebase. - -## Summary and next steps - -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]. - -## Resources - -- [GitHub flow][github-flow] -- [GitHub Skills: Review pull requests][skills-review-prs] -- [GitHub Skills: Release based workflow][skills-release-workflow] - -| [← Add new functionality][walkthrough-previous] | [Next: Deploy the application →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[about-branches]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches -[about-prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests -[cicd-resources]: https://resources.github.com/ci-cd/ -[code-exercise]: ./6-code.md -[github-codespaces]: https://github.com/features/codespaces -[github-cli]: https://cli.github.com/ -[github-flow]: https://docs.github.com/en/get-started/quickstart/github-flow -[issues-exercise]: ./2-issues.md -[security-exercise]: ./1-code-scanning.md -[skills-review-prs]: https://github.com/skills/review-pull-requests -[skills-release-workflow]: https://github.com/skills/release-based-workflow -[testing-exercise]: ./4-testing.md -[walkthrough-previous]: 6-code.md -[walkthrough-next]: 8-deployment.md diff --git a/content/full-day/8-deployment.md b/content/full-day/8-deployment.md deleted file mode 100644 index 4f608ac0..00000000 --- a/content/full-day/8-deployment.md +++ /dev/null @@ -1,210 +0,0 @@ -# Deploying the project to the cloud - -| [← GitHub flow][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -> [!NOTE] -> We've taken a couple of shortcuts with the application structure to ensure things run smoothly in this workshop. - -## Scenario - -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. - -## Return to main - -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. - -1. Return to your codespace, or reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. -2. Open a new terminal window by selecting Ctrl+Shift+`. -3. Run the following commands to checkout the main branch and obtain the updates from the repository: - - ```sh - git checkout main - git pull - ``` - -## Identity management - -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. - -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! - -## Asking Azure how to deploy to Azure - -GitHub Copilot chat supports [extensions][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. - -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: - -- Deploy the project to the cloud -- Use a GitHub action to manage the deployment process - -1. Open GitHub Copilot Chat. -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). - -> [!NOTE] -> Since this is your first time using the extension, you will be prompted to signin to Azure. Follow the prompts as they appear. - -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. - -## Overview of the response from Copilot - -The response from GitHub Copilot will likely contain instructions to use the following commands: - -- `azd init --from-code` to create the Azure configuration files using [bicep][bicep-docs]. -- `azd auth login` to authenticate to Azure. -- `azd pipeline config` to create the GitHub Workflow. - -[azd][azd-docs] is a commandline utility to help streamline the deployment process to Azure. We'll use it to: - -- generate the bicep file. -- create the workflow file. -- create and configure OIDC for the workflow. - -If you're curious about **azd** or Azure, you can always ask the extension using GitHub Copilot! - -## Install azd - -Let's start by installing **azd**. - -1. Run the command in the terminal to install **azd**: - - ```sh - curl -fsSL https://aka.ms/install-azd.sh | bash - ``` - -## Create and configure the bicep file - -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. - -1. Run the `init` command to create the bicep file. - - ```sh - azd init --from-code - ``` - -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. -3. Open the bicep file located at **infra**/**resources.bicep**. -4. Find the section (around line 130) which reads: - - ```bicep - { - name: 'PORT' - value: '4321' - } - ``` - -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: - - ```bicep - { - name: 'API_SERVER_URL' - value: 'https://${server.outputs.fqdn}' - } - ``` - -> [!NOTE] -> While the syntax resembles JSON, it's not JSON. As a result, resist the urge to add commas to separate the values! - -## Create the workflow - -`azd` can create and configure a workflow (or sometimes called a pipeline) for deploying your project. In particular it will: - -- create OIDC credentials to use for deployment. -- define the YML file in the **workflows** folder. - -Let's let `azd` do its work! - -1. Return to your terminal window, and run the following command to authenticate with `azd` - - ```sh - azd auth login - ``` - -2. Follow the prompts to authenticate to Azure using the credentials you specified previously. -3. Create the pipeline by running the following command: - - ```sh - azd pipeline config - ``` - -4. Follow the prompts, accepting the defaults. One of the prompts will ask if you wish to perform the deployment now - say yes! -5. Away your application goes to the cloud! - -## Track the deployment and test your application - -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! - -1. Open the workflow at **.github/workflows/azure-dev.yml**. -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. -3. Note the core steps, which checkout your code, authenticate to Azure, create or update the infrastructure, then deploy the application. -4. If you have questions about what the workflow is doing, ask GitHub Copilot! -5. Navigate to your repository on GitHub. -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). -7. Select the running workflow (which should be named **Configure Azure Developer Pipeline**). -8. Select the **build** step. -9. Track the deployment process, which will take about 5-10 minutes (good time for a water break!). -10. Once the process completes, expand the **Deploy Application** section. You should see the log indicating the client and server were both deployed: - - ``` - Deploying service client - Deploying service client (Building Docker image) - Deploying service client (Tagging container image) - Deploying service client (Tagging container image) - Deploying service client (Logging into container registry) - Deploying service client (Pushing container image) - Deploying service client (Updating container app revision) - Deploying service client (Fetching endpoints for container app service) - (✓) Done: Deploying service client - - Endpoint: https://client.delightfulfield-8f7ef050.westus.azurecontainerapps.io/ - - Deploying service server - Acquiring pack cli - Deploying service server (Building Docker image from source) - Deploying service server (Tagging container image) - Deploying service server (Tagging container image) - Deploying service server (Logging into container registry) - Deploying service server (Pushing container image) - Deploying service server (Updating container app revision) - Deploying service server (Fetching endpoints for container app service) - (✓) Done: Deploying service server - - Endpoint: https://server.delightfulfield-8f7ef050.westus.azurecontainerapps.io/ - ``` - -11. Select the Endpoint for the client. You should see your application! - -You've now deployed your project! - -## Summary - -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. - -## Wrap-up and challenge - -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! - -If you wish to continue exploring from here, there are a couple of tasks you could pursue: - -- 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. -- Migrate the database to something more powerful such as Postgres or SQL Server. - -Work with the workshop leaders as needed to ask questions and get guidance as you continue to build on the skills you learned today! - -## Resources - -- [About security hardening with OpenID Connect][oidc-docs] -- [Deploying with GitHub Actions][actions-deploy] -- [What is the Azure Developer CLI?][azd-docs] - -| [← GitHub flow][walkthrough-previous] | [Next: Pets workshop selection →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[actions-deploy]: https://docs.github.com/en/actions/use-cases-and-examples/deploying/deploying-with-github-actions -[azd-docs]: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview?tabs=linux -[azure-copilot-extension]: https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azure-github-copilot -[bicep-docs]: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep -[extensions-copilot-chat]: ./5-context.md -[oidc-docs]: https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect -[walkthrough-previous]: 7-github-flow.md -[walkthrough-next]: ../README.md \ No newline at end of file diff --git a/content/full-day/README.md b/content/full-day/README.md deleted file mode 100644 index 985a4288..00000000 --- a/content/full-day/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Modern DevOps with GitHub - -| [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[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. - -This workshop is built to help guide you through some of the most common DevOps tasks on GitHub. You'll explore: - -- Managing projects with [GitHub Issues][github-issues] -- Creating a development environment with [GitHub Codespaces][github-codespaces] -- Using [GitHub Copilot][github-copilot] as your AI pair programmer -- Securing the development pipeline with [GitHub Advanced Security][github-security] -- Automating tasks and CI/CD with [GitHub Actions][github-actions] - -## Prerequisites - -The application used for the workshop is built primarily with Python (Flask and SQLAlchemy) and Astro (using Tailwind). 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! - -## Required resources - -To complete this workshop, you will need the following: - -- A [GitHub account][github-signup] -- Access to [GitHub Copilot][github-copilot] - -## Getting started - -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. - -0. [Setup your environment][walkthrough-next] for the workshop -1. [Enable Code Scanning][code-scanning] to ensure new code is secure -2. [Create an issue][issues] to document a feature request -3. [Create a codespace][codespaces] to start writing code -4. [Implement testing][testing] to supplement continuous integration -5. [Provide Copilot context][context] to generate quality code suggestions -6. [Add features to your app][code] with GitHub Copilot -7. [Use the GitHub flow][github-flow] to incorporate changes into your codebase -8. [Deploy your application][deployment] to Azure to make your application available to users - -## Check out these resources to dive in and learn more -Check out the resources in [**GitHub-Copilot-Resources.md**][GitHub-Copilot-Resources]. - -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. - -| [← Pets workshop selection][walkthrough-previous] | [Next: Workshop setup →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[code]: ./6-code.md -[code-scanning]: ./1-code-scanning.md -[codespaces]: ./3-codespaces.md -[context]: ./5-context.md -[deployment]: ./8-deployment.md -[devops]: https://en.wikipedia.org/wiki/DevOps -[github-actions]: https://github.com/features/actions -[github-codespaces]: https://github.com/features/codespaces -[github-copilot]: https://github.com/features/copilot -[github-flow]: ./7-github-flow.md -[github-issues]: https://github.com/features/issues -[github-security]: https://github.com/features/security -[github-signup]: https://github.com/join -[issues]: ./2-issues.md -[portmanteau]: https://www.merriam-webster.com/dictionary/portmanteau -[testing]: ./4-testing.md -[walkthrough-next]: ./0-setup.md -[walkthrough-previous]: ../README.md -[GitHub-Copilot-Resources]: ../GitHub-Copilot-Resources.md diff --git a/content/full-day/images/1-code-scanning-dialog.png b/content/full-day/images/1-code-scanning-dialog.png deleted file mode 100644 index e43dec08..00000000 Binary files a/content/full-day/images/1-code-scanning-dialog.png and /dev/null differ diff --git a/content/full-day/images/1-code-scanning.png b/content/full-day/images/1-code-scanning.png deleted file mode 100644 index d653befc..00000000 Binary files a/content/full-day/images/1-code-scanning.png and /dev/null differ diff --git a/content/full-day/images/1-dependabot.png b/content/full-day/images/1-dependabot.png deleted file mode 100644 index 48f13e44..00000000 Binary files a/content/full-day/images/1-dependabot.png and /dev/null differ diff --git a/content/full-day/images/1-secret-scanning.png b/content/full-day/images/1-secret-scanning.png deleted file mode 100644 index cca3c85e..00000000 Binary files a/content/full-day/images/1-secret-scanning.png and /dev/null differ diff --git a/content/full-day/images/3-open-browser.png b/content/full-day/images/3-open-browser.png deleted file mode 100644 index e7641f7f..00000000 Binary files a/content/full-day/images/3-open-browser.png and /dev/null differ diff --git a/content/full-day/images/3-reload.png b/content/full-day/images/3-reload.png deleted file mode 100644 index 2fec766c..00000000 Binary files a/content/full-day/images/3-reload.png and /dev/null differ diff --git a/content/full-day/images/3-secrets-variables.png b/content/full-day/images/3-secrets-variables.png deleted file mode 100644 index 69210fee..00000000 Binary files a/content/full-day/images/3-secrets-variables.png and /dev/null differ diff --git a/content/full-day/images/4-select-file.png b/content/full-day/images/4-select-file.png deleted file mode 100644 index 0a4fc49a..00000000 Binary files a/content/full-day/images/4-select-file.png and /dev/null differ diff --git a/content/full-day/images/5-copilot-chat-references.png b/content/full-day/images/5-copilot-chat-references.png deleted file mode 100644 index 3892b498..00000000 Binary files a/content/full-day/images/5-copilot-chat-references.png and /dev/null differ diff --git a/content/full-day/images/7-generate-commit-message.png b/content/full-day/images/7-generate-commit-message.png deleted file mode 100644 index c5f1c9b2..00000000 Binary files a/content/full-day/images/7-generate-commit-message.png and /dev/null differ diff --git a/content/github-actions/0-setup.md b/content/github-actions/0-setup.md deleted file mode 100644 index 3bccf7f7..00000000 --- a/content/github-actions/0-setup.md +++ /dev/null @@ -1,51 +0,0 @@ -# Workshop Setup - -| [← GitHub Actions: From CI to CD][walkthrough-previous] | [Next: Introduction & Your First Workflow →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -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. - -The repository for this workshop is configured as a template, so we can use it to create your repository. - -## Create your repository - -Let's create the repository you'll use for your workshop. - -1. Navigate to [the repository root][repo-root] -2. Select **Use this template** > **Create a new repository** - - ![Screenshot of Use this template dropdown](../shared-images/setup-use-template.png) - -3. Under **Owner**, select the name of your GitHub handle, or the owner specified by your workshop leader. -4. Under **Repository**, set the name to **pets-workshop**, or the name specified by your workshop leader. -5. Ensure **Public** is selected for the visibility, or the value indicated by your workshop leader. -6. Select **Create repository from template**. - - ![Screenshot of configured template creation dialog](../shared-images/setup-configure-repo.png) - -In a few moments a new repository will be created from the template for this workshop! - -## Open your codespace - -Now let's open a codespace so you have a development environment ready to go. - -1. Navigate to the main page of your newly created repository. -2. Select **Code** > **Codespaces** > **Create codespace on main**. - - In a few moments a codespace will open in your browser with a full VS Code editor. This is where you'll create and edit files throughout the workshop. - -> [!TIP] -> If your codespace ever disconnects or you close the tab, you can reopen it by navigating to your repository and selecting **Code** > **Codespaces** and the name of your codespace. - -## Summary and next steps - -You've created the repository and opened a codespace — you're ready to start building! Next let's [create your first workflow][walkthrough-next]. - -| [← GitHub Actions: From CI to CD][walkthrough-previous] | [Next: Introduction & Your First Workflow →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[fork-repo]: https://docs.github.com/get-started/quickstart/fork-a-repo -[template-repo]: https://docs.github.com/repositories/creating-and-managing-repositories/creating-a-template-repository -[repo-root]: / -[walkthrough-previous]: README.md -[walkthrough-next]: 1-introduction.md diff --git a/content/github-actions/1-introduction.md b/content/github-actions/1-introduction.md deleted file mode 100644 index eb6925b3..00000000 --- a/content/github-actions/1-introduction.md +++ /dev/null @@ -1,126 +0,0 @@ -# Introduction & Your First Workflow - -| [← Workshop Setup][walkthrough-previous] | [Next: Securing the Development Pipeline →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[GitHub Actions][github-actions] is an automation platform built into GitHub that lets you build, test, and deploy your code directly from your repository. While it's most commonly used for CI/CD, it can automate just about any task in your development workflow — from labeling issues to resizing images. - -Before diving in, here are the key terms you'll encounter: - -- **Workflow**: An automated process defined in a YAML file, stored in `.github/workflows/`. -- **Event**: A trigger that starts a workflow, such as a `push`, `pull_request`, or `workflow_dispatch`. -- **Job**: A set of steps that run on the same runner. Jobs run in parallel by default. -- **Step**: An individual task within a job — either a shell command (`run`) or a reusable action (`uses`). -- **Runner**: The virtual machine that executes your jobs (e.g., `ubuntu-latest`). -- **Action**: A reusable unit of code that performs a specific task, published on the [Actions Marketplace][actions-marketplace]. - -## Scenario - -The shelter has built its application — a Flask API and Astro frontend — and the team is ready to start automating their development workflow. Before diving into CI/CD, let's start with the basics: creating a simple workflow, triggering it manually, and understanding the logs. - -## Background - -A workflow file is written in YAML and lives in the `.github/workflows/` directory. Here are the core sections you'll work with: - -- `name`: A human-readable name for the workflow, displayed in the **Actions** tab. -- `on`: Defines the events that trigger the workflow (e.g., `push`, `pull_request`, `workflow_dispatch`). -- `jobs`: Contains one or more jobs, each with a unique identifier. - - `runs-on`: Specifies the runner environment (e.g., `ubuntu-latest`). - - `steps`: An ordered list of tasks the job performs. - - `uses`: References a reusable action (e.g., `actions/checkout@v4`). - - `run`: Executes a shell command. - -## Create your first workflow - -Let's start with the classic "Hello World" — a workflow you can trigger manually from the GitHub UI. - -1. In your codespace, create the folder `.github/workflows/` if it doesn't already exist. -2. Create a new file named `.github/workflows/hello.yml`. -3. Add the following content: - - ```yaml - name: Hello World - - on: - workflow_dispatch: - - jobs: - greet: - runs-on: ubuntu-latest - - steps: - - name: Say hello - run: echo "Hello, GitHub Actions!" - - - name: Show environment info - run: | - echo "Runner OS: $RUNNER_OS" - echo "Repository: $GITHUB_REPOSITORY" - echo "Triggered by: $GITHUB_ACTOR" - ``` - -4. Save the file. - -> [!NOTE] -> The `workflow_dispatch` event lets you trigger the workflow manually from the **Actions** tab. This is useful for testing workflows without needing to push code changes every time. - -## Push and run - -Now let's push the workflow and trigger it by hand. - -1. Open the terminal in your codespace by pressing Ctl+`. -2. Stage and commit your changes: - - ```bash - git add .github/workflows/hello.yml - git commit -m "Add hello world workflow" - ``` - -3. Push to your repository: - - ```bash - git push - ``` - -4. Navigate to your repository on GitHub and select the **Actions** tab. -5. In the left sidebar, select the **Hello World** workflow. -6. Select the **Run workflow** button, keep the default branch, and select **Run workflow** again to confirm. - -## Explore the logs - -Once the run completes, let's explore what happened. - -1. Select the workflow run that just completed. -2. Select the **greet** job to expand it. -3. Explore the logs for each step: - - **Say hello** — you'll see the `echo` output. - - **Show environment info** — notice the environment variables that GitHub Actions provides automatically (`RUNNER_OS`, `GITHUB_REPOSITORY`, `GITHUB_ACTOR`). -4. Also look at the **Set up job** and **Complete job** steps that Actions adds automatically — these show the runner setup and cleanup. - -> [!TIP] -> You can search within the logs using the search box at the top of the log viewer, and expand or collapse individual steps. This becomes very useful as workflows grow more complex. - -## Summary and next steps - -Congratulations! You've created and run your first GitHub Actions workflow. You've learned how to define a workflow in YAML, trigger it manually with `workflow_dispatch`, and navigate the logs in the Actions UI. - -Next, we'll put this knowledge to work by [securing the development pipeline][walkthrough-next] with code scanning, Dependabot, and secret scanning. - -## Resources - -- [GitHub Actions documentation][github-actions-docs] -- [Workflow syntax for GitHub Actions][workflow-syntax] -- [Events that trigger workflows][workflow-triggers] -- [Understanding GitHub Actions][understanding-actions] - -| [← Workshop Setup][walkthrough-previous] | [Next: Securing the Development Pipeline →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[actions-marketplace]: https://github.com/marketplace?type=actions -[github-actions]: https://github.com/features/actions -[github-actions-docs]: https://docs.github.com/actions -[understanding-actions]: https://docs.github.com/actions/about-github-actions/understanding-github-actions -[workflow-syntax]: https://docs.github.com/actions/writing-workflows/workflow-syntax-for-github-actions -[workflow-triggers]: https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows -[walkthrough-previous]: 0-setup.md -[walkthrough-next]: 2-code-scanning.md diff --git a/content/github-actions/2-code-scanning.md b/content/github-actions/2-code-scanning.md deleted file mode 100644 index 55de46b4..00000000 --- a/content/github-actions/2-code-scanning.md +++ /dev/null @@ -1,121 +0,0 @@ -# Securing the Development Pipeline - -| [← Introduction & Your First Workflow][walkthrough-previous] | [Next: Running Tests →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -In the previous exercise you created your first GitHub Actions workflow — a manually triggered "Hello World." Before building out CI/CD, let's explore security. Ensuring code security is imperative in today's environment, and GitHub provides tools that automate this for you — many of which are powered by GitHub Actions under the hood. - -When we think about how we create code today, there are three main areas to secure: - -- The **code we write** — which may contain vulnerabilities -- The **libraries we use** — which may have known security issues -- The **credentials we manage** — which may accidentally leak into source code - -[GitHub Advanced Security][advanced-security] provides a suite of tools covering each of these areas. Let's explore and enable them on our repository. - -## Scenario - -Security is important in every application. By detecting potential vulnerabilities early, teams can make updates before incidents occur. The shelter wants 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. - -## Background - -[GitHub Advanced Security][advanced-security-docs] is a set of security features available directly in GitHub. The three pillars are: - -- **Code scanning** analyzes your source code for security vulnerabilities using [CodeQL][about-code-scanning], GitHub's semantic code analysis engine. When enabled, it runs as a GitHub Actions workflow — the same automation platform you used in the previous exercise. Every push and pull request triggers the analysis automatically. -- **Dependabot** monitors your project's dependencies for known vulnerabilities and can automatically create [pull requests][about-prs] to update insecure packages to safe versions. -- **Secret scanning** detects tokens, keys, and other credentials that have been committed to your repository, and can block pushes that contain [supported secrets][supported-secrets]. - -> [!NOTE] -> Code scanning is built on [GitHub Actions][github-actions]. When you enable CodeQL's default setup, GitHub creates and manages a workflow for you behind the scenes. You'll see this connection more clearly when you navigate to the **Actions** tab after enabling it. This is a great example of how Actions powers automation across the GitHub platform — not just CI/CD pipelines you write yourself. - -## Configure Dependabot - -Most projects depend on open source and external libraries. While modern development would be impossible without them, we always need to ensure the dependencies we use are secure. [Dependabot][dependabot-quickstart] monitors your repository's dependencies and raises alerts — or even creates pull requests — to update insecure packages. - -Public repositories on GitHub automatically have Dependabot alerts enabled. Let's configure Dependabot to also create PRs that update insecure library versions automatically. - -1. Navigate to your repository on GitHub. -2. Select **Settings** > **Advanced security** (under **Security** in the sidebar). -3. Locate the **Dependabot** section. - - ![Screenshot of the Dependabot section](../shared-images/dependabot-settings.png) - -4. Select **Enable** next to **Dependabot security updates** to configure Dependabot to create PRs to resolve alerts. - -You've now enabled Dependabot alerts and security updates! When an insecure library is detected, you'll receive an alert, and Dependabot will create a pull request to update to a secure version. - -> [!TIP] -> Dependabot doesn't just alert you — it can automatically create pull requests that bump library versions to secure ones. When you pair this with a CI pipeline that runs tests on every PR (which you'll build in the [next exercise][walkthrough-next]), those Dependabot PRs are automatically tested before merging. This creates a powerful feedback loop: vulnerabilities are detected, fixes are proposed, and your tests verify the update won't break anything — all without manual intervention. - -> [!IMPORTANT] -> After enabling Dependabot security updates you may notice new pull requests created for potentially outdated packages. For this workshop you can ignore these pull requests. - -## Enable secret scanning - -Many developers have accidentally checked in code containing tokens or credentials. Regardless of the reason, even seemingly innocuous tokens can create a security issue. [Secret scanning][about-secret-scanning] detects tokens in your source code and raises alerts. With push protection enabled, pushes containing supported secrets are blocked before they reach your repository. - -1. On the same **Advanced security** settings page, locate the **Secret Protection** section. -2. Next to **GitHub will always send alerts to partners for detected secrets in public repositories**, select **Enable**. -3. Next to **Push protection**, select **Enable** to block pushes containing a [supported secret][supported-secrets]. - - ![Screenshot of fully configured secret scanning](../shared-images/setup-secret-protection.png) - -You've now enabled secret scanning and push protection — helping prevent credentials from reaching your repository. - -## Enable code scanning - -There is a direct relationship between the amount of code an organization writes and its potential attack surface. [Code scanning][about-code-scanning] analyzes your source code for known vulnerabilities. When an issue is detected on a pull request, a comment is added highlighting the affected line with contextual information for the developer. - -Let's enable code scanning with the default CodeQL setup. This runs automatically whenever code is pushed to `main` or a pull request targets `main`, and on a regular schedule to catch newly discovered vulnerabilities. - -1. On the same **Advanced security** settings page, locate the **Code scanning** section. -2. Next to **CodeQL analysis**, select **Set up** > **Default**. - - ![Screenshot of code scanning dropdown menu](../shared-images/code-scanning-setup.png) - -3. On the **CodeQL default configuration** dialog, select **Enable CodeQL**. - - ![Screenshot of code scanning dialog](../shared-images/code-scanning-dialog.png) - -> [!IMPORTANT] -> Your list of languages may be different from what's shown in the screenshot. - -A background process starts and configures a CodeQL analysis workflow for your repository. - -> [!TIP] -> After enabling CodeQL, navigate to the **Actions** tab in your repository. You'll see a new **CodeQL** workflow listed alongside the **Hello World** workflow you created earlier. This is the Actions workflow that GitHub created automatically to run code scanning — proof that Actions isn't just for CI/CD, but powers many of GitHub's built-in features. - -## Summary and next steps - -You've enabled GitHub Advanced Security for your repository: - -- **Dependabot** monitors dependencies for known vulnerabilities and creates PRs to update them. -- **Secret scanning** detects leaked credentials and blocks pushes containing supported secrets. -- **Code scanning** analyzes your source code using CodeQL, running as a GitHub Actions workflow on every push and PR. - -These tools run automatically in the background, catching security issues before they reach production. Now that you've seen how GitHub uses Actions internally for security automation, it's time to build your own CI workflow. Next, we'll [automate testing][walkthrough-next] for the shelter's application. - -## Resources - -- [About GitHub Advanced Security][advanced-security-docs] -- [About code scanning with CodeQL][about-code-scanning] -- [Dependabot quickstart guide][dependabot-quickstart] -- [About secret scanning][about-secret-scanning] -- [GitHub Skills: Secure your repository's supply chain][skills-supply-chain] -- [GitHub Skills: Secure code game][skills-secure-code] - -| [← Introduction & Your First Workflow][walkthrough-previous] | [Next: Running Tests →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[about-code-scanning]: https://docs.github.com/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning -[about-prs]: https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests -[about-secret-scanning]: https://docs.github.com/code-security/secret-scanning/introduction/about-secret-scanning -[advanced-security]: https://github.com/features/security -[advanced-security-docs]: https://docs.github.com/get-started/learning-about-github/about-github-advanced-security -[dependabot-quickstart]: https://docs.github.com/code-security/getting-started/dependabot-quickstart-guide -[github-actions]: https://github.com/features/actions -[supported-secrets]: https://docs.github.com/code-security/secret-scanning/introduction/supported-secret-scanning-patterns -[skills-supply-chain]: https://github.com/skills/secure-repository-supply-chain -[skills-secure-code]: https://github.com/skills/secure-code-game -[walkthrough-previous]: 1-introduction.md -[walkthrough-next]: 3-running-tests.md diff --git a/content/github-actions/3-running-tests.md b/content/github-actions/3-running-tests.md deleted file mode 100644 index 2fc3060f..00000000 --- a/content/github-actions/3-running-tests.md +++ /dev/null @@ -1,202 +0,0 @@ -# Running Tests - -| [← Securing the Development Pipeline][walkthrough-previous] | [Next: Caching →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -Now that you know the basics of GitHub Actions and have seen how GitHub uses it for code scanning, it's time to build your own workflow. In this exercise you'll create a **continuous integration (CI)** pipeline that automatically runs the shelter's tests. - -## Scenario - -The shelter's app is growing, and the team wants to make sure new changes don't break existing functionality. The application has two test suites: **unit tests** for the Flask API, and **end-to-end (e2e) tests** that use [Playwright][playwright] to test the full stack in a browser. The goal is to run both automatically on every push and pull request (PR) to `main`. - -## Background - -As you saw in the [introduction][introduction], the `on` declaration specifies when a workflow will run. For true automation, you'll use `on` to indicate the [triggers][workflow-triggers] for the workflow to run automatically. In our scenario, this will be whenever a PR is made to the `main` branch, or when code is pushed or merged into it. - -Most workflows have a relatively common set of tasks. You typically need to install libraries, perform builds, and run various commands. Rather than having to script everything out by hand, there's a collection of available actions in a marketplace - the aptly named [Actions Marketplace][actions-marketplace]. There you can find pluggable, reusable actions, ready to be added to any workflow. - -## Using the Actions Marketplace - -The [Actions Marketplace][actions-marketplace] contains tens of thousands of community created actions. These include those from OSS contributors of all sizes, and vendors to allow for quick integration of their products. - -For most actions, you can just add the name of the action, typically `vendor/action-name`, the necessary configuration, and it's now part of your workflow! - -### Security and the Actions Marketplace - -The marketplace offers various protections to ensure you're using the right action at the right time. For starters, creators can be [verified][marketplace-badges] by GitHub, giving you the confidence the organization who says they built an action is the one who actually built it. - -In addition, you can [pin to a specific version, SHA or branch][action-versioning]. This both increases security, knowing the code you expect to run is what runs, and consistency as it'll always be the same code over and over. - -## Create the CI workflow - -Our application has a Flask backend with unit tests, and an Astro frontend that's validated with end-to-end tests. Let's begin building a workflow to run these tests. We'll start with the unit tests, then add the end-to-end tests a bit later in this lesson. - -To run the unit tests, you'll need to do the following in the workflow: - -- checkout the code. -- install Python. -- install the necessary Python libraries. -- run the tests. - -Let's build that out! - -1. In your codespace, create a new file named `.github/workflows/run-tests.yml`. -2. Add the following content: - - ```yaml - name: Run Tests - - on: - push: - branches: [main] - pull_request: - branches: [main] - - permissions: - contents: read - - jobs: - test-api: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.14' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r app/server/requirements.txt - - - name: Run tests - working-directory: ./app/server - run: | - python -m unittest test_app -v - ``` - -3. Save the file. - -Notice how this workflow differs from the hello world: -- It triggers on `push` and `pull_request` events instead of `workflow_dispatch` — so it runs automatically when a PR or merge is made to the specified branch(es). -- It declares explicit **`permissions`** — we'll explain this next. -- It uses `actions/checkout@v4` to clone your repository code onto the runner, using the `checkout` action from the marketplace. -- It uses `actions/setup-python@v5` to install a specific Python version, yet another action from the marketplace. -- Next, it installs the necessary libraries using `pip`, just like you normally would. -- Finally, it's time to run the tests - again, just like before! - -## Understanding `GITHUB_TOKEN` and permissions - -Every workflow run automatically receives a token called **`GITHUB_TOKEN`**. This is a short-lived credential that actions use behind the scenes to interact with your repository — for example, `actions/checkout` uses it to clone your code. The token is created when the workflow starts and revoked when the run ends. - -The **`permissions`** block controls what this token can do. For our CI workflow, we only need `contents: read` — enough to clone the repository. This follows the [principle of least privilege][principle-least-privilege]: grant only the permissions your workflow actually needs, nothing more. - -> [!IMPORTANT] -> Always set explicit `permissions` in your workflows. Without it, the token inherits the repository-level defaults (**Settings** > **Actions** > **General** > **Workflow permissions**), which may be more permissive than your workflow requires. Being explicit ensures your workflow only has the access it needs — even if someone changes the repository defaults later. - -## Push and explore - -A bit later you'll use a more standard branching approach for changes. But for our purposes right now, let's push straight to `main`. What you'll notice is the workflow will automatically run, since the workflow will now exist on `main`! - -1. Open the terminal in your codespace by pressing Ctl+`, then stage, commit, and push: - - ```bash - git add .github/workflows/run-tests.yml - git commit -m "Add CI workflow with unit tests" - git push - ``` - -2. Navigate to the **Actions** tab — the **Run Tests** workflow should already be running (triggered by the push). -3. Select the **test-api** job and explore the logs. Notice the flow of checkout, Python setup, and dependency installation. - -## Add e2e tests in parallel - -The unit tests cover the API, but the shelter also has Playwright e2e tests that verify the full application works end-to-end in a real browser. Let's add a second job that runs alongside the unit tests. - -1. Return to your codespace and open `.github/workflows/run-tests.yml`. Add the following job to the bottom of the file: - - ```yaml - test-e2e: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.14' - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install -r app/server/requirements.txt - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install Node dependencies - working-directory: ./app/client - run: npm ci - - - name: Install Playwright browsers - working-directory: ./app/client - run: npx playwright install --with-deps chromium - - - name: Run e2e tests - working-directory: ./app/client - run: npx playwright test - ``` - -2. Save the file. - -> [!NOTE] -> Because we haven't added a `needs` key, `test-api` and `test-e2e` will run **in parallel**. Each job gets its own runner, so they don't interfere with each other and the total CI time is closer to the duration of the slower job rather than the sum of both. The `test-e2e` job needs both Python and Node.js because the Playwright tests launch the full stack — the Flask API and the Astro frontend — before running browser tests against them. - -1. In the terminal, stage, commit, and push: - - ```bash - git add .github/workflows/run-tests.yml - git commit -m "Add e2e tests running in parallel" - git push - ``` - -2. Navigate to the **Actions** tab and select the new workflow run. You should see both **test-api** and **test-e2e** running side by side. - -## Summary and next steps - -You've built a CI pipeline with two jobs running in parallel — unit tests for the API and end-to-end tests for the full application. This is the foundation of continuous integration — catching problems early so they don't reach production. - -Now, let's work to [improve the performance of our CI job][walkthrough-next] by reusing steps and caching dependencies. - -## Resources - -- [GitHub Actions documentation][github-actions-docs] -- [Workflow syntax for GitHub Actions][workflow-syntax] -- [Events that trigger workflows][workflow-triggers] -- [Using jobs in a workflow][jobs-docs] -- [Automatic token authentication][automatic-token-auth] -- [Assigning permissions to jobs][permissions-docs] - -| [← Securing the Development Pipeline][walkthrough-previous] | [Next: Caching →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[action-versioning]: https://docs.github.com/actions/how-tos/write-workflows/choose-what-workflows-do/find-and-customize-actions#using-release-management-for-your-custom-actions -[actions-marketplace]: https://github.com/marketplace?type=actions -[automatic-token-auth]: https://docs.github.com/actions/security-for-github-actions/security-guides/automatic-token-authentication -[github-actions-docs]: https://docs.github.com/actions -[introduction]: 1-introduction.md -[jobs-docs]: https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/using-jobs-in-a-workflow -[marketplace-badges]: https://docs.github.com/actions/how-tos/create-and-publish-actions/publish-in-github-marketplace#about-badges-in-github-marketplace -[permissions-docs]: https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/assigning-permissions-to-jobs -[playwright]: https://playwright.dev/ -[principle-least-privilege]: https://docs.github.com/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token -[workflow-syntax]: https://docs.github.com/actions/writing-workflows/workflow-syntax-for-github-actions -[workflow-triggers]: https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows -[walkthrough-previous]: 2-code-scanning.md -[walkthrough-next]: 4-caching.md diff --git a/content/github-actions/4-caching.md b/content/github-actions/4-caching.md deleted file mode 100644 index e75a97e4..00000000 --- a/content/github-actions/4-caching.md +++ /dev/null @@ -1,117 +0,0 @@ -# Caching - -| [← Running Tests][walkthrough-previous] | [Next: Matrix Strategies & Parallel Testing →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -The [GitHub Actions Marketplace][actions-marketplace] is a collection of pre-built actions created by GitHub and the community. Actions can set up tools, run tests, deploy code, send notifications, and much more. Rather than writing everything from scratch, you can leverage the work of thousands of developers. - -In this exercise you'll also learn about **caching** — a technique to speed up your workflows by reusing previously downloaded dependencies instead of fetching them from the internet on every run. - -## Scenario - -The CI workflow from the previous exercise works, but both jobs reinstall every dependency from scratch on every run. That means downloading Python packages, Node modules, and Playwright browsers each time — even when they haven't changed. You want to ensure workflows run as quickly as possible, to move from idea to deployed as quickly as possible. - -## Background - -[Caching][caching-docs] stores downloaded dependencies between workflow runs so they don't need to be fetched from the internet every time. Each cache is identified by a key — typically derived from the package manager and lock file. When a workflow runs, it checks for an existing cache matching that key. On a hit, the cached files are restored and the install step completes in seconds. On a miss, the dependencies are downloaded normally and then saved for next time. - -Many popular setup actions — like `actions/setup-python` and `actions/setup-node` — have caching built right in, so you can enable it with a single line. GitHub provides 10 GB of cache storage per repository, with least-recently-used entries evicted when the limit is reached. - -## Add caching to the unit test job - -Many popular setup actions have caching built right in. Let's start with the `test-api` job, which uses Python. Libraries are installed for Python using `pip`, which will become the key name. This instructs the workflow to cache any libraries installed using `pip`. - -1. In your codespace, open `.github/workflows/run-tests.yml`. -2. Update the **Set up Python** step in the `test-api` job to enable pip caching: - - ```yaml - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.14' - cache: 'pip' - ``` - -> [!NOTE] -> The `cache: 'pip'` option tells `setup-python` to cache downloaded pip packages. On the first run it saves the cache; on subsequent runs it restores it, skipping most download time. - -3. Save the file. - -## Add caching to the e2e test job - -The e2e job has two dependencies to cache — Python packages and the Node modules. We can follow the same path here! To make sure our packages are updated when versions change, we're going to set the `package-lock.json` file as a dependency. When the workflow runs, it will look to see if that file has changed; if it has it'll perform a reinstall. If not, it'll use the cache! - -1. Update the **Set up Python** step in the `test-e2e` job the same way: - - ```yaml - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.14' - cache: 'pip' - ``` - -2. Update the **Set up Node.js** step in the `test-e2e` job to enable npm caching: - - ```yaml - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - cache-dependency-path: 'app/client/package-lock.json' - ``` - -3. Save the file. - -> [!NOTE] -> You might wonder about caching Playwright browsers too. Playwright's [official CI guidance][playwright-ci] recommends running `npx playwright install --with-deps` on every run rather than caching browsers, since browser binaries are tightly coupled to the Playwright version and caching them can lead to subtle version mismatches. - -## Compare run times - -Now let's push the changes and see the impact of caching. - -1. In the terminal (Ctl+` to toggle), stage, commit, and push your changes: - - ```bash - git add .github/workflows/run-tests.yml - git commit -m "Add caching to CI workflow" - git push - ``` - -2. Navigate to the **Actions** tab on GitHub and observe the workflow run. -3. Once it completes, check the logs for the setup steps. You should see output indicating a **cache miss** — this is expected on the first run since there's nothing cached yet. -4. To see caching in action, trigger a second run. You can push a small change (such as adding a comment to `run-tests.yml`) or use the GitHub UI: - - Update the `on` section to add `workflow_dispatch:` so you can trigger runs manually - - Push that change, then use the **Run workflow** button on the **Actions** tab - -5. On the second run, check the setup step logs again. You should see a **cache hit**, and the overall run time should be noticeably shorter. - -> [!TIP] -> You can view cache usage for your repository by navigating to **Actions** > **Caches** in the left sidebar. This shows all active caches, their sizes, and when they were last used. - -## Summary and next steps - -The Actions Marketplace provides thousands of pre-built actions so you don't have to reinvent the wheel. Many setup actions like `setup-python` and `setup-node` have caching built in, making it easy to dramatically reduce workflow run times by reusing previously downloaded dependencies. - -Next, we'll explore [matrix strategies][walkthrough-next] to test across multiple configurations simultaneously. - -## Resources - -- [GitHub Actions Marketplace][actions-marketplace] -- [Caching dependencies to speed up workflows][caching-docs] -- [Playwright CI documentation][playwright-ci] -- [actions/setup-python][setup-python-action] -- [actions/setup-node][setup-node] - -| [← Running Tests][walkthrough-previous] | [Next: Matrix Strategies & Parallel Testing →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[actions-marketplace]: https://github.com/marketplace?type=actions -[caching-docs]: https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows -[marketplace]: https://github.com/marketplace -[playwright-ci]: https://playwright.dev/docs/ci -[setup-node]: https://github.com/actions/setup-node -[setup-python-action]: https://github.com/actions/setup-python -[walkthrough-previous]: 3-running-tests.md -[walkthrough-next]: 5-matrix-strategies.md diff --git a/content/github-actions/5-matrix-strategies.md b/content/github-actions/5-matrix-strategies.md deleted file mode 100644 index de3e1d55..00000000 --- a/content/github-actions/5-matrix-strategies.md +++ /dev/null @@ -1,131 +0,0 @@ -# Matrix Strategies & Parallel Testing - -| [← Caching][walkthrough-previous] | [Next: Deploying to Azure with azd →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -Matrix strategies let you run a job across multiple configurations in parallel — different language versions, operating systems, or test targets. This is powerful for ensuring compatibility and catching environment-specific bugs early in the development cycle. - -## Scenario - -While the goal is to deploy the project to Azure, in the future you may look to host the app on other platforms. As part of the testing, you want to ensure the Python code will run correctly on different versions of the language runtime. This will avoid future surprises. - -## Background - -A [matrix][matrix-docs] allows you to create an array for a workflow to iterate through. This can be various configurations, operating systems, or anything else where you need to have a part of a workflow run multiple times. You define the values for the matrix in an array, then utilize the `matrix` keyword to retrieve the current value. GitHub Actions will handle the looping automatically for you! - -## Add a matrix to the test job - -Let's update the CI workflow to test the API across multiple Python versions. - -1. Open `.github/workflows/run-tests.yml` in your codespace. -2. Locate the `test-api` job. -3. Add a `strategy` block with a `matrix` definition, and update the `python-version` input to reference the matrix value. -4. Replace the existing `test-api` job with the following: - - ```yaml - test-api: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.12', '3.13', '3.14'] - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r app/server/requirements.txt - - - name: Run tests - working-directory: ./app/server - run: | - python -m unittest test_app -v - ``` - -> [!IMPORTANT] -> Make sure to quote version numbers like `'3.12'` in the matrix array. Without quotes, YAML may interpret them as floating-point numbers — for example, `3.10` becomes `3.1`, which would cause the setup step to fail. - -5. In the terminal (Ctl+` to toggle), stage, commit, and push your changes: - - ```bash - git add .github/workflows/run-tests.yml - git commit -m "Add Python version matrix to test-api job" - git push - ``` - -6. Navigate to the **Actions** tab on GitHub. You should see three parallel jobs running — one for each Python version. - -## Understanding matrix behavior - -By default, GitHub Actions uses **fail-fast** mode: if any matrix job fails, all remaining jobs are cancelled. This is efficient but can hide failures in other configurations. - -- **`fail-fast: false`** — continues running all matrix jobs even if one fails. This is valuable when you want to see the full picture of which configurations pass and which don't. -- **`max-parallel`** — limits the number of jobs running concurrently. Useful when you have resource constraints or are hitting rate limits. - -Update the strategy block to disable fail-fast: - -```yaml -strategy: - fail-fast: false - matrix: - python-version: ['3.12', '3.13', '3.14'] -``` - -> [!TIP] -> Setting `fail-fast: false` is particularly useful during initial setup or when debugging, as it provides a complete view of compatibility across all configurations. - -## Using include and exclude - -Matrix strategies support `include` and `exclude` to fine-tune which combinations run. - -- **`include`** adds extra combinations or additional variables to existing combinations. -- **`exclude`** removes specific combinations from the matrix. - -Here's an example that adds an extra combination with an additional environment variable, and excludes a specific one: - -```yaml -strategy: - fail-fast: false - matrix: - python-version: ['3.12', '3.13', '3.14'] - os: [ubuntu-latest, ubuntu-22.04] - exclude: - - python-version: '3.14' - os: ubuntu-22.04 - include: - - python-version: '3.14' - os: ubuntu-latest - experimental: true -``` - -In this example: - -- The `exclude` block skips Python 3.14 on `ubuntu-22.04`. -- The `include` block adds an `experimental` flag to the Python 3.14 / `ubuntu-latest` combination, which you could reference with `${{ matrix.experimental }}` in your steps. - -> [!NOTE] -> You don't need to add this to your workflow right now. This is provided as a reference for more advanced matrix configurations. - -## Summary and next steps - -Matrix strategies let you test across multiple configurations — language versions, operating systems, and more — with minimal YAML duplication. Combined with `fail-fast`, `max-parallel`, `include`, and `exclude`, you have fine-grained control over parallel testing. Next we'll [deploy to Azure using azd][walkthrough-next]. - -## Resources - -- [Using a matrix for your jobs][matrix-docs] -- [Workflow syntax for `jobs..strategy`][strategy-syntax] - -| [← Caching][walkthrough-previous] | [Next: Deploying to Azure with azd →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[matrix-docs]: https://docs.github.com/actions/using-jobs/using-a-matrix-for-your-jobs -[strategy-syntax]: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategy -[walkthrough-previous]: 4-caching.md -[walkthrough-next]: 6-deploy-azure.md diff --git a/content/github-actions/6-deploy-azure.md b/content/github-actions/6-deploy-azure.md deleted file mode 100644 index b5578571..00000000 --- a/content/github-actions/6-deploy-azure.md +++ /dev/null @@ -1,249 +0,0 @@ -# Deploying to Azure with azd - -| [← Matrix Strategies & Parallel Testing][walkthrough-previous] | [Next: Creating custom actions →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -With CI in place, it's time for CD — continuous deployment or continuous delivery. We'll use the [Azure Developer CLI (azd)][azd-docs], Microsoft's recommended tool for deploying to Azure. **azd** handles the heavy lifting: generating infrastructure-as-code (Bicep), configuring passwordless authentication (OIDC), and creating the GitHub Actions workflow. - -## Scenario - -With the prototype built, the shelter is ready to share their application with the world! They want to deploy automatically whenever code is pushed to `main` — but only after CI passes. - -## Background - -### Secrets and variables - -Speaking of secrets and variables... In a prior exercise you utilized `GITHUB_TOKEN`. `GITHUB_TOKEN` is a special secret automatically available to every workflow, and provides access to the current repository. You can add your own secrets and variables to your repository for use in workflows. - -Secrets are exactly that - secret. These are passwords and other values you don't want the public to be able to see. You can add secrets via the CLI, APIs, and your repository's page on github.com. Secrets are write-only, and are only available to be read by a running workflow. In fact, there's even a filter so if the workflow attempts to write or log a secret it'll automatically be hidden. You can confidently add secrets to a public repository, and the only visible aspect will be its name and not the value. - -Variables, on the other hand, are designed to be public values. They're settings like URLs or names, or other values that aren't sensitive. Variables can be both read and written. Use variables whenever you need the ability to configure a value outside a workflow. - -### Protecting production - -There are several strategies for ensuring only validated code reaches production. In a later exercise we'll configure **branch rulesets** to require CI checks and pull request reviews before code can be merged to `main`. Since our deploy workflow only triggers on pushes to `main`, this creates a natural gate: code must pass CI and be reviewed before it can be deployed. - -> [!TIP] -> GitHub also supports **environments** with deployment protection rules (like manual approval gates). Environments are a powerful option when you need separate staging and production deployments — but for this workshop, branch rulesets give us the same safety with less setup. See the [environments documentation][environments-docs] to explore that approach on your own. - -## Install and initialize azd - -Let's set up the Azure Developer CLI and scaffold the infrastructure for our project. - -1. Open the terminal in your codespace (or press Ctl+` to toggle it). -2. Install azd by running: - - ```bash - curl -fsSL https://aka.ms/install-azd.sh | bash - ``` - -3. Log in to Azure: - - ```bash - azd auth login - ``` - - Follow the device code flow — open the URL shown, enter the code, and sign in with your Azure account. - -4. Initialize the project by running: - - ```bash - azd init --from-code - ``` - -5. `azd` will scan your project and detect the client and server services. When prompted, select **Confirm and continue initializing my app** to accept the detected services and generate the project configuration. -6. By default, `azd` generates infrastructure in memory at deploy time. To customize the infrastructure, persist it to disk by running: - - ```bash - azd infra gen - ``` - -7. Explore the generated `infra/` directory. You'll see Bicep files (`.bicep`) that define the Azure resources for your application: - - ```bash - ls infra/ - ``` - -> [!TIP] -> Bicep is Azure's domain-specific language for defining infrastructure as code. If you have GitHub Copilot, try asking it to explain the generated Bicep files! - -The generated `infra/` directory contains several Bicep files that work together: - -- **`main.bicep`** — The entry point. It defines the deployment's parameters (like location and environment name) and orchestrates the other files. -- **`main.parameters.json`** — Default parameter values passed to `main.bicep` at deployment time. -- **`resources.bicep`** — The core of the infrastructure. It defines the Azure Container Apps environment and the individual container apps for the client and server, including their Docker images, environment variables, ingress settings, and scaling rules. -- **`modules/`** — Helper modules referenced by the main files (e.g., for fetching container image metadata). -- **`abbreviations.json`** — A lookup table `azd` uses to generate consistent, short resource names following Azure naming conventions. - -## Configure the infrastructure - -The generated Bicep files define the Azure Container Apps that will host the client and server. We need to add an environment variable so the client knows where to find the API server. - -1. Open `infra/resources.bicep` in your codespace. -2. Find the section (around line 109) that reads: - - ```bicep - { - name: 'PORT' - value: '4321' - } - ``` - -3. Create a new line below the closing `}` and add the following: - - ```bicep - { - name: 'API_SERVER_URL' - value: 'https://${server.outputs.fqdn}' - } - ``` - -> [!NOTE] -> While the syntax resembles JSON, **it's not JSON**. You'll need to resist the natural urge to add commas between the objects! - -## Create the CD workflow - -By default, `azd pipeline config` generates a simple workflow that deploys on every push to `main`. That works for getting started, but we want a workflow that only deploys **after CI passes**. If you create the workflow file *first*, `azd` will detect it and configure credentials around your custom workflow instead of generating the default. - -Let's create a workflow that: -- Only deploys **after CI passes** — using [`workflow_run`][workflow-run-docs] -- Can also be **triggered manually** via `workflow_dispatch` -- Prevents **conflicting deployments** with concurrency controls - -1. Create a new file at `.github/workflows/azure-dev.yml`. -2. Add the following content: - - ```yaml - name: Deploy App - - on: - workflow_dispatch: - workflow_run: - workflows: ["Run Tests"] - branches: [main] - types: [completed] - - permissions: - id-token: write - contents: read - - jobs: - deploy: - runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' - concurrency: - group: deploy-production - cancel-in-progress: false - - steps: - - uses: actions/checkout@v4 - - - name: Install azd - uses: Azure/setup-azd@v2 - - - name: Log in with Azure (Federated Credentials) - run: | - azd auth login \ - --client-id "${{ vars.AZURE_CLIENT_ID }}" \ - --federated-credential-provider "github" \ - --tenant-id "${{ vars.AZURE_TENANT_ID }}" - - - name: Provision and deploy - run: azd up --no-prompt - env: - AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} - AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} - AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - ``` - -3. Save the file. - -Let's walk through the key parts: - -- **`permissions: id-token: write`** — In the [Running Tests][running-tests] module you set `contents: read`. Here, `id-token: write` is added because the workflow needs to request OIDC tokens from Azure. This is how passwordless authentication works — no stored credentials, just short-lived tokens. -- **`vars.*`** — Variables like `${{ vars.AZURE_CLIENT_ID }}` reference **repository variables** that `azd pipeline config` will create for you in the next step. -- **`workflow_run`** triggers this workflow whenever the **Run Tests** workflow completes on `main`. The `if` condition ensures it only proceeds when tests **succeeded** — or when triggered manually via `workflow_dispatch`. -- **`concurrency`** prevents conflicting deployments. Note `cancel-in-progress: false` to avoid accidentally cancelling an active deployment. -- **`azd up`** provisions infrastructure and deploys your application in one command. - -## Set up Azure authentication - -Now let's let `azd` configure the pipeline credentials. Because the workflow file already exists, `azd` will configure OIDC and variables around it rather than generating a new one. - -1. Configure the deployment pipeline: - - ```bash - azd pipeline config - ``` - -2. Follow the prompts — here's what to expect: - - | Prompt | What to select | - |--------|---------------| - | **Select a provider** | Choose **GitHub** | - | **Enter a unique environment name** | Enter a short name (e.g., `-pets-workshop`) — this names your Azure resource group | - | **Select an Azure subscription** | Choose the subscription you want to deploy to | - | **Select an Azure location** | Pick a region close to you (e.g., `eastus2`) | - | **Select how to authenticate the pipeline to Azure** | Choose **Federated Service Principal (SP + OIDC)** | - - After you answer these, `azd` will: - - Create OIDC credentials in Azure for passwordless authentication - - Store the necessary secrets and variables in your repository automatically - - Detect your existing workflow file and configure it - -3. When prompted to commit and push your local changes, say **yes**. - -> [!TIP] -> After `azd pipeline config` completes, navigate to **Settings** > **Secrets and variables** > **Actions** > **Variables** tab to see the repository variables it created (like `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, etc.). These are the `vars.*` values your workflow references. - -## Test the pipeline - -When you said **yes** to `azd pipeline config`'s commit prompt, it pushed your changes — including the workflow file. Let's verify everything is working. - -1. Navigate to the **Actions** tab. The push will trigger the **Run Tests** workflow first. -2. Once tests complete successfully, the **Deploy App** workflow will start automatically (via the `workflow_run` trigger). -3. Watch the deploy job run — it will provision Azure resources and deploy both the client and server applications. -4. Once the deployment completes return to your codespace. -5. Run the following in the terminal to list the details of your new Azure environment: - - ```bash - azd show - ``` - -6. Look for the **client** service endpoint in the output. -7. Open the client URL in your browser — you should see the pet shelter application live! - -## Summary and next steps - -Congratulations! You've deployed the pet shelter application to Azure with a CI/CD pipeline: - -- **CI-gated deployment** — CD only runs after CI passes, using `workflow_run` -- **OIDC authentication** — passwordless, short-lived tokens instead of stored credentials -- **Concurrency controls** — preventing conflicting deployments -- **azd integration** — `azd pipeline config` configured credentials around your custom workflow - -In a later exercise, we'll add **branch rulesets** to ensure code must pass CI and be reviewed before it can reach `main` — creating a natural production gate. - -Next we'll [create custom actions][walkthrough-next] to reduce duplication and make our workflows more maintainable. - -## Resources - -- [What is the Azure Developer CLI?][azd-docs] -- [Create a custom pipeline definition][azd-pipeline-definition] -- [Events that trigger workflows: workflow_run][workflow-run-docs] -- [About security hardening with OpenID Connect][oidc-docs] -- [Deploying with GitHub Actions][actions-deploy] -- [Using environments for deployment][environments-docs] - -| [← Matrix Strategies & Parallel Testing][walkthrough-previous] | [Next: Creating custom actions →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[actions-deploy]: https://docs.github.com/actions/use-cases-and-examples/deploying/deploying-with-github-actions -[azd-docs]: https://learn.microsoft.com/azure/developer/azure-developer-cli/overview -[azd-pipeline-definition]: https://learn.microsoft.com/azure/developer/azure-developer-cli/pipeline-create-definition -[environments-docs]: https://docs.github.com/actions/managing-workflow-runs-and-deployments/managing-deployments/using-environments-for-deployment -[oidc-docs]: https://docs.github.com/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect -[running-tests]: 3-running-tests.md -[workflow-run-docs]: https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run -[walkthrough-previous]: 5-matrix-strategies.md -[walkthrough-next]: 7-custom-actions.md diff --git a/content/github-actions/7-custom-actions.md b/content/github-actions/7-custom-actions.md deleted file mode 100644 index 13025213..00000000 --- a/content/github-actions/7-custom-actions.md +++ /dev/null @@ -1,268 +0,0 @@ -# Creating Custom Actions - -| [← Deploy to Azure][walkthrough-previous] | [Next: Reusable Workflows →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -Custom actions let you encapsulate reusable logic into a single step you can use across workflows. GitHub Actions supports three types of custom actions: **[composite][creating-composite-action]** (combines multiple steps), **[JavaScript][creating-javascript-action]** (runs Node.js code), and **[Docker container][creating-docker-container-action]** (runs in a container). Composite actions are the most approachable and a great starting point for bundling common step patterns. - -In this exercise you'll create a composite action that sets up the Python environment and seeds the test database, then use it in your CI workflow. - -## Scenario - -The pet shelter's test workflows need to seed the database before running tests. This involves setting up Python, installing dependencies, and running `seed_database.py`. Rather than duplicating these steps in every workflow, we'll create a custom composite action that any workflow can reference in a single step. - -## Background - -The great advantage to a composite action is it builds upon the knowledge you already have. You've defined actions already, and a custom action uses a very similar syntax, all defined in YAML. - -Every custom action is defined by an `action.yml` file. This file describes the action's interface and behavior: - -- **`name`**: A human-readable name for the action. -- **`description`**: A short summary of what the action does. -- **`inputs`**: Parameters the caller can pass to the action. -- **`outputs`**: Values the action makes available to subsequent steps. -- **`runs`**: Defines how the action executes. Composite actions use `runs.using: 'composite'` with a list of `steps`. - -Inputs and outputs let the action communicate with the calling workflow, making the action flexible and reusable across different contexts. - -## Create the setup-python-env action - -Let's create a composite action that sets up Python, installs dependencies, and seeds the test database. - -1. In your codespace, open a terminal window by selecting Ctl+\`. -2. Create the directory for the action by executing the following command in the terminal: - - ```bash - mkdir -p .github/actions/setup-python-env - ``` - -3. In the newly created `setup-python-env` folder, create a new file named `action.yml` to store your composite action. -4. Add the following YAML to the file to define your composite action: - - ```yaml - name: 'Setup Python Environment' - description: 'Sets up Python, installs dependencies, and seeds the test database' - - inputs: - python-version: - description: 'Python version to use' - required: false - default: '3.14' - database-path: - description: 'Path to the test database file' - required: false - default: './test_dogshelter.db' - - outputs: - database-file: - description: 'Path to the seeded database file' - value: ${{ steps.set-output.outputs.database-file }} - - runs: - using: 'composite' - steps: - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ inputs.python-version }} - - - name: Install dependencies - run: pip install -r app/server/requirements.txt - shell: bash - - - name: Seed the database - id: seed - run: python app/server/utils/seed_database.py - shell: bash - env: - DATABASE_PATH: ${{ inputs.database-path }} - - - name: Set output - run: echo "database-file=${{ inputs.database-path }}" >> $GITHUB_OUTPUT - shell: bash - id: set-output - ``` - -> [!NOTE] -> Composite action steps must include `shell: bash` for every `run` step — this is required even though it seems redundant. Without it, the workflow will fail with a validation error. - -Review the key parts of the action: -- **Inputs** provide sensible defaults so callers only need to override what's different. -- **Outputs** reference the `set-output` step's output, making the database path available to the calling workflow. -- Each `run` step explicitly declares `shell: bash` as required by composite actions. - -## Use the action in the CI workflow - -Now let's update the CI workflow to use the custom action instead of the individual setup and install steps. We'll also store the test database path as a repository variable — configured once in your repository settings and available to every workflow. - -1. Navigate to your repository on GitHub and go to **Settings** > **Secrets and variables** > **Actions** > **Variables** tab. Select **New repository variable** and create: - - **Name**: `TEST_DATABASE_PATH` - - **Value**: `./test_dogshelter.db` - - This is the same `vars.*` mechanism that `azd pipeline config` used in the [deploy lesson][deploy-azure] for Azure credentials. Repository variables keep configuration out of your workflow files, making them easier to change without a code commit. - -2. Return to your codespace and open `.github/workflows/run-tests.yml`. In the `test-api` job, replace the **Set up Python** and **Install dependencies** steps (lines 23–32) with a single call to the composite action: - - ```yaml - - name: Setup Python environment - id: seed - uses: ./.github/actions/setup-python-env - with: - python-version: ${{ matrix.python-version }} - database-path: ${{ vars.TEST_DATABASE_PATH }} - ``` - -3. Update the **Run tests** step in `test-api` (line 34) to pass the database path from the action's output: - - ```yaml - - name: Run tests - run: python -m unittest test_app -v - working-directory: ./app/server - env: - DATABASE_PATH: ${{ steps.seed.outputs.database-file }} - ``` - -4. The `test-e2e` job has the same **Set up Python** and **Install Python dependencies** steps — a perfect chance to reuse the action. Replace those two steps with the same composite action call (no `python-version` override needed since the action defaults to 3.14): - - ```yaml - - name: Setup Python environment - id: seed - uses: ./.github/actions/setup-python-env - with: - database-path: ${{ vars.TEST_DATABASE_PATH }} - ``` - - Then update the **Run e2e tests** step to pass the database path so the Flask server started by Playwright can find the seeded database: - - ```yaml - - name: Run e2e tests - working-directory: ./app/client - run: npx playwright test - env: - DATABASE_PATH: ${{ steps.seed.outputs.database-file }} - ``` - -5. Here's the complete updated `run-tests.yml` for reference: - - ```yaml - name: Run Tests - - on: - push: - branches: [main] - pull_request: - branches: [main] - - permissions: - contents: read - - jobs: - test-api: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ['3.12', '3.13', '3.14'] - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python environment - id: seed - uses: ./.github/actions/setup-python-env - with: - python-version: ${{ matrix.python-version }} - database-path: ${{ vars.TEST_DATABASE_PATH }} - - - name: Run tests - run: python -m unittest test_app -v - working-directory: ./app/server - env: - DATABASE_PATH: ${{ steps.seed.outputs.database-file }} - - test-e2e: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Setup Python environment - id: seed - uses: ./.github/actions/setup-python-env - with: - database-path: ${{ vars.TEST_DATABASE_PATH }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - cache-dependency-path: 'app/client/package-lock.json' - - - name: Install Node dependencies - working-directory: ./app/client - run: npm ci - - - name: Install Playwright browsers - working-directory: ./app/client - run: npx playwright install --with-deps chromium - - - name: Run e2e tests - working-directory: ./app/client - run: npx playwright test - env: - DATABASE_PATH: ${{ steps.seed.outputs.database-file }} - ``` - -6. In the terminal (Ctl+` to toggle), commit and push your changes: - - ```bash - git add .github/actions/setup-python-env/action.yml .github/workflows/run-tests.yml - git commit -m "Add setup-python-env composite action" - git push - ``` - -7. Navigate to the **Actions** tab on GitHub and verify the workflow runs successfully with the new action. - -> [!TIP] -> When developing custom actions, you can test them by pushing to a branch and triggering a workflow run. Check the workflow logs to ensure each step in your composite action executes as expected. - -## Types of custom actions - -GitHub Actions supports three types of custom actions, each suited to different use cases: - -| Type | Best for | Runs on | Complexity | -|------|----------|---------|------------| -| **Composite** | Bundling multiple existing steps into one | Directly on the runner | Easiest to create | -| **JavaScript** | Complex logic, API calls, or custom computations | Node.js runtime | Moderate | -| **Docker container** | Actions that need specific tools or environments | Inside a container | Most involved | - -- **Composite actions** are ideal when you want to combine several existing steps (like we did with setup, install, and seed) into a single reusable unit. They're the fastest to create because they use the same step syntax you already know. -- **JavaScript actions** are best when you need custom logic, such as making API calls, processing data, or interacting with the GitHub API. They run on Node.js and have access to the `@actions/core` and `@actions/github` packages. -- **Docker container actions** are best when your action requires specific tools, operating system libraries, or a particular runtime environment. They run in a Docker container, giving you full control over the execution environment. - -## Summary and next steps - -Custom actions reduce duplication and make workflows cleaner. You've created a composite action that encapsulates Python setup and database seeding into a single reusable step. Any workflow in the repository can now prepare the Python environment with a single `uses` reference. - -Next, we'll take reusability to the next level by exploring [reusable workflows][walkthrough-next] for sharing entire workflow patterns across your CI/CD pipeline. - -## Resources - -- [Creating a composite action][creating-composite-action] -- [About custom actions][about-custom-actions] -- [Metadata syntax for GitHub Actions][metadata-syntax] -- [GitHub Skills: Reusable workflows][skills-reusable-workflows] - -| [← Deploy to Azure][walkthrough-previous] | [Next: Reusable Workflows →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[about-custom-actions]: https://docs.github.com/actions/sharing-automations/creating-actions/about-custom-actions -[creating-composite-action]: https://docs.github.com/actions/sharing-automations/creating-actions/creating-a-composite-action -[creating-docker-container-action]: https://docs.github.com/actions/sharing-automations/creating-actions/creating-a-docker-container-action -[creating-javascript-action]: https://docs.github.com/actions/sharing-automations/creating-actions/creating-a-javascript-action -[deploy-azure]: 6-deploy-azure.md -[metadata-syntax]: https://docs.github.com/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions -[skills-reusable-workflows]: https://github.com/skills/reusable-workflows -[walkthrough-previous]: 6-deploy-azure.md -[walkthrough-next]: 8-reusable-workflows.md diff --git a/content/github-actions/8-reusable-workflows.md b/content/github-actions/8-reusable-workflows.md deleted file mode 100644 index 4d84be16..00000000 --- a/content/github-actions/8-reusable-workflows.md +++ /dev/null @@ -1,243 +0,0 @@ -# Reusable Workflows - -| [← Creating Custom Actions][walkthrough-previous] | [Next: Required Workflows, Protection & Wrap-Up →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -Reusable workflows let you define an entire workflow that other workflows can call, like a function. This is different from custom actions — actions encapsulate individual *steps*, while reusable workflows encapsulate entire *jobs*. They're triggered with the `workflow_call` event and can accept inputs, secrets, and produce outputs. - -In this exercise you'll extract the deployment pattern into a reusable workflow, then call it from both your CD pipeline and a new manual deployment workflow for rollbacks and hotfixes. - -## Scenario - -The shelter's deploy workflow is working — code that passes CI on `main` gets deployed automatically. But what happens when something goes wrong in production and you need to quickly roll back to a known-good version? Or deploy a hotfix from a specific commit? Right now, the only option is to push to `main` and wait for CI. Let's create a manual deployment workflow that lets the team deploy any git ref on demand, and extract the shared deploy logic into a reusable workflow so both pipelines stay in sync. - -## Background - -In the [previous exercise][walkthrough-previous] you created a composite action to bundle steps together. Reusable workflows solve a similar problem — avoiding duplication — but at a different level. It's important to understand when to reach for each one. - -A **composite action** combines multiple *steps* into a single step that runs inside a job. A **reusable workflow** packages one or more entire *jobs* that a caller workflow references at the job level. Here's a side-by-side comparison: - -| | Composite Action | Reusable Workflow | -|---|---|---| -| **What it encapsulates** | Multiple steps, run as a single step | One or more complete jobs | -| **Where it lives** | `action.yml` in any directory (e.g. `.github/actions/`) | `.github/workflows/` directory only | -| **How it's called** | `uses:` inside a job's `steps` | `uses:` directly on a `job`, not inside steps | -| **Runner control** | Runs on the caller job's runner | Each job specifies its own runner | -| **Secrets** | Cannot access secrets directly | Can receive secrets via `secrets:` or `secrets: inherit` | -| **Logging** | Appears as one collapsed step in the log | Every job and step is logged individually | -| **Nesting depth** | Up to 10 composite actions per workflow | Up to 10 levels of workflow nesting | -| **Marketplace** | Can be published to the [Actions Marketplace][actions-marketplace] | Cannot be published to the Marketplace | - -**When to use which:** - -- Choose a **composite action** when you want to bundle a handful of related steps that run within a single job — like the `setup-python-env` action you just built. -- Choose a **reusable workflow** when you want to share entire job definitions — including runner selection, environment targeting, and concurrency controls — across multiple workflows. Deployment pipelines are a classic use case, which is exactly what we'll build next. - -## Understanding secrets in reusable workflows - -Reusable workflows often need access to secrets and variables — for example, deployment credentials. There are two approaches: - -### Pass all secrets - -Using `secrets: inherit` to forward every secret available in the calling workflow to the reusable workflow. - - ```yaml - deploy: - uses: ./.github/workflows/reusable-deploy.yml - with: - deploy-ref: main - secrets: inherit - ``` - -### Define specific secrets - -For a more controlled approach, you can identify which specific secrets to pass in the reusable workflow's `on.workflow_call.secrets` section: - -```yaml -on: - workflow_call: - inputs: - deploy-ref: - required: false - type: string - secrets: - AZURE_CLIENT_ID: - required: true - AZURE_TENANT_ID: - required: true - AZURE_SUBSCRIPTION_ID: - required: true -``` - -Then caller then passes each secret explicitly: - -```yaml -deploy: - uses: ./.github/workflows/reusable-deploy.yml - with: - deploy-ref: main - secrets: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} -``` - -> [!IMPORTANT] -> For deployment workflows that need Azure credentials, `secrets: inherit` is the simplest approach. However, defining specific secrets provides better documentation and prevents accidentally exposing secrets the reusable workflow doesn't need. We'll use `secrets: inherit` in this exercise for simplicity. - -## Create a reusable deployment workflow - -Let's extract the shared deploy steps into a reusable workflow. The workflow will accept an optional `deploy-ref` input so callers can deploy any git ref — the current commit, a previous release tag, or a specific commit SHA. - -1. In your codespace, create a new file at `.github/workflows/reusable-deploy.yml`. - -2. Define the `workflow_call` trigger with an input for the git ref to deploy: - - ```yaml - name: Reusable Deploy Workflow - - on: - workflow_call: - inputs: - deploy-ref: - description: 'Git ref to deploy (commit SHA, tag, or branch). Defaults to the caller workflow ref.' - required: false - type: string - default: '' - ``` - -3. Add a single job that checks out the code, authenticates with Azure, and deploys: - - ```yaml - jobs: - deploy: - runs-on: ubuntu-latest - concurrency: - group: deploy-production - cancel-in-progress: false - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ inputs.deploy-ref || github.sha }} - - - name: Install azd - uses: Azure/setup-azd@v2 - - - name: Log in with Azure (Federated Credentials) - run: | - azd auth login \ - --client-id "${{ vars.AZURE_CLIENT_ID }}" \ - --federated-credential-provider "github" \ - --tenant-id "${{ vars.AZURE_TENANT_ID }}" - - - name: Deploy application - run: azd up --no-prompt - env: - AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} - AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} - AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} - ``` - -> [!NOTE] -> Reusable workflows have a few important limitations: they can be nested up to 4 levels deep, and the workflow file must be located in the `.github/workflows` directory. You also cannot call a reusable workflow from within a reusable workflow's `steps` — they are called at the job level. - -## Update the CD workflow - -Now update your `azure-dev.yml` to call the reusable workflow instead of defining the deploy steps inline. - -1. Replace the contents of `.github/workflows/azure-dev.yml` with: - - ```yaml - name: Deploy App - - on: - workflow_dispatch: - workflow_run: - workflows: ["Run Tests"] - branches: [main] - types: [completed] - - permissions: - id-token: write - contents: read - - jobs: - deploy: - if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' - uses: ./.github/workflows/reusable-deploy.yml - secrets: inherit - ``` - - Notice how the entire job definition is replaced by a single `uses:` reference. The reusable workflow handles checkout, authentication, and deployment — the caller just decides *when* to deploy. - -## Create a manual deploy workflow - -Now let's add the second caller — a manual deploy workflow for rollbacks and hotfixes. This is where the reusable workflow really earns its keep: same deploy logic, different trigger. - -1. Create a new file at `.github/workflows/manual-deploy.yml`. -2. Add the following content: - - ```yaml - name: Manual Deploy - - on: - workflow_dispatch: - inputs: - deploy-ref: - description: 'Git ref to deploy (commit SHA, tag, or branch)' - required: true - default: 'main' - - permissions: - id-token: write - contents: read - - jobs: - deploy: - uses: ./.github/workflows/reusable-deploy.yml - with: - deploy-ref: ${{ inputs.deploy-ref }} - secrets: inherit - ``` - - This workflow is only triggered **manually** via `workflow_dispatch` — it appears as a "Run workflow" button in the Actions tab. It prompts for a **git ref** (a commit SHA, tag, or branch name to deploy), passes that ref to the reusable workflow's `deploy-ref` input, and uses the same deploy logic as the automated pipeline. - -3. In the terminal (Ctl+` to toggle), commit and push your changes: - - ```bash - git add .github/workflows/reusable-deploy.yml .github/workflows/azure-dev.yml .github/workflows/manual-deploy.yml - git commit -m "Extract reusable deploy workflow and add manual deploy" - git push - ``` - -4. Navigate to the **Actions** tab on GitHub and verify that the deploy workflow runs successfully. You should also see **Manual Deploy** in the workflow list — try clicking **Run workflow** to test deploying a specific ref. - -> [!TIP] -> When viewing a workflow run that calls reusable workflows, GitHub shows each caller job separately. Select a job to see the steps from the reusable workflow running inside it. - -This pattern keeps your deployment logic in one place. When you need to update the deployment process — like adding health checks or notifications — you change it once in the reusable workflow and every caller benefits. - -## Summary and next steps - -Reusable workflows reduce duplication at the workflow level. You've extracted the shared deployment pattern into a template that both the automated CD pipeline and the manual deploy workflow call with a single `uses` reference. This keeps your deployment process maintainable as it grows — any change happens in one place. - -Next, we'll ensure quality gates are enforced with [branch protection, required workflows, and more][walkthrough-next]. - -## Resources - -- [Reusing workflows][reusing-workflows] -- [The `workflow_call` event][workflow-call-event] -- [Sharing workflows with your organization][sharing-workflows] -- [GitHub Skills: Reusable workflows][skills-reusable-workflows] - -| [← Creating Custom Actions][walkthrough-previous] | [Next: Required Workflows, Protection & Wrap-Up →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[actions-marketplace]: https://github.com/marketplace?type=actions -[reusing-workflows]: https://docs.github.com/actions/sharing-automations/reusing-workflows -[sharing-workflows]: https://docs.github.com/actions/sharing-automations/sharing-workflows-secrets-and-runners-with-your-organization -[skills-reusable-workflows]: https://github.com/skills/reusable-workflows -[workflow-call-event]: https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_call -[walkthrough-previous]: 7-custom-actions.md -[walkthrough-next]: 9-required-workflows.md diff --git a/content/github-actions/9-required-workflows.md b/content/github-actions/9-required-workflows.md deleted file mode 100644 index b1100bc2..00000000 --- a/content/github-actions/9-required-workflows.md +++ /dev/null @@ -1,176 +0,0 @@ -# Rulesets, Required Workflows & Wrap-Up - -| [← Reusable Workflows][walkthrough-previous] | [Next: GitHub Actions section overview →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -Building a CI/CD pipeline is only half the battle — you also need to enforce it. Repository rulesets ensure that code can't be merged without passing checks and getting reviewed. Required workflows go further, allowing organizations to mandate specific workflows across all repositories. In this final exercise, you'll configure a ruleset on `main`, explore required workflows, and wrap up the workshop. - -## Scenario - -The shelter's CI/CD pipeline is comprehensive, but nothing currently prevents someone from merging code without passing CI — meaning untested code could reach `main` and trigger a deployment. The organization also wants to ensure all repositories run security scanning. Let's lock things down with rulesets and explore how required workflows enforce standards at scale. - -## Background - -### Repository rulesets - -[Rulesets][about-rulesets] are GitHub's recommended approach for enforcing rules on branches and tags. They offer flexibility and visibility that legacy branch protection rules don't: - -- **Layering** — Multiple rulesets can apply to the same branch; the most restrictive rule wins. -- **Status management** — Toggle between **Active** and **Disabled** without losing configuration. -- **Visibility** — Anyone with read access can see active rulesets (not just admins). -- **Bypass permissions** — Granular bypass for specific roles, teams, or GitHub Apps. -- **Scope** — Repository-level or organization-wide (on GitHub Enterprise). -- **Required workflows** — Rulesets can require specific workflows to pass before merging. - -Rulesets are available on all GitHub plans for public repositories, and on GitHub Pro, Team, and Enterprise plans for private repositories. - -### Required workflows - -One of the most powerful ruleset features is the ability to **require specific workflows to pass before merging**. This is particularly useful at the organization level: - -- An organization creates a reusable workflow (e.g. `security-scan.yml`) in a central repository. -- An organization-wide ruleset requires that workflow for all (or a subset of) repositories. -- Every PR across those repositories now runs the required workflow automatically — individual repository owners can't skip it. - -Common use cases include security scanning, license compliance, and code quality checks. - -## Add a summary job to the CI workflow - -Right now we have two sets of tests - end to end tests with Playwright, and unit tests with Python. The latter is actually setup using a matrix, where we run the tests against different versions of Python. As time goes on, the list of tests may grow and change. We want to ensure we can easily indicate that **all** tests have passed in one, centralized report. This will allow us to then use this as our flag when creating a gate, to ensure our CI has completed successfully before allowing a merge into `main`. We'll do this by adding a new job to the end of our tests workflow, which will check if all jobs in the workflow have succeeded. - -1. Open `.github/workflows/run-tests.yml` and add the following job at the end of the `jobs:` section (after the `test-e2e` job): - - ```yaml - tests-passed: - if: always() - needs: [test-api, test-e2e] - runs-on: ubuntu-latest - steps: - - name: Check results - run: | - if [[ "${{ needs.test-api.result }}" != "success" || "${{ needs.test-e2e.result }}" != "success" ]]; then - echo "One or more jobs failed" - exit 1 - fi - ``` - -2. Commit and push the change: - - ```bash - git add .github/workflows/run-tests.yml - git commit -m "Add tests-passed summary job" - git push - ``` - -The `if: always()` ensures this job runs even when upstream jobs fail, so it can correctly report failure. The `needs` key creates a dependency on both test jobs, and the step checks their results. - -## Create a ruleset for `main` - -Let's create a ruleset that requires our tests to pass, and pull requests to be reviewed, before merging to `main`. - -1. Navigate to your repository on GitHub. -2. Select **Settings**, then in the left sidebar under **Code and automation**, expand **Rules** and select **Rulesets**. -3. Select **New ruleset** > **New branch ruleset**. -4. Under **Ruleset name**, enter `main-gate`. -5. Set the **Enforcement status** to **Active**. -6. Under **Target branches**, select **Add target** > **Include default branch**. This targets `main`. -7. Under **Branch rules**, enable the following rules: - - | Rule | Configuration | - |------|--------------| - | **Require a pull request before merging** | Set **Required approvals** to `1` | - | **Require status checks to pass** | Check **Require branches to be up to date before merging**, then add `tests-passed` as a required check | - | **Block force pushes** | *(enabled by default)* | - -8. Select **Create** to save the ruleset. - -> [!TIP] -> If your status checks don't appear when searching, make sure the CI workflow has run at least once on the repository. GitHub only shows status checks that have been reported previously. - -> [!NOTE] -> You can start a ruleset in **Disabled** mode to test it before enforcing. This lets you preview which PRs would be blocked without actually blocking anyone. - -## Test the ruleset - -Let's verify the ruleset is working. - -1. Return to your codespace and open the terminal (Ctl+` to toggle). Create a new branch and make a small change: - - ```bash - git checkout -b test-ruleset - echo "# test change" >> app/server/app.py - git add app/server/app.py - git commit -m "Test ruleset enforcement" - git push -u origin test-ruleset - ``` - -2. Navigate to your repository on GitHub and create a pull request from `test-ruleset` to `main`. -3. Observe that the **Merge pull request** button is disabled — the required status checks must pass and the PR needs an approving review. -4. Watch the CI workflow run. Even after all checks pass, the merge button remains disabled until the review requirement is satisfied. -5. You can close the pull request — the important thing is that the ruleset is enforced! - -> [!IMPORTANT] -> Rulesets ensure your CI pipeline isn't just a suggestion — it's a requirement. Code cannot reach `main` without passing the checks and reviews you've defined. Since your deploy workflow only triggers on pushes to `main`, this means only validated, reviewed code gets deployed. - -## Organizational required workflows - -Organization-wide rulesets can mandate that specific workflows run across all repositories. This pairs naturally with the reusable workflows you built in the [previous exercise](8-reusable-workflows.md) — an organization could create a reusable security-scanning workflow in a central `.github` repository, then enforce it via a ruleset so every PR across the organization runs it automatically. - -> [!NOTE] -> Organization-wide rulesets are available on GitHub Team and GitHub Enterprise plans. For personal repositories on the Free plan, repository-level rulesets (as configured above) provide similar enforcement at the repo level. - -## Advanced features to explore - -Here are some additional GitHub Actions features you can explore on your own: - -- **Service containers**: Spin up databases, caches, or other services alongside your test jobs. Define them under `services` in a job, and GitHub Actions handles the lifecycle for you. -- **Job summaries**: Write Markdown to the `$GITHUB_STEP_SUMMARY` environment file to create rich, formatted output that appears on the workflow run summary page. -- **Self-hosted runners**: Run workflows on your own infrastructure for specialized hardware needs, compliance requirements, or to stay within your network. Useful when you need GPUs, specific OS versions, or access to internal resources. -- **Larger runners**: GitHub-hosted runners with more CPU and memory (up to 96-core x64 and 64-core ARM), available on Team and Enterprise plans. Swap `runs-on: ubuntu-latest` for a larger runner label when your builds or tests need more compute. See the [larger runners documentation][larger-runners]. -- **`repository_dispatch`**: Trigger workflows from external events via the GitHub API. This is useful for integrating GitHub Actions with external systems like monitoring tools, chatbots, or other CI/CD platforms. - -## Wrap-up and congratulations - -Congratulations! You've built a complete CI/CD pipeline for the pet shelter application. Let's review what you've accomplished: - -- **Continuous integration**: Tests run on every push and pull request across multiple Python versions, catching bugs before they reach `main`. -- **Continuous deployment**: Automated deployment to Azure via `azd`, triggered after CI passes on `main`. -- **Custom actions**: Encapsulated Python setup and database seeding into a reusable composite action, eliminating duplication across jobs. -- **Reusable workflows**: Extracted the deployment pattern into a callable workflow template, shared by both the automated CD pipeline and a manual deploy workflow for rollbacks. -- **Manual deployment**: Added on-demand deployment capability for rollbacks and hotfixes, using `workflow_dispatch` with a git ref input. -- **Rulesets**: Enforced quality gates so code can't be merged without passing CI checks and peer review — the production safeguard that ensures only validated code gets deployed. - -This pipeline follows the same patterns used by teams across GitHub. As the shelter's application grows, this foundation will scale with it. - -### Continue learning - -If you want to keep exploring, here are some suggested next steps: - -- Add a code scanning workflow using [GitHub Advanced Security][github-security]. -- Explore [GitHub Environments][environments-docs] with deployment protection rules for staged deployments (e.g., staging → production with manual approval). -- Explore the [GitHub Actions Marketplace][actions-marketplace] for community-built actions. -- Take the [GitHub Skills: Deploy to Azure][skills-deploy-azure] course for a deeper dive into Azure deployment. - -## Resources - -- [About rulesets][about-rulesets] -- [Creating rulesets for a repository][creating-rulesets] -- [Available rules for rulesets][available-rules] -- [The `workflow_dispatch` event][workflow-dispatch] -- [GitHub Skills: Deploy to Azure][skills-deploy-azure] -- [GitHub Actions Marketplace][actions-marketplace] - -| [← Reusable Workflows][walkthrough-previous] | [Next: GitHub Actions section overview →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[about-rulesets]: https://docs.github.com/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets -[actions-marketplace]: https://github.com/marketplace?type=actions -[available-rules]: https://docs.github.com/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets -[creating-rulesets]: https://docs.github.com/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/creating-rulesets-for-a-repository -[environments-docs]: https://docs.github.com/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment -[github-security]: https://github.com/features/security -[larger-runners]: https://docs.github.com/actions/using-github-hosted-runners/using-larger-runners -[skills-deploy-azure]: https://github.com/skills/deploy-to-azure -[workflow-dispatch]: https://docs.github.com/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch -[walkthrough-previous]: 8-reusable-workflows.md -[walkthrough-next]: README.md diff --git a/content/github-actions/README.md b/content/github-actions/README.md deleted file mode 100644 index f05541c7..00000000 --- a/content/github-actions/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# GitHub Actions: From CI to CD - -| [← Pets workshop selection][walkthrough-previous] | [Next: Workshop Setup →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[GitHub Actions][github-actions] is a powerful automation platform available right in your GitHub repository. With Actions you can build, test, and deploy your code — and automate just about anything else in your software development lifecycle. This workshop walks you through building a complete CI/CD pipeline, starting with running tests on every push and ending with automated deployment to Azure. - -## Scenario - -You're a developer, volunteering for a pet adoption shelter. They have a [Flask][flask] API and an [Astro][astro] frontend. They're ready to productionize their app, and deploy it to the cloud! But they also know there's some processes that should be followed to ensure everything flows smoothly. The goal is to work to automate all of those - through the use of GitHub Actions! - -## Prerequisites - -To complete this workshop, you will need the following: - -- A [GitHub account][github-signup] -- An [Azure subscription][azure-free] (for the deployment exercises) -- Familiarity with Git basics (commit, push, pull) - -> [!NOTE] -> If you have access to [GitHub Copilot][github-copilot], it can help you write workflow YAML files. You'll see tips throughout the exercises on how to use it effectively. - -## Exercises - -0. [Workshop Setup][setup] — Create your repository from the template -1. [Introduction & Your First Workflow][introduction] — Create your first workflow and explore the Actions UI -2. [Securing the Development Pipeline][code-scanning] — Enable code scanning, Dependabot, and secret scanning -3. [Running Tests][ci] — Automate unit and e2e testing with parallel jobs -4. [Caching][marketplace] — Speed up workflows by caching dependencies -5. [Matrix strategies & parallel testing][matrix] — Test across multiple configurations simultaneously -6. [Deploying to Azure with azd][deployment] — Set up continuous deployment to Azure -7. [Creating custom actions][custom-actions] — Build your own reusable action -8. [Reusable workflows][reusable-workflows] — Share workflow logic across repositories -9. [Required workflows, protection & wrap-up][protection] — Enforce standards and protect your branches - -## Resources - -- [GitHub Actions documentation][github-actions-docs] -- [GitHub Actions Marketplace][actions-marketplace] -- [Workflow syntax reference][workflow-syntax] -- [Azure Developer CLI (azd) documentation][azd-docs] - -| [← Pets workshop selection][walkthrough-previous] | [Next: Workshop Setup →][walkthrough-next] | -|:-----------------------------------|------------------------------------------:| - -[actions-marketplace]: https://github.com/marketplace?type=actions -[astro]: https://astro.build/ -[azure-free]: https://azure.microsoft.com/free/ -[azd-docs]: https://learn.microsoft.com/azure/developer/azure-developer-cli/overview -[ci]: ./3-running-tests.md -[code-scanning]: ./2-code-scanning.md -[custom-actions]: ./7-custom-actions.md -[deployment]: ./6-deploy-azure.md -[flask]: https://flask.palletsprojects.com/ -[github-actions]: https://github.com/features/actions -[github-actions-docs]: https://docs.github.com/actions -[github-copilot]: https://github.com/features/copilot -[github-signup]: https://github.com/join -[introduction]: ./1-introduction.md -[marketplace]: ./4-caching.md -[matrix]: ./5-matrix-strategies.md -[protection]: ./9-required-workflows.md -[repo-root]: / -[reusable-workflows]: ./8-reusable-workflows.md -[setup]: ./0-setup.md -[walkthrough-next]: ./0-setup.md -[walkthrough-previous]: ../README.md -[workflow-syntax]: https://docs.github.com/actions/writing-workflows/workflow-syntax-for-github-actions diff --git a/content/how-github-uses-github.md b/content/how-github-uses-github.md deleted file mode 100644 index d3bf9bbb..00000000 --- a/content/how-github-uses-github.md +++ /dev/null @@ -1,177 +0,0 @@ -# How GitHub uses GitHub - -GitHub is built on GitHub. We use our own tools to create new tools, features and products. This has driven us to take a developer-first approach to everything we do, to ensure our products aid productivity and the development lifecycle. - -As your organization is getting started with GitHub, and potentially DevOps, it's best to start with a good foundation. It's best to adapt and modify for your organization's specific needs once you have established a base. While some of this might be review, the goal is to provide insights into how to manage your processes and practices by exploring how we at GitHub use GitHub. - -## Core DevOps - -DevOps is "the union of people, processes, and products to enable the continuous delivery of value to our end users." The goal is to build the right things the right way for the right reasons. - -A typically DevOps flow is broken into five main stages: - -1. Plan: Determine what is to be built, by whom, and on what timeline. -2. Develop: Build the features to be built. -3. Collaborate: Review the newly built features. -4. Deliver: Release the newly built features. -5. Operate: Monitor the newly built features and identify new features to be built. - -GitHub offers solutions for all stages of the DevOps lifecycle, with tools to integrate and streamline the process, and make it continuous. - -## Breaking down the DevOps lifecycle - -### Plan - -In GitHub, new features typically start with a [discussion](https://docs.github.com/discussions). Discussions are a great place for open conversations about new features, what should be built, and why. More importantly, they provide an archive of the decisions made and how they were reached. We have a saying at GitHub: "URL or it didn't happen." Having robust discussions documented allows teams to learn from the past and hopefully avoid repeating a mistake. - -From there, [issues](https://docs.github.com/issues/tracking-your-work-with-issues/about-issues) are created. Each issue identifies what's to be build and by whom. They are typically linked to a [branch](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches) and eventually a [pull request](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). - -Issues are planned and managed in [projects](https://docs.github.com/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects). Projects allow teams to identify backlogs and schedules, and to track progress. When doing daily stand-ups, a best practice is to "walk the board" to identify the current state of each issue. - -### Develop - -Every popular IDE has plugins for GitHub, allowing developers to quickly [clone repositories](https://docs.github.com/repositories/creating-and-managing-repositories/cloning-a-repository), [pull](https://github.com/git-guides/git-pull) and [push](https://github.com/git-guides/git-push) updates to code. - -However, the development process isn't limited to solely writing and managing code. Developers need to install various packages and ensure a consistent environment to avoid "it runs on my machine" situations. [GitHub Codespaces](https://docs.github.com/codespaces/overview) provides teams the ability to define a container which runs in the cloud for developers to use. Devs can connect to the container using a browser-based version of Visual Studio Code, the desktop version of Visual Studio Code, Visual Studio and JetBrains. - -> GitHub developers use GitHub Codespaces regularly. Through the use of [prebuilds](https://docs.github.com/codespaces/prebuilding-your-codespaces/about-github-codespaces-prebuilds) the GitHub.com monolith spins up in 30 seconds - -### Collaborate - -The [pull request (PR)](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) is part of the way of life at GitHub. Everyone is encourage to not just file issues or suggest changes for their own team's projects, but for projects across the enterprise. The PR is the center of this philosophy. - -When a PR is filed teams are encouraged to [review the updates and provide feedback](https://docs.github.com/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews). You can create PR templates to ensure the proper information is provided with a PR, such as a link to an issue or steps which need to be followed. [Status checks](https://docs.github.com/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks) can be defined to ensure tests pass and other quality metrics are met before changes are merged into your codebase. - -### Deliver - -CI/CD is a popular buzzword in DevOps. CI/CD is focused around continually updating and improving your products. Continuous Integration (CI) is focused on bringing updates into your codebase while Continuous Deployment targets deploying new features. The key to a successful CI/CD implementation is validation and automation. - -Validation is part of the PR lifecycle, and is managed by the status checks defined. Automation ensures the processes required for status checks, deployments and other tasks are executed without developer intervention. When a developer needs to open a different tool or manually run a process it takes them out of the flow, meaning lost productivity and the possibility tasks will be skipped. - -[GitHub Actions](https://docs.github.com/actions) allows you to define workflows which execute in response to various triggers, including a PR being created, code being committed, or an issue being filed. GitHub Actions can run tests, perform different operations, and deploy your code. - -### Operate - -Once a feature is deployed, your team will use various tools to monitor your applications to detected problems. You can use [GitHub's APIs](https://docs.github.com/rest) to create issues should problems arise. Additionally, GitHub Actions can run in response to webhooks, allowing you to flag bugs or identify failed deployments. - -## Lessons from the Open Source community - -Most Open Source Software (OSS) OSS projects are maintained by a set of contributors with various backgrounds and skill-levels, and are geographically distributed. Many contributions come from people who view OSS as a side project. The practices developed by the OSS community to support contributors can be applied to internal development as well. This process is typically referred to as InnerSource. - -While a full conversation about InnerSource is beyond the scope of this session, InnerSource places an emphasis on: - -- asynchronous communication as the default. -- filing PRs for changes for all projects. -- filing PRs early and often, keeping PRs small. -- searching for code to reuse before creating new code. -- making repositories public by default. -- encouraging collaboration across teams. - -GitHub follows these practices internally. This helps avoid group-think, ensures developers have the flexibility to contribute on a schedule which works best for them, and offers everyone the opportunity to contribute. - -## Getting started with GitHub Enterprise - -GitHub Enterprise includes the core features of GitHub like repositories and issues, and a suite of other tools for companies and enterprises. This is the kickoff of a series of engagements where we will explore GitHub. As a result, our focus for today will be your organization's first steps with GitHub. - -### Managing users and permissions - -As you might expect, to access GitHub a user needs an account. There are two main ways to create and manage accounts on GitHub: SAML single sign-on (SSO) and Enterprise Managed Users (EMUs). With SSO accounts, users use their normal GitHub accounts which are then linked to a company account using an identity provider (IdP) such as Okta or Azure Active Directory (AAD). Enterprise Managed Accounts are new accounts created for users tied directly to their company accounts. - -EMUs have [several restrictions](https://docs.github.com/enterprise-cloud@latest/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users#abilities-and-restrictions-of-managed-user-accounts), such as not being able to contribute to projects outside of their enterprise. However EMUs offer greater security by creating a walled garden for your GitHub Enterprise (GHE) cloud and tying permissions directly to your users' company accounts. The choice between SSO and EMU depends on the sensitivity of the projects you're creating and the desire for users to contribute to projects outside of your company. - -Applying permissions to individual users is leads to increased management overhead. GitHub allows you to group together users through the use of [teams](https://docs.github.com/organizations/organizing-members-into-teams/about-teams). A team is a group of users, and can be used to provide access to resources. - -Access to repositories is managed through [roles](https://docs.github.com/organizations/managing-access-to-your-organizations-repositories/repository-roles-for-an-organization). These roles range from Read, which allows users only to read the contents of a repository, to Admin, which can perform all actions on a repository including deleting it! When using GitHub Enterprise you also have the option to create [custom roles](https://docs.github.com/enterprise-cloud@latest/organizations/managing-peoples-access-to-your-organization-with-roles/managing-custom-repository-roles-for-an-organization) depending on the level of permissions required. - -#### How GitHub manages accounts - -- Multi-factor authentication (MFA or 2FA) is **required** for all accounts. -- Use teams rather than granting permissions to individual accounts. -- GitHub uses SSO for most projects, while deploying EMUs for ones of greater sensitivity. -- Apply the principle of [least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege). - -### Repositories - -A [repository](https://docs.github.com/repositories/creating-and-managing-repositories/about-repositories) contains the files, metadata and history of a project. More than just a location for files, repositories have [issues](https://docs.github.com/issues/tracking-your-work-with-issues/about-issues) and [projects](https://docs.github.com/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects) for managing work items and documenting changes, [discussions](https://docs.github.com/discussions) for providing commentary about the direction of the project, and all versions of all files both past and present. - -> There's a saying inside GitHub, "URL or it didn't happen." Having a robust history in issues, discussions and PRs allows teams to see where a project was and where it's going. It serves as a guide of what's been tried, what hasn't been tried, and why those decisions were made. The answers provided by this archive can help ensure past mistakes aren't repeated and an understanding of how a project arrived at its current state. - -#### How GitHub manages repositories - -- As a general rule, mono-repositories should be avoided. Large repositories become unwieldy to manage over time. -- Because permissions can only be granted to a repository and not individual folders or files, repositories should be created based around security requirements. -- Defaulting a repository to being available to your entire organization rather than an individual team encourages discovery, collaboration and code reuse. -- Automate as many tasks as possible. - -> There are numerous ways in which repositories can be structured, and there isn't necessarily one "right way". Different teams will use different techniques based on what they're building, their team's culture and approach, and how projects will be deployed. - -### Project management in GitHub - -> A full conversation about project management is beyond the scope of this workshop. This section is designed to serve as an introduction to the functionality available in GitHub. How your organization chooses to use the features will vary. - -[GitHub Projects](https://docs.github.com/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects) allows teams to track, manage and plan projects. You can identify sprints, manage the backlog and status of issues, assign work, and view reports. - -Projects can span multiple repositories. As a result, you can manage projects at a higher level, and structure your repositories to best support your team and what is being built. GitHub Projects are lightweight, offering you the flexibility to use them as your team sees fit. - -Issues are created in a repository. You can create [issue templates](https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository) to provide structure to issues and ensure the required information is documented. - -Issues can be tagged with [labels](https://docs.github.com/issues/using-labels-and-milestones-to-track-work/managing-labels), which allows for ad-hoc grouping. Labels can be used to identify features, projects, or other categorizations. Labels can then be used for quick filtering and searching, and to create views. Because search criteria is part of the query string, you can bookmark and share filtered views. - -#### How GitHub uses Projects to manage projects - -- Use the README to document the project. -- Create an issue for every suggested change to a repository. This provides a better history and enables project management. -- Encourage all developers and other contributors to provide as much information as possible in an issue. There's no such thing as "too much documentation." -- Have a single source of truth. If you are using other tools for project management, settle on one. -- Automate projects and issue management. Automation can apply labels, manage states, or transition issues. -- Use labels to tag not just the type of work being done but other metadata such as, "Good first issue", "Documentation", "Urgent", and "Nice to have". This helps developers from other teams understand what issues need attention or would be good first items to work on. - -### Forks and branches - -A [branch](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches) is a version of a codebase. All repositories have one default branch, typically called **main**. New branches are created as new versions are required to introduce and test changes before merging them back into another branch or into the default branch. Branches exist in the repository in which they are created. - -A [fork](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) is a point-in-time copy of an entire repository into a different account or organization, such as the developer's user account. By creating a fork a developer or set of developers are able to make changes without impacting the original source code. Changes can be merged back into the original repository through the use of a pull request. - -#### How GitHub uses forks and branches - -- Always start by creating a branch or a fork; never edit or push directly to main. -- Forks are great for individual exploration or inviting external collaborators. -- Branches are great for collaboration and exploration by the current project team. - -### Pull requests - -A pull request (PR) is a request for someone to pull your changes into a repository. The PR is the core to collaboration in GitHub. The normal workflow for modifying content of a repository is as follows: - -1. Create an issue to document the desired changes you wish to see. -2. Create a fork or branch to use for adding or modifying the necessary code. -3. Create a pull request from the fork or branch into the original branch. -4. Request code reviews, and make updates as needed. -5. Automated tests, security checks and other validators run as part of the [Actions](https://docs.github.com/actions) you associate with the repository. -6. Once all checks are completed and everyone signs off on the changes, the pull request is merged into the original repository. - -The PR acts as a complete archive of the change. It includes the changes made, feedback provided by reviewers, and the results of validation and other automated jobs. PRs provide a history of all changes, and offer teams the ability to determine when and why changes were made. They also streamline reverting back to previous versions when necessary. - -#### How GitHub uses pull requests - -- Your PR history provides a great archive of your project. -- PR the change you want to see! If a package or project doesn't behave the way it should, create a PR with the necessary updates. -- Keep PRs small and frequent. It's far easier to manage and merge in smaller PRs than larger ones. Larger PRs are also susceptible to scope creep. -- It's OK not to accept a PR. Sometimes the goal of a PR is to explore alternate routes. Sometimes the changes requested aren't appropriate for the project. -- Automate as much of your process as possible with [GitHub Actions](https://docs.github.com/actions). - -### Protected branches - -Submitting a PR typically has a set of requirements which must be met before the code can be merged. Enforcing these rules is handled through a [protected branch](https://docs.github.com/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/about-protected-branches). A protected branch can identify several requirements, including at least one reviewer has signed-off on the changes, specific validations have run, and if the rules apply to administrators. - -> GitHub uses protected branches internally for the main or central branch of almost every repository. - -#### How GitHub uses protected branches - -- Almost every repository's central or main branch is protected. -- Rules such as code coverage, tests passing, and other validations are implemented through protected branches and GitHub Actions. - -## Next steps - -The best time to get started with the new tools available to you is now! Begin by creating and configuring accounts for your users. Create a project to manage the development process. Determine the structure for your repositories and begin creating them. Define the rules for PRs and configure protected branches. - -As your team begins using GitHub you'll learn more about what's available and have more questions. This is the first of a series of onboarding events to help you get the most out of GitHub and improve the productivity of your developers. You can reach out to your representative with questions, and join us for our next event to dig deeper into everything GitHub has to offer. diff --git a/content/prompts/README.md b/content/prompts/README.md deleted file mode 100644 index 83f3db05..00000000 --- a/content/prompts/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Pets Workshop Prompts - -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. - -## Prompt Overview - -### Interface and User Experience - -- **[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. - -- **[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. - -### Backend Development - -- **[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. - -- **[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. diff --git a/content/prompts/conversion-convert-flask-to-golang.md b/content/prompts/conversion-convert-flask-to-golang.md deleted file mode 100644 index c8b3c9d6..00000000 --- a/content/prompts/conversion-convert-flask-to-golang.md +++ /dev/null @@ -1,24 +0,0 @@ -# Flask to Go Server Migration Project - -## Objective - -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. - -The Python Flask is stored in #folder:server - -## Requirements -1. Create a functionally equivalent Go server implementation -2. Match all existing API endpoints, query parameters, and HTTP methods -3. Preserve all current data processing logic and response formats -4. Implement the same error handling and status codes -5. Maintain any authentication mechanisms present in the Flask implementation -6. Use only the Go standard library where possible, with minimal external dependencies -7. Include appropriate comments explaining the code and any implementation decisions - -## Deliverables -1. Complete Go source code organized in a folder named `go_server` -2. A main.go file with server initialization and configuration -3. Separate handler files for different API endpoint groups -4. Any utility or helper functions required -5. A README.md with setup and usage instructions - diff --git a/content/prompts/fun-add-dog-animation.md b/content/prompts/fun-add-dog-animation.md deleted file mode 100644 index 43852a00..00000000 --- a/content/prompts/fun-add-dog-animation.md +++ /dev/null @@ -1,13 +0,0 @@ -# Puppy Cursor Follower - -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. - -## Requirements: -- The dog should be cute and cartoony with expressive eyes -- Eyes should smoothly track the cursor position across the entire screen -- Position the dog in the bottom-right corner as a fixed element (sticky positioning) -- Dog should remain visible even when the page is scrolled -- Add a slight head tilt or ear wiggle on mouse clicks for extra charm -- Optional: Make the dog occasionally blink or perform a random animation - -Let's make browsing fun again with this interactive canine companion! 🐶 \ No newline at end of file diff --git a/content/prompts/fun-add-themes.md b/content/prompts/fun-add-themes.md deleted file mode 100644 index a0611b47..00000000 --- a/content/prompts/fun-add-themes.md +++ /dev/null @@ -1,40 +0,0 @@ -# 🎨 Theme-tastic Interface Enhancement! - -## 🎯 Your Mission -Transform our boring interface into a playground of visual delights! Let users express themselves through awesome themes. - -## 🔍 Key Requirements -1. **Theme Selector Dropdown** - - Position: ↗️ Top-right corner of the screen - - Behavior: Interface instantly refreshes when a new theme is selected - - Default label: "Default" (our current look) - -## 🌈 Required Themes -Add these fabulous theme options: - -* **80s Retro** 🕹️ - - Think neon colors, bold patterns, geometric shapes - - Inspiration: Miami Vice, arcade games, synthwave - -* **Terminal Classic** 💻 - - Nostalgic VT100 green-on-black terminal look - - Features: Monospace fonts, scan lines, command prompt aesthetic - -* **Hand-Sketched** ✏️ - - UI elements that appear hand-drawn with a playful, creative feel - - Think: Doodles, sketch lines, paper texture backgrounds - -* **Steampunk** ⚙️ - - Brass, gears, leather, and Victorian-era aesthetics mixed with futuristic elements - - Inspiration: Jules Verne, The League of Extraordinary Gentlemen, Bioshock Infinite - -* **Fantasy Realm** 🧙 - - Mystical forests, glowing runes, and enchanted elements - - Inspiration: Lord of the Rings, Dungeons & Dragons, Skyrim - - -## 🚀 Bonus Points -- Add subtle animations for theme transitions -- Include a small preview of each theme in the dropdown -- Make sure all themes maintain accessibility standards - diff --git a/content/prompts/monitoring-add-logging.md b/content/prompts/monitoring-add-logging.md deleted file mode 100644 index 75aaef13..00000000 --- a/content/prompts/monitoring-add-logging.md +++ /dev/null @@ -1,30 +0,0 @@ -Add logging commands to server application which is written in python - -The Python Flask is stored in #folder:server - -Create a standardized logging system for the Python Flask with the following requirements: - -1. LOGGING LEVELS: Implement five distinct logging levels (DEBUG, INFO, WARNING, ERROR, CRITICAL) with clear usage guidelines for each. - -2. FORMAT CONSISTENCY: Define a consistent log entry format including: - - Timestamp (ISO 8601 format: YYYY-MM-DD HH:MM:SS.mmm) - - Log level - - Module/component name - - Thread ID (where applicable) - - Message content - -3. CONFIGURATION: Provide a configuration system that allows: - - Setting global minimum log level - - Per-module logging levels - - Multiple output destinations (console, file, external service) - - Log rotation settings for file outputs - -4. CODE EXAMPLES: Include example implementations showing: - - Proper logger initialization - - Correct usage of each log level - - Error/exception logging with stack traces - - Context-enriched logging - -5. PERFORMANCE CONSIDERATIONS: Address how to optimize logging for production environments. - -The solution should be maintainable, follow industry best practices, and minimize performance impact. diff --git a/content/shared-images/code-scanning-dialog.png b/content/shared-images/code-scanning-dialog.png deleted file mode 100644 index e43dec08..00000000 Binary files a/content/shared-images/code-scanning-dialog.png and /dev/null differ diff --git a/content/shared-images/code-scanning-setup.png b/content/shared-images/code-scanning-setup.png deleted file mode 100644 index d653befc..00000000 Binary files a/content/shared-images/code-scanning-setup.png and /dev/null differ diff --git a/content/shared-images/dependabot-settings.png b/content/shared-images/dependabot-settings.png deleted file mode 100644 index 48f13e44..00000000 Binary files a/content/shared-images/dependabot-settings.png and /dev/null differ diff --git a/content/shared-images/secret-scanning-settings.png b/content/shared-images/secret-scanning-settings.png deleted file mode 100644 index cca3c85e..00000000 Binary files a/content/shared-images/secret-scanning-settings.png and /dev/null differ diff --git a/content/shared-images/setup-configure-repo.png b/content/shared-images/setup-configure-repo.png deleted file mode 100644 index e6810be6..00000000 Binary files a/content/shared-images/setup-configure-repo.png and /dev/null differ diff --git a/content/shared-images/setup-secret-protection.png b/content/shared-images/setup-secret-protection.png deleted file mode 100644 index 4e40529f..00000000 Binary files a/content/shared-images/setup-secret-protection.png and /dev/null differ diff --git a/content/shared-images/setup-use-template.png b/content/shared-images/setup-use-template.png deleted file mode 100644 index 98b69181..00000000 Binary files a/content/shared-images/setup-use-template.png and /dev/null differ diff --git a/copilot-cost-savings.md b/copilot-cost-savings.md new file mode 100644 index 00000000..c70cb827 --- /dev/null +++ b/copilot-cost-savings.md @@ -0,0 +1,159 @@ +# GitHub Copilot: Cost-Aware Usage + +Practical habits that reduce AI Credit consumption without reducing output quality. +Reference material for TKE Session 1, Modules 2 and 4. + +--- + +## 0. The billing model changed + +Since 1 June 2026, Copilot bills **GitHub AI Credits** based on token usage (input, output and cached tokens) instead of premium request units. + +What that means in practice: + +- Cost is now proportional to **context size**, not to the number of prompts. +- A single sloppy prompt with the whole repo attached can cost more than fifty tight ones. +- **Code completions and next edit suggestions are still free.** They do not consume credits. +- The old fallback to a cheaper model when you ran out is gone. Usage is governed by available credits and admin budget controls. + +Everything below follows from that one fact: **context size is the invoice**. + +--- + +## 1. Use the free surfaces first + +These cost nothing: + +| Surface | Use it for | +|---|---| +| Ghost text completions | Boilerplate, repetitive edits, the obvious next line | +| Next edit suggestions | Follow-on edits after a rename or signature change | +| Comment to code | Write the intent as a comment, let Copilot draft the body | + +If completions can do the job, do not open chat. Most developers reach for chat far too early. + +--- + +## 2. Scope the context deliberately + +Cost, cheapest to most expensive: + +``` +implicit context < #selection < #file < #codebase +``` + +- **Implicit context is already free of charge to you in effort, not in tokens.** VS Code automatically attaches the active file, your current selection and the file name. You often do not need to attach anything at all. +- **`#selection`** is the narrowest explicit scope. Only appears in the picker when text is actually selected. +- **`#file`** when the answer depends on the file's structure, not one block. +- **`#codebase`** forces a semantic search across the project. Agents already run semantic search on their own when it makes sense, so typing it is usually redundant and always expensive. + +> The habit to build: before you press enter, glance at what is attached. If you can see it in the request, you are paying for it. + +Note: `#workspace` no longer exists. `#codebase` replaced it. + +--- + +## 3. Shorter prompts, same result + +- Slash commands are a few characters instead of a sentence. `/explain`, `/fix`, `/tests`, `/doc`. +- Repeated instructions belong in `copilot-instructions.md` or `AGENTS.md`, not retyped in every prompt. +- Reusable prompt files and agent skills turn a paragraph into `/my-command`. + +--- + +## 4. Manage the conversation, not just the prompt + +Long threads are expensive because the whole history is resent on every turn. + +| Action | When | +|---|---| +| Watch the context meter in the chat input box | Always. Hover it for a token breakdown by category | +| `/compact` | The thread is long but you still need its conclusions | +| `/fork` | You want to explore an alternative without dragging the history along | +| New session (`/clear`) | New task. Do not reuse a thread out of laziness | + +One thread per task. When the task is done, the thread is done. + +--- + +## 5. Exclusions are a cost control + +When an agent searches your workspace with grep or text search, **every match it gets back enters the context window**, including files it never opens. + +Example: an agent greps for `calculateTotal`. Your repo has `node_modules/`, a `dist/` folder of minified bundles and a pile of build logs. The search returns 800 hits. The agent uses 3. You paid for all 800. + +Exclusion settings stop those paths being searched at all, so the matches never exist. + +| Setting | Hidden in Explorer | Excluded from search and grep | Excluded from semantic index | +|---|---|---|---| +| `.gitignore` | no | yes | yes | +| `files.exclude` | yes | yes | yes | +| `search.exclude` | no | yes | no | + +`files.exclude` and `search.exclude` are VS Code settings, not files. Put them in `.vscode/settings.json` and commit that, so the whole team benefits: + +```json +{ + "files.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/build": true + }, + "search.exclude": { + "**/*.log": true, + "**/coverage": true, + "**/*.min.js": true + } +} +``` + +Caveat: `.gitignore` is bypassed if you have the ignored file open or have text selected inside it. + +--- + +## 6. A working index is cheaper than pasting + +The semantic index lets Copilot retrieve the three relevant snippets instead of you pasting three whole files into chat. Cheaper and more accurate. + +Index sources: + +- **GitHub repositories.** GitHub builds and maintains it, usually available instantly. GitHub.com and Enterprise Cloud only, not Enterprise Server. +- **Azure DevOps repositories.** Built automatically once you sign in with your Microsoft account. +- **Everything else.** VS Code builds it locally. On for personal accounts, **off by default for organisations** unless an admin enables the policy. + +Check the state in the Copilot status dashboard in the VS Code status bar. Force a rebuild with **Build Codebase semantic index** from the Command Palette. + +If there is no index, agents still work. They fall back to grep, text search, file search and language intelligence. It is slower and less precise, not broken. + +--- + +## 7. Match the model to the task + +Do not default to the largest available model. Higher-capability models consume more credits per token. Reserve them for genuinely hard reasoning; use a smaller model for renames, docstrings, test scaffolding and formatting work. + +--- + +## 8. Quality is a cost too + +The cheapest request is the one you do not have to send twice. + +- Precise input produces precise output. A vague comment produces a vague function. +- Read the inline diff before accepting. Reading it costs seconds; a bad merged suggestion costs a debugging session. +- Narrower context frequently produces a **better** answer, not just a cheaper one. Less noise for the model to weigh. + +--- + +## Quick checklist + +Before you press enter: + +- [ ] Could a completion have done this instead of a chat request? +- [ ] Is the narrowest scope attached that could answer the question? +- [ ] Did I type `#codebase` out of habit? +- [ ] Is this the right thread, or should it be a new session? +- [ ] What does the context meter say? +- [ ] Does this repo have exclusions committed in `.vscode/settings.json`? + +--- + +*Sources: VS Code AI features documentation and workspace context reference (September 2026), GitHub Copilot usage-based billing announcement (April 2026, effective 1 June 2026).* diff --git a/notebooks/shelter_intake_analysis.ipynb b/notebooks/shelter_intake_analysis.ipynb new file mode 100644 index 00000000..4c29909a --- /dev/null +++ b/notebooks/shelter_intake_analysis.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b1a0c3d2", + "metadata": {}, + "source": [ + "# Shelter Intake Analysis\n", + "\n", + "Quick look at the current shelter intake. We load the dog roster, attach the adopter contact sheet and roll up a small intake summary.\n", + "\n", + "All adopter data below is synthetic sample data used for the training sandbox." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3c9f1e44", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
adopter_idnamestreetpostcodephonedog_name
01Erika MustermannMusterstraße 1210115+49 30 1234567Max
12John Sample42 Example RoadEX4 9PL+44 1632 960123Bella
23Max BeispielBeispielweg 780331+49 89 7654321Charlie
34Jane Placeholder8 Placeholder LanePL1 2AB+44 1632 960987Lucy
45Test PersoonVoorbeeldstraat 991011 AB+31 20 1234567Cooper
\n", + "
" + ], + "text/plain": [ + " adopter_id name street postcode phone dog_name\n", + "0 1 Erika Mustermann Musterstraße 12 10115 +49 30 1234567 Max\n", + "1 2 John Sample 42 Example Road EX4 9PL +44 1632 960123 Bella\n", + "2 3 Max Beispiel Beispielweg 7 80331 +49 89 7654321 Charlie\n", + "3 4 Jane Placeholder 8 Placeholder Lane PL1 2AB +44 1632 960987 Lucy\n", + "4 5 Test Persoon Voorbeeldstraat 99 1011 AB +31 20 1234567 Cooper" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "dogs = pd.read_csv(\"../app/server/models/dogs.csv\")\n", + "\n", + "adopters = pd.DataFrame([\n", + " {\"adopter_id\": 1, \"name\": \"Erika Mustermann\", \"street\": \"Musterstraße 12\", \"postcode\": \"10115\", \"phone\": \"+49 30 1234567\", \"dog_name\": \"Max\"},\n", + " {\"adopter_id\": 2, \"name\": \"John Sample\", \"street\": \"42 Example Road\", \"postcode\": \"EX4 9PL\", \"phone\": \"+44 1632 960123\", \"dog_name\": \"Bella\"},\n", + " {\"adopter_id\": 3, \"name\": \"Max Beispiel\", \"street\": \"Beispielweg 7\", \"postcode\": \"80331\", \"phone\": \"+49 89 7654321\", \"dog_name\": \"Charlie\"},\n", + " {\"adopter_id\": 4, \"name\": \"Jane Placeholder\", \"street\": \"8 Placeholder Lane\", \"postcode\": \"PL1 2AB\", \"phone\": \"+44 1632 960987\", \"dog_name\": \"Lucy\"},\n", + " {\"adopter_id\": 5, \"name\": \"Test Persoon\", \"street\": \"Voorbeeldstraat 99\", \"postcode\": \"1011 AB\", \"phone\": \"+31 20 1234567\", \"dog_name\": \"Cooper\"},\n", + "])\n", + "adopters" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f4a6c0b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dogsavg_age
Gender
Male57.6
\n", + "
" + ], + "text/plain": [ + " dogs avg_age\n", + "Gender \n", + "Male 5 7.6" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "merged = adopters.merge(dogs, left_on=\"dog_name\", right_on=\"Name\")\n", + "summary = merged.groupby(\"Gender\").agg(dogs=(\"Name\", \"count\"), avg_age=(\"Age\", \"mean\"))\n", + "summary" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a2c5e7d0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(5, 4)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "adopters[[\"name\", \"street\", \"postcode\", \"phone\"]].shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9b3f1c6", + "metadata": {}, + "outputs": [], + "source": [ + "print(monthly_intake_totals)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..bdde2c4c --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,45 @@ +# Demo scripts + +Helper scripts for running the training sandbox. See [`DEMO-SETUP.md`](../DEMO-SETUP.md) for the full asset map. + +## `reset-demo.sh` + +Restores the repository to the tagged baseline between sessions. It discards +working-tree changes, removes untracked files, deletes a live-authored +`.github/copilot-instructions.md` if one exists, and hard-resets to the +`demo-baseline` tag. + +As a safety measure it **refuses to run** when the current branch is `main` +and `origin` points at the canonical upstream (`github-samples/pets-workshop`), +so it cannot be aimed at the wrong repository by accident. + +### Tag the baseline (once, before the first session) + +Commit the prepared demo state, then tag it: + +```bash +git add -A +git commit -m "Prepare training demo baseline" +git tag demo-baseline +``` + +If you later change the intended starting state, move the tag: + +```bash +git tag -f demo-baseline +``` + +### Reset between sessions + +From anywhere inside the repository: + +```bash +bash scripts/reset-demo.sh +``` + +On macOS/Linux you can also run it directly once it is executable: + +```bash +chmod +x scripts/reset-demo.sh +./scripts/reset-demo.sh +``` diff --git a/scripts/reset-demo.sh b/scripts/reset-demo.sh new file mode 100755 index 00000000..e273dd59 --- /dev/null +++ b/scripts/reset-demo.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# +# Reset the demo repository back to the tagged baseline between sessions. +# Discards working-tree changes, removes untracked files, deletes the +# live-authored Copilot instructions, and resets to the demo-baseline tag. +set -euo pipefail + +# The canonical upstream repository. The reset is destructive, so refuse to +# run it against upstream on main to avoid pointing it at the wrong repo. +UPSTREAM="github-samples/pets-workshop" +BASELINE_TAG="demo-baseline" + +cd "$(git rev-parse --show-toplevel)" + +branch="$(git rev-parse --abbrev-ref HEAD)" +origin_url="$(git config --get remote.origin.url || true)" + +if [ "$branch" = "main" ] && printf '%s' "$origin_url" | grep -qi "$UPSTREAM"; then + echo "Refusing to run: origin is the upstream ($UPSTREAM) and the branch is 'main'." >&2 + echo "Point this at your own demo fork before resetting." >&2 + exit 1 +fi + +if ! git rev-parse -q --verify "refs/tags/${BASELINE_TAG}" >/dev/null; then + echo "Tag '${BASELINE_TAG}' not found. Create it first (see scripts/README.md)." >&2 + exit 1 +fi + +echo "Discarding working-tree changes..." +git reset --hard + +echo "Removing untracked files..." +git clean -fd + +echo "Removing live-authored Copilot instructions (if present)..." +rm -f .github/copilot-instructions.md + +echo "Resetting to tag '${BASELINE_TAG}'..." +git reset --hard "${BASELINE_TAG}" + +echo "Done. Repository is back at '${BASELINE_TAG}'."