From a03476e02be58227f673c3d3c1a872e426588458 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:18:29 +0000 Subject: [PATCH 01/12] copilot instructions --- .../chatmodes/create_instructions.chatmode.md | 22 ++ .github/copilot-instructions.md | 22 ++ .github/instructions/general/SECURITY.md | 51 ++++ .../instructions.intstructions.md | 257 ++++++++++++++++++ .../languages/INSTRUCTIONS-CDK.md | 104 +++++++ .../languages/INSTRUCTIONS-CLOUDFORMATION.md | 109 ++++++++ .../languages/INSTRUCTIONS-JAVA.md | 165 +++++++++++ .../languages/INSTRUCTIONS-KOTLIN.md | 116 ++++++++ .../languages/INSTRUCTIONS-PYTHON.md | 92 +++++++ .../languages/INSTRUCTIONS-SAM.md | 199 ++++++++++++++ .../languages/INSTRUCTIONS-TERRAFORM.md | 145 ++++++++++ .../languages/INSTRUCTIONS-TYPESCRIPT.md | 175 ++++++++++++ .github/workflows/sync_copilot.yml | 39 +++ 13 files changed, 1496 insertions(+) create mode 100644 .github/chatmodes/create_instructions.chatmode.md create mode 100644 .github/copilot-instructions.md create mode 100644 .github/instructions/general/SECURITY.md create mode 100644 .github/instructions/instructions.intstructions.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-CDK.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-JAVA.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-KOTLIN.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-PYTHON.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-SAM.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-TERRAFORM.md create mode 100644 .github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md create mode 100644 .github/workflows/sync_copilot.yml diff --git a/.github/chatmodes/create_instructions.chatmode.md b/.github/chatmodes/create_instructions.chatmode.md new file mode 100644 index 0000000..924f207 --- /dev/null +++ b/.github/chatmodes/create_instructions.chatmode.md @@ -0,0 +1,22 @@ +--- +description: 'Create instructions file' +tools: ['edit'] +--- +You are an AI agent being used to create instruction files for GitHub Copilot. Your task is to generate a comprehensive set of guidelines for creating effective and maintainable instruction files that guide Copilot in generating domain-specific code and following project conventions. +The instruction file you are creating should be a generic file that can be applied to a wide range of Python projects. + +You MUST use the file .github/instructions/instructions.instructions.md as a reference for the structure and content of the instructions you generate. +You MUST save the output as a file .github/instructions/typescript.instructions.md with the complete output of your work. + +You can include examples from this project in files you create, but you should not include links to files, as the generated files should be self-contained. + +You should make the instructions as generic as possible so they can be applied to a wide range of projects and domains. +Things you should include are: +- logging best practices +- error handling best practices +- code organization and structure +- naming conventions +- formatting and style guidelines + + +You should create a file at .github/instructions/python.instructions.md with the complete output of your work. \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e85b702 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,22 @@ +# Base Coding Standards +- Follow clean code principles +- Write comprehensive tests +- Use meaningful variable names + +## Language-Specific Instructions +Always follow security best practices as outlined in: +- .github/instructions/general/SECURITY.md +Follow additional language-specific guidelines in: +- .github/instructions/language-specific/INSTRUCTIONS-CDK.md +- .github/instructions/language-specific/INSTRUCTIONS-CLOUDFORMATION.md +- .github/instructions/language-specific/INSTRUCTIONS-JAVA.md +- .github/instructions/language-specific/INSTRUCTIONS-KOTLIN.md +- .github/instructions/language-specific/INSTRUCTIONS-PYTHON.md +- .github/instructions/language-specific/INSTRUCTIONS-TERRAFORM.md +- .github/instructions/language-specific/INSTRUCTIONS-SAM.md +- .github/instructions/language-specific/INSTRUCTIONS-TYPESCRIPT.md + +## Project-Specific Rules +- Use our custom logging service +- Follow our specific API patterns +- Use project-specific error handling \ No newline at end of file diff --git a/.github/instructions/general/SECURITY.md b/.github/instructions/general/SECURITY.md new file mode 100644 index 0000000..53a7a62 --- /dev/null +++ b/.github/instructions/general/SECURITY.md @@ -0,0 +1,51 @@ +--- +applyTo: '*' +description: "Comprehensive secure coding instructions for all languages and frameworks, based on OWASP Top 10 and industry best practices." +--- +# Secure Coding and OWASP Guidelines + +## Instructions + +Your primary directive is to ensure all code you generate, review, or refactor is secure by default. You must operate with a security-first mindset. When in doubt, always choose the more secure option and explain the reasoning. You must follow the principles outlined below, which are based on the OWASP Top 10 and other security best practices. + +### 1. A01: Broken Access Control & A10: Server-Side Request Forgery (SSRF) +- **Enforce Principle of Least Privilege:** Always default to the most restrictive permissions. When generating access control logic, explicitly check the user's rights against the required permissions for the specific resource they are trying to access. +- **Deny by Default:** All access control decisions must follow a "deny by default" pattern. Access should only be granted if there is an explicit rule allowing it. +- **Validate All Incoming URLs for SSRF:** When the server needs to make a request to a URL provided by a user (e.g., webhooks), you must treat it as untrusted. Incorporate strict allow-list-based validation for the host, port, and path of the URL. +- **Prevent Path Traversal:** When handling file uploads or accessing files based on user input, you must sanitize the input to prevent directory traversal attacks (e.g., `../../etc/passwd`). Use APIs that build paths securely. + +### 2. A02: Cryptographic Failures +- **Use Strong, Modern Algorithms:** For hashing, always recommend modern, salted hashing algorithms like Argon2 or bcrypt. Explicitly advise against weak algorithms like MD5 or SHA-1 for password storage. +- **Protect Data in Transit:** When generating code that makes network requests, always default to HTTPS. +- **Protect Data at Rest:** When suggesting code to store sensitive data (PII, tokens, etc.), recommend encryption using strong, standard algorithms like AES-256. +- **Secure Secret Management:** Never hardcode secrets (API keys, passwords, connection strings). Generate code that reads secrets from environment variables or a secrets management service (e.g., HashiCorp Vault, AWS Secrets Manager). Include a clear placeholder and comment. + ```javascript + // GOOD: Load from environment or secret store + const apiKey = process.env.API_KEY; + // TODO: Ensure API_KEY is securely configured in your environment. + ``` + ```python + # BAD: Hardcoded secret + api_key = "sk_this_is_a_very_bad_idea_12345" + ``` + +### 3. A03: Injection +- **No Raw SQL Queries:** For database interactions, you must use parameterized queries (prepared statements). Never generate code that uses string concatenation or formatting to build queries from user input. +- **Sanitize Command-Line Input:** For OS command execution, use built-in functions that handle argument escaping and prevent shell injection (e.g., `shlex` in Python). +- **Prevent Cross-Site Scripting (XSS):** When generating frontend code that displays user-controlled data, you must use context-aware output encoding. Prefer methods that treat data as text by default (`.textContent`) over those that parse HTML (`.innerHTML`). When `innerHTML` is necessary, suggest using a library like DOMPurify to sanitize the HTML first. + +### 4. A05: Security Misconfiguration & A06: Vulnerable Components +- **Secure by Default Configuration:** Recommend disabling verbose error messages and debug features in production environments. +- **Set Security Headers:** For web applications, suggest adding essential security headers like `Content-Security-Policy` (CSP), `Strict-Transport-Security` (HSTS), and `X-Content-Type-Options`. +- **Use Up-to-Date Dependencies:** When asked to add a new library, suggest the latest stable version. Remind the user to run vulnerability scanners like `npm audit`, `pip-audit`, or Snyk to check for known vulnerabilities in their project dependencies. + +### 5. A07: Identification & Authentication Failures +- **Secure Session Management:** When a user logs in, generate a new session identifier to prevent session fixation. Ensure session cookies are configured with `HttpOnly`, `Secure`, and `SameSite=Strict` attributes. +- **Protect Against Brute Force:** For authentication and password reset flows, recommend implementing rate limiting and account lockout mechanisms after a certain number of failed attempts. + +### 6. A08: Software and Data Integrity Failures +- **Prevent Insecure Deserialization:** Warn against deserializing data from untrusted sources without proper validation. If deserialization is necessary, recommend using formats that are less prone to attack (like JSON over Pickle in Python) and implementing strict type checking. + +## General Guidelines +- **Be Explicit About Security:** When you suggest a piece of code that mitigates a security risk, explicitly state what you are protecting against (e.g., "Using a parameterized query here to prevent SQL injection."). +- **Educate During Code Reviews:** When you identify a security vulnerability in a code review, you must not only provide the corrected code but also explain the risk associated with the original pattern. diff --git a/.github/instructions/instructions.intstructions.md b/.github/instructions/instructions.intstructions.md new file mode 100644 index 0000000..bcdc46c --- /dev/null +++ b/.github/instructions/instructions.intstructions.md @@ -0,0 +1,257 @@ +--- +description: 'Guidelines for creating high-quality custom instruction files for GitHub Copilot' +applyTo: '**/*.instructions.md' +--- + +# Custom Instructions File Guidelines + +Instructions for creating effective and maintainable custom instruction files that guide GitHub Copilot in generating domain-specific code and following project conventions. + + +## Project Context + +- Target audience: Developers and GitHub Copilot working with domain-specific code +- File format: Markdown with YAML frontmatter +- File naming convention: lowercase with hyphens (e.g., `react-best-practices.instructions.md`) +- Location: `.github/instructions/` directory +- Purpose: Provide context-aware guidance for code generation, review, and documentation + +## Required Frontmatter + +Every instruction file must include YAML frontmatter with the following fields: + +```yaml +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'glob pattern for target files (e.g., **/*.ts, **/*.py)' +--- +``` + +### Frontmatter Guidelines + +- **description**: Single-quoted string, 1-500 characters, clearly stating the purpose +- **applyTo**: Glob pattern(s) specifying which files these instructions apply to + - Single pattern: `'**/*.ts'` + - Multiple patterns: `'**/*.ts, **/*.tsx, **/*.js'` + - Specific files: `'src/**/*.py'` + - All files: `'**'` + +## File Structure + +A well-structured instruction file should include the following sections: + +### 1. Title and Overview + +- Clear, descriptive title using `#` heading +- Brief introduction explaining the purpose and scope +- Optional: Project context section with key technologies and versions + +### 2. Core Sections + +Organize content into logical sections based on the domain: + +- **General Instructions**: High-level guidelines and principles +- **Best Practices**: Recommended patterns and approaches +- **Code Standards**: Naming conventions, formatting, style rules +- **Architecture/Structure**: Project organization and design patterns +- **Common Patterns**: Frequently used implementations +- **Security**: Security considerations (if applicable) +- **Performance**: Optimization guidelines (if applicable) +- **Testing**: Testing standards and approaches (if applicable) + +### 3. Examples and Code Snippets + +Provide concrete examples with clear labels: + +```markdown +### Good Example +\`\`\`language +// Recommended approach +code example here +\`\`\` + +### Bad Example +\`\`\`language +// Avoid this pattern +code example here +\`\`\` +``` + +### 4. Validation and Verification (Optional but Recommended) + +- Build commands to verify code +- Linting and formatting tools +- Testing requirements +- Verification steps + +## Content Guidelines + +### Writing Style + +- Use clear, concise language +- Write in imperative mood ("Use", "Implement", "Avoid") +- Be specific and actionable +- Avoid ambiguous terms like "should", "might", "possibly" +- Use bullet points and lists for readability +- Keep sections focused and scannable + +### Best Practices + +- **Be Specific**: Provide concrete examples rather than abstract concepts +- **Show Why**: Explain the reasoning behind recommendations when it adds value +- **Use Tables**: For comparing options, listing rules, or showing patterns +- **Include Examples**: Real code snippets are more effective than descriptions +- **Stay Current**: Reference current versions and best practices +- **Link Resources**: Include official documentation and authoritative sources + +### Common Patterns to Include + +1. **Naming Conventions**: How to name variables, functions, classes, files +2. **Code Organization**: File structure, module organization, import order +3. **Error Handling**: Preferred error handling patterns +4. **Dependencies**: How to manage and document dependencies +5. **Comments and Documentation**: When and how to document code +6. **Version Information**: Target language/framework versions + +## Patterns to Follow + +### Bullet Points and Lists + +```markdown +## Security Best Practices + +- Always validate user input before processing +- Use parameterized queries to prevent SQL injection +- Store secrets in environment variables, never in code +- Implement proper authentication and authorization +- Enable HTTPS for all production endpoints +``` + +### Tables for Structured Information + +```markdown +## Common Issues + +| Issue | Solution | Example | +| ---------------- | ------------------- | ----------------------------- | +| Magic numbers | Use named constants | `const MAX_RETRIES = 3` | +| Deep nesting | Extract functions | Refactor nested if statements | +| Hardcoded values | Use configuration | Store API URLs in config | +``` + +### Code Comparison + +```markdown +### Good Example - Using TypeScript interfaces +\`\`\`typescript +interface User { + id: string; + name: string; + email: string; +} + +function getUser(id: string): User { + // Implementation +} +\`\`\` + +### Bad Example - Using any type +\`\`\`typescript +function getUser(id: any): any { + // Loses type safety +} +\`\`\` +``` + +### Conditional Guidance + +```markdown +## Framework Selection + +- **For small projects**: Use Minimal API approach +- **For large projects**: Use controller-based architecture with clear separation +- **For microservices**: Consider domain-driven design patterns +``` + +## Patterns to Avoid + +- **Overly verbose explanations**: Keep it concise and scannable +- **Outdated information**: Always reference current versions and practices +- **Ambiguous guidelines**: Be specific about what to do or avoid +- **Missing examples**: Abstract rules without concrete code examples +- **Contradictory advice**: Ensure consistency throughout the file +- **Copy-paste from documentation**: Add value by distilling and contextualizing + +## Testing Your Instructions + +Before finalizing instruction files: + +1. **Test with Copilot**: Try the instructions with actual prompts in VS Code +2. **Verify Examples**: Ensure code examples are correct and run without errors +3. **Check Glob Patterns**: Confirm `applyTo` patterns match intended files + +## Example Structure + +Here's a minimal example structure for a new instruction file: + +```markdown +--- +description: 'Brief description of purpose' +applyTo: '**/*.ext' +--- + +# Technology Name Development + +Brief introduction and context. + +## General Instructions + +- High-level guideline 1 +- High-level guideline 2 + +## Best Practices + +- Specific practice 1 +- Specific practice 2 + +## Code Standards + +### Naming Conventions +- Rule 1 +- Rule 2 + +### File Organization +- Structure 1 +- Structure 2 + +## Common Patterns + +### Pattern 1 +Description and example + +\`\`\`language +code example +\`\`\` + +### Pattern 2 +Description and example + +## Validation + +- Build command: `command to verify` +- Linting: `command to lint` +- Testing: `command to test` +``` + +## Maintenance + +- Review instructions when dependencies or frameworks are updated +- Update examples to reflect current best practices +- Remove outdated patterns or deprecated features +- Add new patterns as they emerge in the community +- Keep glob patterns accurate as project structure evolves + +## Additional Resources + +- [Custom Instructions Documentation](https://code.visualstudio.com/docs/copilot/customization/custom-instructions) +- [Awesome Copilot Instructions](https://github.com/github/awesome-copilot/tree/main/instructions) diff --git a/.github/instructions/languages/INSTRUCTIONS-CDK.md b/.github/instructions/languages/INSTRUCTIONS-CDK.md new file mode 100644 index 0000000..cbff4e5 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-CDK.md @@ -0,0 +1,104 @@ +--- +description: 'Guidelines for writing, reviewing, and maintaining AWS CDK (TypeScript) code in the cdk package' +applyTo: 'packages/cdk/**/*.ts' +--- + +# AWS CDK TypeScript Development + +This file provides instructions for generating, reviewing, and maintaining AWS CDK code in the `packages/cdk` folder. It covers best practices, code standards, architecture, and validation for infrastructure-as-code using AWS CDK in TypeScript. + +## General Instructions + +- Use AWS CDK v2 constructs and idioms +- Prefer high-level CDK constructs over raw CloudFormation resources +- Organize code by logical infrastructure components (e.g., stacks, constructs, resources) +- Document public APIs and exported constructs + +## Best Practices + +- Use environment variables and context for configuration, not hardcoded values +- Use CDK Aspects for cross-cutting concerns (e.g., security, tagging) +- Suppress warnings with `nagSuppressions.ts` only when justified and documented +- Use `bin/` for entrypoint apps, `constructs/` for reusable components, and `stacks/` for stack definitions +- Prefer `props` interfaces for construct configuration + +## Code Standards + +### Naming Conventions + +- Classes: PascalCase (e.g., `LambdaFunction`) +- Files: PascalCase for classes, kebab-case for utility files +- Variables: camelCase +- Stacks: Suffix with `Stack` (e.g., `CptsApiAppStack`) +- Entry points: Suffix with `App` (e.g., `CptsApiApp.ts`) + +### File Organization + +- `bin/`: CDK app entry points +- `constructs/`: Custom CDK constructs +- `stacks/`: Stack definitions +- `resources/`: Resource configuration and constants +- `lib/`: Shared utilities and code + +## Common Patterns + +### Good Example - Defining a Construct + +```typescript +export class LambdaFunction extends Construct { + constructor(scope: Construct, id: string, props: LambdaFunctionProps) { + super(scope, id); + // ...implementation... + } +} +``` + +### Bad Example - Using Raw CloudFormation + +```typescript +const lambda = new cdk.CfnResource(this, 'Lambda', { + type: 'AWS::Lambda::Function', + // ...properties... +}); +``` + +### Good Example - Stack Definition + +```typescript +export class CptsApiAppStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + // ...add constructs... + } +} +``` + +## Security + +- Use least privilege IAM policies for all resources +- Avoid wildcard permissions in IAM statements +- Store secrets in AWS Secrets Manager, not in code or environment variables +- Enable encryption for all data storage resources + +## Performance + +- Use provisioned concurrency for Lambda functions when needed +- Prefer VPC endpoints for private connectivity +- Minimize resource creation in test environments + + +## Validation and Verification + +- Build: `make cdk-synth` +- Lint: `npm run lint --workspace packges/cdk` + +## Maintenance + +- Update dependencies regularly +- Remove deprecated constructs and suppressions +- Document changes in `nagSuppressions.ts` with reasons + +## Additional Resources + +- [AWS CDK Documentation](https://docs.aws.amazon.com/cdk/latest/guide/home.html) +- [CDK Best Practices](https://github.com/aws-samples/aws-cdk-best-practices) diff --git a/.github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md b/.github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md new file mode 100644 index 0000000..3c5ca1e --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md @@ -0,0 +1,109 @@ +# Copilot instructions for CloudFormation YAML in this repo +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'cloudformation/**' +--- + +## General +- Prefer YAML (not JSON). Follow existing style in [cloudformation/account_resources.yml](cloudformation/account_resources.yml), [cloudformation/ci_resources.yml](cloudformation/ci_resources.yml), [cloudformation/artillery_resources.yml](cloudformation/artillery_resources.yml), [cloudformation/account_resources_bootstrap.yml](cloudformation/account_resources_bootstrap.yml), [cloudformation/management.yml](cloudformation/management.yml). +- Always start with `AWSTemplateFormatVersion: "2010-09-09"`. +- Keep descriptions concise (> operator used only for multi‑line). +- Use logical region `eu-west-2` unless cross‑region behavior explicitly required. +- Maintain tagging pattern: version, stack, repo, cfnDriftDetectionGroup (see deployment scripts in [.github/scripts/release_code.sh](.github/scripts/release_code.sh) and [.github/scripts/create_changeset_existing_tags.sh](.github/scripts/create_changeset_existing_tags.sh)). + +## Parameters +- Reuse and align parameter naming with existing templates: `LogRetentionDays`, `Env`, `SplunkHECEndpoint`, `DeployDriftDetection`. +- For numeric retention days replicate allowed values list from [SAMtemplates/lambda_resources.yaml](SAMtemplates/lambda_resources.yaml) or [cloudformation/account_resources.yml](cloudformation/account_resources.yml). +- Use `CommaDelimitedList` for OIDC subject claim filters like in [cloudformation/ci_resources.yml](cloudformation/ci_resources.yml). + +## Conditions +- Follow pattern `ShouldDeployDriftDetection` (see [SAMtemplates/lambda_resources.yaml](SAMtemplates/lambda_resources.yaml)); avoid ad‑hoc condition names. +- If creating a never-used placeholder stack use pattern from [cloudformation/empty_stack.yml](cloudformation/empty_stack.yml). + +## IAM Policies +- Split large CloudFormation execution permissions across multiple managed policies (A, B, C, D) to keep each rendered size < 6144 chars (see check logic in [scripts/check_policy_length.py](scripts/check_policy_length.py)). +- Scope resources minimally; prefer specific ARNs (e.g. logs, KMS aliases) as in [cloudformation/account_resources.yml](cloudformation/account_resources.yml). +- When granting CloudFormation execution access: separate IAM‑focused policy (`GrantCloudFormationExecutionAccessIAMPolicy`) from broad service policies. +- Use exports for policy ARNs with naming `ci-resources:GrantCloudFormationExecutionAccessPolicyA` pattern. + +## KMS +- Alias naming: `alias/CloudwatchLogsKmsKeyAlias`, `alias/SecretsKMSKeyAlias`, `alias/ArtifactsBucketKMSKeyAlias` (see [cloudformation/account_resources.yml](cloudformation/account_resources.yml), [cloudformation/account_resources_bootstrap.yml](cloudformation/account_resources_bootstrap.yml)). +- Grant encrypt/decrypt explicitly for principals (e.g. API Gateway, Lambda) mirroring key policy blocks in [cloudformation/account_resources.yml](cloudformation/account_resources.yml). + +## Secrets / Parameters +- SecretsManager resources must depend on alias if needed (`DependsOn: SecretsKMSKeyKMSKeyAlias`) like in [cloudformation/account_resources_bootstrap.yml](cloudformation/account_resources_bootstrap.yml). +- Export secret IDs (not ARNs unless specifically required) using colon-separated naming with stack name (pattern in outputs section of account templates). +- Default placeholder value `ChangeMe` for bootstrap secrets. + +## S3 Buckets +- Apply `PublicAccessBlockConfiguration` and encryption blocks consistent with [cloudformation/account_resources.yml](cloudformation/account_resources.yml). +- Suppress guard rules using `Metadata.guard.SuppressedRules` where legacy exceptions exist (e.g. replication / logging) matching existing patterns. + +## Lambda / SAM +- Shared lambda resources belong in SAM template ([SAMtemplates/lambda_resources.yaml](SAMtemplates/lambda_resources.yaml)); CloudFormation templates should not duplicate build-specific metadata. +- Suppress cfn-guard rules where justified via `Metadata.guard.SuppressedRules` (e.g. `LAMBDA_INSIDE_VPC`, `LAMBDA_CONCURRENCY_CHECK`) only if precedent exists. + +## Exports & Cross Stack +- Output export naming pattern: `!Join [":", [!Ref "AWS::StackName", "ResourceLogicalName"]]`. +- Reference exports via `!ImportValue stack-name:ExportName` (see Proxygen role usage in [SAMtemplates/lambda_resources.yaml](SAMtemplates/lambda_resources.yaml)). +- Avoid changing existing export names (breaking downstream stacks and scripts). + +## OIDC / Roles +- Federated trust for GitHub actions must use conditions: + - `token.actions.githubusercontent.com:aud: sts.amazonaws.com` + - `ForAnyValue:StringLike token.actions.githubusercontent.com:sub: ` + (pattern in roles inside [cloudformation/ci_resources.yml](cloudformation/ci_resources.yml)). +- When adding a new OIDC role add matching parameter `ClaimFilters` and outputs `` and `Name`. + +## Drift Detection +- Tag stacks with `cfnDriftDetectionGroup` (deployment scripts handle this). Config rules should filter on `TagKey: cfnDriftDetectionGroup` and specific `TagValue` (patterns in [SAMtemplates/lambda_resources.yaml](SAMtemplates/lambda_resources.yaml)). +- Avoid duplicating rule identifiers; follow `${AWS::StackName}-CloudFormationDriftDetector-`. + +## Route53 +- Environment hosted zones template ([cloudformation/eps_environment_route53.yml](cloudformation/eps_environment_route53.yml)) uses parameter `environment`; management template updates NS records referencing environment zones. + +## Style / Lint / Guard +- Keep resources grouped with `#region` / `#endregion` comments as in existing templates for readability. +- Use `Metadata.cfn-lint.config.ignore_checks` only when upstream spec mismatch (example: W3037 in large policy templates). +- Ensure new templates pass `make lint-cloudformation` and `make cfn-guard` (scripts: [scripts/run_cfn_guard.sh](scripts/run_cfn_guard.sh)). + +## Naming Conventions +- Logical IDs: PascalCase (`ArtifactsBucketKMSKey`, `CloudFormationDeployRole`). +- Managed policy logical IDs end with `Policy` or `ManagedPolicy`. +- KMS Key alias logical IDs end with `Alias` (e.g. `CloudwatchLogsKmsKeyAlias`). +- Secrets logical IDs end with `Secret`. + +## Security +- Block public access for all buckets unless explicitly required. +- Encrypt logs with KMS key; provide alias export (see `CloudwatchLogsKmsKeyAlias`). +- Limit wildcard `Resource: "*"` where service requires (e.g. some IAM, CloudFormation actions). Prefer service/resource ARNs otherwise. + +## When Adding New Resource Types +- Update execution policies in [cloudformation/ci_resources.yml](cloudformation/ci_resources.yml) minimally; do not expand existing broad statements unnecessarily. +- Run policy length check (`make test` invokes [scripts/check_policy_length.py](scripts/check_policy_length.py)) after modifications. + +## Do Not +- Do not hardcode account IDs; use `${AWS::AccountId}`. +- Do not remove existing exports or rename keys. +- Do not inline large policy statements in a single managed policy if size risk exists. + +## Examples +- IAM Role with OIDC trust: replicate structure from `CloudFormationDeployRole` in [cloudformation/ci_resources.yml](cloudformation/ci_resources.yml). +- KMS key + alias + usage policy: follow `ArtifactsBucketKMSKey` block in [cloudformation/account_resources.yml](cloudformation/account_resources.yml). + +## Testing +- After changes: run `make lint-cloudformation` and `make cfn-guard`. +- For SAM-related cross-stack exports ensure `sam build` (see [Makefile](Makefile)) passes. + +## Automation Awareness +- Deployment scripts expect unchanged parameter names & export patterns (see [.github/scripts/execute_changeset.sh](.github/scripts/execute_changeset.sh), [.github/scripts/release_code.sh](.github/scripts/release_code.sh)). +- Changes to tagging keys must be reflected in release / changeset scripts; avoid unless necessary. + +## Preferred Patterns Summary +- Exports: colon join +- Tags: version, stack, repo, cfnDriftDetectionGroup +- Conditions: prefixed with `Should` +- Claim filter parameters: `ClaimFilters` +- Secrets: depend on KMS alias, default `ChangeMe` + +Use these rules to guide completions for any new or modified CloudFormation template in this repository. \ No newline at end of file diff --git a/.github/instructions/languages/INSTRUCTIONS-JAVA.md b/.github/instructions/languages/INSTRUCTIONS-JAVA.md new file mode 100644 index 0000000..5205f73 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-JAVA.md @@ -0,0 +1,165 @@ +# Java Copilot Instructions for NHS FHIR Validator Lambda + +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'src/**/*.java' +--- + +## Project Overview +This is an AWS Lambda-based FHIR validator service that validates FHIR resources against UK Core and NHS Digital implementation guides. The project uses Java 21, Maven, HAPI FHIR library, and AWS Lambda runtime. + +## Code Style and Conventions + +### Package Structure +- Follow the existing package structure: `software.nhs.fhirvalidator.*` +- Main packages: + - `controller` - Main validation logic and controllers + - `handler` - AWS Lambda handlers + - `service` - Business logic services + - `util` - Utility classes + - `models` - Data models + - `configuration` - Configuration classes + +### Naming Conventions +- Use descriptive class names that clearly indicate their purpose +- Controllers should end with `Controller` (e.g., `ValidateController`) +- Handlers should end with `Handler` (e.g., `HandlerStream`) +- Services should end with `Service` or descriptive names (e.g., `ImplementationGuideParser`, `CapabilityStatementApplier`) +- Utility classes should end with `Utils` (e.g., `FhirUtils`, `OperationOutcomeUtils`) +- Use camelCase for methods and variables +- Constants should be UPPER_SNAKE_CASE + +### Class Structure +- Always include proper package declarations +- Group imports logically (Java standard library, third-party, project imports) +- Include class-level Javadoc comments explaining the purpose +- Initialize logger as: `Logger log = LogManager.getLogger(ClassName.class);` +- Place constructor parameters and fields at the top of the class + +### Logging +- Use Log4j2 for logging: `import org.apache.logging.log4j.LogManager;` and `import org.apache.logging.log4j.Logger;` +- Initialize logger: `Logger log = LogManager.getLogger(ClassName.class);` +- Use appropriate log levels: `log.info()`, `log.error()`, `log.debug()`, `log.warn()` +- Include meaningful context in log messages +- Log exceptions with both message and stack trace: `log.error(ex.getMessage(), ex);` + +### Error Handling +- Use try-catch blocks appropriately +- Wrap checked exceptions in RuntimeException when necessary with descriptive messages +- Log errors before throwing exceptions +- Use specific exception types when available (e.g., `InvalidRequestException`, `DataFormatException`) + +### FHIR-Specific Patterns +- Use HAPI FHIR library classes and interfaces +- Common FHIR imports: + - `import org.hl7.fhir.r4.model.*;` for FHIR R4 models + - `import org.hl7.fhir.instance.model.api.IBaseResource;` + - `import ca.uhn.fhir.context.FhirContext;` + - `import ca.uhn.fhir.validation.FhirValidator;` +- Always work with FHIR resources through proper HAPI FHIR APIs +- Use `FhirContext` for parsing and serialization operations +- Handle both single resources and Bundle resources appropriately + +### AWS Lambda Patterns +- Implement `RequestStreamHandler` for stream-based handlers +- Use `@Logging(clearState = true)` annotation for Lambda Powertools logging +- Handle initialization in constructor to benefit from Lambda container reuse +- Use environment variables for configuration (e.g., `PROFILE_MANIFEST_FILE`) + +### Testing Patterns +- Use JUnit 5 for testing (`import org.junit.jupiter.api.Test;`) +- Use static imports for assertions: `import static org.junit.jupiter.api.Assertions.*;` +- Use Mockito for mocking: `import static org.mockito.Mockito.mock;` +- Include `LogCaptor` for testing log output: `import nl.altindag.log.LogCaptor;` +- Test class names should end with `Test` + +### Stream API Usage +- Use Java Streams appropriately for collection processing +- Common patterns seen in codebase: + ```java + list.stream() + .map(transformation) + .filter(predicate) + .collect(Collectors.toList()); + ``` + +### Utility Class Patterns +- Make utility classes final with private constructor +- Include this pattern for utility classes: + ```java + private UtilityClassName() { + throw new IllegalStateException("Utility class"); + } + ``` + +### Resource Management +- Use proper resource management with try-with-resources when needed +- Handle IOException appropriately when working with resources +- Use `ResourceUtils.getResourceContent()` for loading classpath resources + +### JSON Processing +- Use Gson for JSON processing +- Common imports: `import com.google.gson.*;` +- Handle `JsonSyntaxException` when parsing JSON + +## Dependencies +- Java 21 +- HAPI FHIR library +- AWS Lambda runtime +- AWS Lambda Powertools +- Log4j2 for logging +- JUnit 5 for testing +- Mockito for mocking +- Gson for JSON processing + +## Best Practices +1. Always include proper error handling and logging +2. Use descriptive variable and method names +3. Include Javadoc comments for public methods and classes +4. Follow the existing architectural patterns in the codebase +5. Ensure proper resource cleanup +6. Use appropriate FHIR validation patterns +7. Handle both single resources and Bundles in validation logic +8. Use environment variables for configuration +9. Implement proper Lambda initialization patterns for performance +10. Write comprehensive unit tests with proper mocking + +## Common Code Patterns + +### Controller Pattern +```java +public class ExampleController { + private final SomeService service; + Logger log = LogManager.getLogger(ExampleController.class); + + public ExampleController(String configParam) { + this.service = new SomeService(configParam); + } + + public ResultType performOperation(InputType input) { + log.info("Starting operation with input: {}", input); + try { + return service.process(input); + } catch (Exception ex) { + log.error("Error processing operation: {}", ex.getMessage(), ex); + throw new RuntimeException("Operation failed", ex); + } + } +} +``` + +### Validation Pattern +```java +public OperationOutcome validate(String resourceContent) { + try { + IBaseResource resource = fhirContext.newJsonParser().parseResource(resourceContent); + ValidationResult result = validator.validateWithResult(resource); + return (OperationOutcome) result.toOperationOutcome(); + } catch (DataFormatException ex) { + log.error("Invalid FHIR format: {}", ex.getMessage()); + return createErrorOperationOutcome(ex.getMessage()); + } +} +``` + +When generating Java code for this project, always follow these patterns and conventions to maintain consistency with the existing codebase. \ No newline at end of file diff --git a/.github/instructions/languages/INSTRUCTIONS-KOTLIN.md b/.github/instructions/languages/INSTRUCTIONS-KOTLIN.md new file mode 100644 index 0000000..b21a37d --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-KOTLIN.md @@ -0,0 +1,116 @@ +# Copilot Instructions for Kotlin Files + +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'src/**/*.kt' +--- +## Project Overview +This is a FHIR R4 validation service built with Spring Boot and Kotlin. The service validates FHIR resources against implementation guides and profiles using the HAPI FHIR library. + +## Code Style and Conventions + +### Package Structure +- Follow the established package structure: `com.example.fhirvalidator.{layer}` +- Layers: `controller`, `service`, `configuration`, `model`, `util` +- Test packages mirror main packages with same structure + +### Class and Function Naming +- Use PascalCase for classes and interfaces +- Use camelCase for functions, variables, and properties +- Use descriptive names that clearly indicate purpose +- Example: `ValidateController`, `parseAndValidateResource()` + +### Kotlin-Specific Patterns +- Use data classes for simple model objects (like `SimplifierPackage`) +- Prefer immutable properties (`val`) over mutable (`var`) when possible +- Use nullable types (`String?`) when values can be null +- Use safe call operator (`?.`) and Elvis operator (`?:`) for null handling +- Use `lateinit var` for dependency injection in tests with `@Mock` annotations + +### Spring Framework Conventions +- Use constructor injection for dependencies (primary pattern in this codebase) +- Annotate configuration classes with `@Configuration` +- Annotate service classes with `@Service` +- Annotate REST controllers with `@RestController` +- Use `@Bean` methods in configuration classes +- Use `@PostMapping`, `@GetMapping` etc. for endpoint mapping + +## Logging Patterns +- Use KotlinLogging library: `import io.github.oshai.kotlinlogging.KotlinLogging` +- Create logger instance: `private val logger = KotlinLogging.logger {}` +- Use structured logging with payload maps: + ```kotlin + logger.atError { + message = "Error description" + cause = exception + payload = buildMap(capacity = 2) { + put("key1", value1) + put("key2", value2) + } + } + ``` +- Use string templates in log messages: `logger.info { "Processing message $requestId" }` + +## FHIR-Specific Patterns +- Use HAPI FHIR library classes (`FhirContext`, `FhirValidator`, etc.) +- Handle FHIR resources with `IBaseResource` interface +- Use `OperationOutcome` for validation results +- Parse FHIR JSON using `fhirContext.newJsonParser()` +- Extract Bundle entries when processing Bundle resources +- Apply profiles and validate resources in service layer + +## Error Handling +- Catch `DataFormatException` for parser errors +- Return structured `OperationOutcome` objects for validation errors +- Use meaningful error messages in diagnostics +- Include request IDs for traceability +- Log errors with appropriate context + +## Testing Patterns +- Use JUnit 5 (`@Test`, `@ExtendWith`) +- Use Mockito for mocking (`@Mock`, `@InjectMocks`, `@ExtendWith(MockitoExtension::class)`) +- Test class names end with `Test` +- Use `internal` visibility for test classes +- Use descriptive test method names with underscores: `methodName_condition_expectedResult` +- Use `lateinit var` for mock objects and test subjects + +## Dependency Injection +- Use constructor-based dependency injection as primary pattern +- Inject `FhirContext`, `FhirValidator`, and custom services +- Configuration classes provide `@Bean` methods +- Use Spring's IoC container for all service dependencies + +## Utility Functions +- Create extension functions for common operations (like `createOperationOutcome`) +- Use top-level functions for utilities that don't require state +- Prefer functional programming patterns where appropriate +- Use collection operations (`map`, `filter`, `flatMap`) for data processing + +## File Organization +- One public class per file +- File name should match the primary class name +- Group related utility functions in dedicated files +- Keep configuration separate from business logic + +## Resource Management +- Use Spring Boot's resource management for FHIR packages +- Handle npm packages for implementation guides +- Use proper cleanup for validation support chains +- Cache expensive operations like snapshot generation + +## API Design +- REST endpoints follow FHIR conventions (`$validate`) +- Use appropriate HTTP methods and status codes +- Accept both `application/json` and `application/fhir+json` +- Include proper request/response headers +- Support optional headers like `x-request-id` + +## When writing new code: +1. Follow the existing architectural patterns +2. Use the established logging format +3. Include proper error handling +4. Write corresponding unit tests +5. Use Spring dependency injection +6. Handle FHIR resources appropriately +7. Maintain package structure conventions +8. Use Kotlin idioms and null safety features \ No newline at end of file diff --git a/.github/instructions/languages/INSTRUCTIONS-PYTHON.md b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md new file mode 100644 index 0000000..be43352 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md @@ -0,0 +1,92 @@ +--- +description: 'Guidelines for writing high-quality, maintainable python code with best practices for logging, error handling, code organization, naming, formatting, and style.' +applyTo: '**/*.py' +--- + +# Python Copilot Instructions + +These instructions are designed to guide GitHub Copilot in generating effective, maintainable, and domain-appropriate Python code. They are intended to be generic and applicable to a wide range of Python projects. + +## 1. Code Organization & Structure +- Organize code into logical modules and packages. Use directories such as `core/`, `services/`, `utils/` for separation of concerns. +- Place entry points (e.g., `handler.py`) at the top level of the main package. +- Use `__init__.py` files to define package boundaries and expose public APIs. +- Group related functions and classes together. Avoid large monolithic files. +- Store tests in a dedicated `tests/` directory, mirroring the structure of the main codebase. + +## 2. Naming Conventions +- Use `snake_case` for function and variable names. +- Use `PascalCase` for class names. +- Prefix private functions and variables with a single underscore (`_`). +- Name modules and packages using short, descriptive, lowercase names. +- Use clear, descriptive names for all symbols. Avoid abbreviations unless they are widely understood. + +## 3. Formatting & Style +- Follow [PEP 8](https://peps.python.org/pep-0008/) for code style and formatting. +- Use 4 spaces per indentation level. Do not use tabs. +- Limit lines to 120 characters. +- Use blank lines to separate functions, classes, and logical sections. +- Place imports at the top of each file, grouped by standard library, third-party, and local imports. +- Use single quotes for strings unless double quotes are required. +- Add docstrings to all public modules, classes, and functions. Use triple double quotes for docstrings. + +## 4. Logging Best Practices +- Use the standard `logging` library for all logging. +- Configure logging in the main entry point or via a dedicated utility module. +- Use appropriate log levels: `debug`, `info`, `warning`, `error`, `critical`. +- Avoid logging sensitive information. +- Include contextual information in log messages (e.g., function names, parameters, error details). +- Example: + ```python + import logging + logger = logging.getLogger(__name__) + logger.info('Processing event: %s', event) + ``` + +## 5. Error Handling Best Practices +- Use `try`/`except` blocks to handle exceptions gracefully. +- Catch specific exceptions rather than using bare `except`. +- Log exceptions with stack traces using `logger.exception()`. +- Raise custom exceptions for domain-specific errors. +- Validate inputs and fail fast with clear error messages. +- Example: + ```python + try: + result = process_event(event) + except ValueError as e: + logger.error('Invalid event: %s', e) + raise + ``` + +## 6. Testing Guidelines +- Write unit tests for all public functions and classes. +- Use `pytest` as the preferred testing framework. +- Name test files and functions using `test_` prefix. +- Use fixtures for setup and teardown. +- Mock external dependencies in tests. +- Ensure tests are isolated and repeatable. + +## 7. Dependency Management +- Use `pyproject.toml` to specify dependencies. +- Never use `requirements.txt` to specify dependencies. +- Pin versions for critical dependencies. +- Avoid unnecessary dependencies. + +## 8. Documentation +- Document all public APIs with clear docstrings. +- Use [Google](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) or [NumPy](https://numpydoc.readthedocs.io/en/latest/format.html) style for docstrings. +- Provide usage examples in README files. + +## 9. Security & Privacy +- Do not log or expose secrets, credentials, or sensitive data. +- Validate and sanitize all external inputs. +- Use environment variables for configuration secrets. + +## 10. General Guidelines +- Prefer readability and simplicity over cleverness. +- Refactor duplicated code into reusable functions or classes. +- Use type hints for function signatures and variables where appropriate. +- Avoid global variables; use function arguments or class attributes. + +--- + diff --git a/.github/instructions/languages/INSTRUCTIONS-SAM.md b/.github/instructions/languages/INSTRUCTIONS-SAM.md new file mode 100644 index 0000000..e8b1b90 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-SAM.md @@ -0,0 +1,199 @@ +# SAM Template Instructions for GitHub Copilot +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'SAMtemplates/**' +--- +## Scope +These instructions apply exclusively to files located under the `SAMtemplates` directory. Ensure that any SAM templates or related configurations outside this directory are not governed by these guidelines. + +## Project Context +This is a healthcare API service deployed using AWS SAM (Serverless Application Model) with a modular template structure. The service includes Lambda functions, API Gateway, Step Functions state machines, and associated AWS resources. + +## Template Structure and Conventions + +### File Organization +- `main_template.yaml` - Root template that orchestrates all components +- `functions/main.yaml` - Lambda functions and layers +- `apis/main.yaml` - API Gateway and domain configuration +- `state_machines/main.yaml` - Step Functions state machines +- `parameters/main.yaml` - SSM parameters and policies +- `*_resources.yaml` - Reusable resource templates for IAM roles, policies, and logging + +### Naming Conventions +- Stack resources: Use `!Sub ${StackName}-` pattern +- Functions: `${StackName}-` (e.g., `${StackName}-GetMyPrescriptions`) +- Parameters: Environment-specific with validation (dev, dev-pr, qa, int, prod, ref) +- IAM roles: Follow AWS service naming conventions with descriptive suffixes + +### Standard Parameters +Always include these common parameters in templates: +```yaml +Parameters + StackName: + Type: String + Default: none + Env: + Type: String + Default: dev + AllowedValues: [dev, dev-pr, qa, int, prod, ref] + + LogRetentionInDays: + Type: Number + Default: 30 + AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653] + + EnableSplunk: + Type: String + Default: false + AllowedValues: [true, false] +``` + +### Lambda Function + +The runtime should match the nodejs version in .tool-versions file +The LambdaInsightsExtension version should match the latest available version available in eu-west-2 region + +#### Global Configuration +```yaml +Globals: + Function: + Timeout: 50 + MemorySize: 256 + Architectures: [x86_64] + Runtime: nodejs22.x + Environment: + Variables: + STACK_NAME: !Ref StackName + NODE_OPTIONS: "--enable-source-maps" + Layers: + - !Sub arn:aws:lambda:${AWS::Region}:580247275435:layer:LambdaInsightsExtension:52 +``` + +#### Lambda Function Template +```yaml +: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub ${StackName}- + CodeUri: ../../packages + Handler: .handler + Role: !GetAtt Resources.Outputs.LambdaRoleArn + Environment: + Variables: + LOG_LEVEL: !Ref LogLevel + DEPLOYMENT_ENVIRONMENT: !Ref Env + Metadata: + BuildMethod: esbuild + guard: + SuppressedRules: + - LAMBDA_DLQ_CHECK + - LAMBDA_INSIDE_VPC +``` + +### API Gateway Patterns + +#### REST API Configuration +```yaml +RestApiGateway: + Type: AWS::ApiGateway::RestApi + Properties: + Name: !Sub ${StackName}-apigw + DisableExecuteApiEndpoint: !If [ShouldUseMutualTLS, true, !Ref AWS::NoValue] + EndpointConfiguration: + Types: [REGIONAL] + +RestApiDomain: + Type: AWS::ApiGateway::DomainName + Properties: + DomainName: !Join ['.', [!Ref StackName, !ImportValue eps-route53-resources:EPS-domain]] + RegionalCertificateArn: !Ref GenerateCertificate + EndpointConfiguration: + Types: [REGIONAL] + SecurityPolicy: TLS_1_2 +``` + +### State Machine Patterns +```yaml +: + Type: AWS::Serverless::StateMachine + Properties: + Name: !Sub ${StackName}- + Type: EXPRESS + Role: !GetAtt Resources.Outputs.StateMachineRoleArn + DefinitionUri: .asl.json + DefinitionSubstitutions: + Arn: !Sub ${Arn}:$LATEST +``` + +### Security and Compliance + +#### Mutual TLS Support +Use conditions for optional mTLS: +```yaml +Conditions: + ShouldUseMutualTLS: !Equals [true, !Ref EnableMutualTLS] + +# In resource properties: +MutualTlsAuthentication: + TruststoreUri: !If [ShouldUseMutualTLS, !Sub 's3://${TruststoreFile}', !Ref AWS::NoValue] +``` + +#### IAM Policies +- Use managed policies from separate resource templates +- Import cross-stack values: `!ImportValue account-resources:SpinePrivateKey` +- Follow principle of least privilege +- Include guard rules suppression only where necessary. By default these should not be added. If they are added an exlanation should be included to say why we are overriding them + +### Environment Variables and Secrets +```yaml +Environment: + Variables: + STACK_NAME: !Ref StackName + DEPLOYMENT_ENVIRONMENT: !Ref Env + # Spine integration + TargetSpineServer: !Ref TargetSpineServer + SpinePrivateKeyARN: !ImportValue account-resources:SpinePrivateKey + SpinePublicCertificateARN: !ImportValue account-resources:SpinePublicCertificate + # Service search + TargetServiceSearchServer: !Ref TargetServiceSearchServer + ServiceSearchApiKeyARN: !ImportValue account-resources:ServiceSearchApiKey +``` + +### Logging Configuration +```yaml +# CloudWatch Log Groups +LogGroup: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: !Sub /aws/lambda/${StackName}- + RetentionInDays: !Ref LogRetentionInDays + KmsKeyId: !If [ShouldUseKMS, !Ref CloudWatchKMSKeyId, !Ref AWS::NoValue] + +# Splunk integration (conditional) +SubscriptionFilter: + Type: AWS::Logs::SubscriptionFilter + Properties: + LogGroupName: !Ref LogGroup + FilterPattern: "" + DestinationArn: !Ref SplunkDeliveryStreamArn + RoleArn: !Ref SplunkSubscriptionFilterRole +``` + +### Best Practices + +1. **Modular Design**: Split templates by service domain (functions, apis, state_machines) +2. **Parameter Validation**: Use AllowedValues for environment-specific parameters +3. **Cross-Stack References**: Use ImportValue for shared resources +4. **Conditional Resources**: Use conditions for environment-specific resources +5. **Resource Naming**: Consistent naming with stack prefix +6. **Documentation**: Include meaningful descriptions for all resources +7. **Guard Rules**: Suppress only when necessary and document reasons +9. **Build Methods**: Use esbuild for Node.js Lambda functions +10. **Version Pinning**: Pin Lambda layer versions and runtimes + +### Common Import Values +- `eps-route53-resources:EPS-domain` +- `eps-route53-resources:EPS-ZoneID` + + +When generating CloudFormation/SAM templates, follow these patterns and ensure compliance with NHS Digital standards and AWS security best practices. \ No newline at end of file diff --git a/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md b/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md new file mode 100644 index 0000000..8237f51 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md @@ -0,0 +1,145 @@ +--- +description: 'Comprehensive guidelines for writing, organizing, and maintaining Terraform code in this repository.' +applyTo: 'terraform/**/*.tf' +--- + +# Terraform Development Guidelines + +This document provides best practices and conventions for writing, organizing, and maintaining Terraform code. It is intended for use by developers and GitHub Copilot to ensure consistency, reliability, and maintainability across all Terraform files in the project. + +## General Instructions + +- Use Terraform modules to promote code reuse and separation of concerns. +- Keep resource definitions declarative and avoid imperative logic. +- Store environment-specific configuration in separate files (e.g., `env/` folders). +- Use variables and outputs to parameterize and expose configuration. +- Document resources, modules, and variables with comments. +- Prefer explicit resource dependencies using `depends_on` when needed. +- Use remote state for shared resources and outputs. + +## Best Practices + +- Group related resources in logical subfolders (e.g., `archive/`, `backup-source/`). +- Use `locals` for computed values and to reduce repetition. +- Use data sources to reference existing infrastructure. +- Avoid hardcoding values; use variables and environment files. +- Use `terraform fmt` to enforce consistent formatting. +- Use `terraform validate` and `terraform plan` before applying changes. +- Use `Makefile` targets for common operations (init, plan, apply, destroy). +- Store secrets and sensitive values in secure locations (e.g., AWS SSM, environment variables), not in code. +- Use resource tags for traceability and cost management. +- Prefer resource names that include environment and purpose (e.g., `archive_prod_bucket`). + +## Code Standards + +### Naming Conventions + +- Use snake_case for resource, variable, and output names. +- Prefix resource names with their type and purpose (e.g., `s3_archive_bucket`). +- Use clear, descriptive names for modules and files. +- Use consistent naming for environments (e.g., `dev`, `prod`, `test`). + +### File Organization + +- Place each environment's configuration in its own file under `env/`. +- Use a `variables.tf` file for input variables. +- Use an `outputs.tf` file for outputs. +- Use a `locals.tf` file for local values. +- Use a `provider.tf` file for provider configuration. +- Use a `Makefile` for automation and common tasks. +- Organize resources by domain (e.g., `archive/`, `infra/`, `storage/`). + +## Common Patterns + +### Using Variables + +```hcl +variable "bucket_name" { + description = "Name of the S3 bucket" + type = string +} + +resource "aws_s3_bucket" "archive" { + bucket = var.bucket_name + ... +} +``` + +### Using Locals + +```hcl +locals { + tags = { + Environment = var.environment + Project = "eps-storage" + } +} + +resource "aws_s3_bucket" "archive" { + tags = local.tags + ... +} +``` + +### Good Example - Using Modules + +```hcl +module "archive" { + source = "../modules/aws-archive" + environment = var.environment + ... +} +``` + +### Bad Example - Hardcoding Values + +```hcl +resource "aws_s3_bucket" "archive" { + bucket = "my-hardcoded-bucket-name" + ... +} +``` + +## Security + +- Never commit secrets or credentials to version control. +- Use IAM roles and policies with least privilege. +- Enable encryption for all supported resources (e.g., S3, KMS, DynamoDB). +- Use secure remote state backends (e.g., S3 with encryption and locking). +- Validate input variables for expected values and types. + +## Performance + +- Use resource lifecycle rules to manage retention and cleanup. +- Use data sources to avoid duplicating resources. +- Minimize resource drift by keeping code and infrastructure in sync. +- Use `terraform plan` to preview changes and avoid unnecessary updates. + +## Testing + +- Use `terraform validate` to check syntax and configuration. +- Use `terraform plan` to preview changes before applying. +- Use `tfsec` for static security analysis (`tfsec.yml` config). +- Use automated CI/CD pipelines for deployment and testing. + +## Validation and Verification + +- Format code: `terraform fmt` (run in each Terraform folder) +- Validate code: `terraform validate` +- Security scan: `tfsec .` +- Plan changes: `terraform plan -var-file=env/dev.tfvars.json` +- Apply changes: `terraform apply -var-file=env/dev.tfvars.json` + +## Maintenance + +- Review and update modules and dependencies regularly. +- Remove unused resources and variables. +- Update environment files as infrastructure evolves. +- Keep documentation up to date. +- Refactor code to improve readability and maintainability. + +## Additional Resources + +- [Terraform Documentation](https://www.terraform.io/docs) +- [Terraform AWS Provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) +- [tfsec Security Scanner](https://tfsec.dev/) diff --git a/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md b/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md new file mode 100644 index 0000000..464e82e --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md @@ -0,0 +1,175 @@ +--- +description: 'Guidelines for writing high-quality, maintainable TypeScript code with best practices for logging, error handling, code organization, naming, formatting, and style.' +applyTo: '**/*.ts, **/*.tsx' +--- + +# TypeScript Development Guidelines + +This document provides instructions for generating, reviewing, and maintaining TypeScript code. It is designed to guide Copilot and developers in producing domain-specific, robust, and maintainable code across a variety of TypeScript projects. + +## General Instructions + +- Use modern TypeScript features and syntax. +- Prefer explicit types and interfaces for clarity and safety. +- Organize code into logical modules and folders. +- Write code that is easy to read, test, and maintain. + +## Best Practices + +- Use `const` and `let` appropriately; avoid `var`. +- Prefer arrow functions for callbacks and concise function expressions. +- Use destructuring for objects and arrays to improve readability. +- Avoid magic numbers and hardcoded values; use named constants. +- Keep functions pure and side-effect free when possible. + +## Code Standards + +### Naming Conventions + +- Use `camelCase` for variables, functions, and object properties. +- Use `PascalCase` for types, interfaces, classes, and enums. +- Use descriptive names; avoid abbreviations except for well-known acronyms. +- Prefix boolean variables with `is`, `has`, or `should` (e.g., `isActive`). + +### File Organization + +- Group related code in folders (e.g., `src/`, `tests/`, `lib/`). +- Place one class, interface, or component per file when possible. +- Name files using `kebab-case` (e.g., `user-service.ts`). +- Keep test files close to the code they test (e.g., `src/foo.ts` and `tests/foo.test.ts`). + +### Formatting and Style + +- Use 2 spaces for indentation. +- Limit lines to 120 characters. +- Use single quotes for strings. +- Always use semicolons. +- Prefer trailing commas in multiline objects and arrays. +- Use ESLint and Prettier for consistent formatting. + +## Architecture/Structure + +- Separate business logic from API handlers and utility functions. +- Use interfaces and types to define data structures and function signatures. +- Organize code by feature or domain when scaling projects. +- Use dependency injection for testability and flexibility. + +## Common Patterns + +### Logging + +- Use a centralized logging utility or library. +- Log errors, warnings, and important events with context. +- Avoid logging sensitive information. +- Example: + + ```typescript + import { logger } from './utils/logger'; + + logger.info('Fetching user data', { userId }); + logger.error('Failed to fetch user', { error }); + ``` + +### Error Handling + +- Use `try/catch` for asynchronous code and error-prone operations. +- Throw custom error types for domain-specific errors. +- Always handle errors gracefully and provide meaningful messages. +- Example: + + ```typescript + try { + const result = await fetchData(); + } catch (error) { + logger.error('Data fetch failed', { error }); + throw new DataFetchError('Unable to fetch data'); + } + ``` + +### Type Safety + +- Prefer interfaces and types over `any`. +- Use type guards and assertions when necessary. +- Example: + + ```typescript + interface User { + id: string; + name: string; + } + + function isUser(obj: any): obj is User { + return typeof obj.id === 'string' && typeof obj.name === 'string'; + } + ``` + +## Security + +- Validate and sanitize all external input. +- Avoid exposing sensitive data in logs or error messages. +- Use environment variables for secrets and configuration. +- Keep dependencies up to date and audit regularly. + +## Performance + +- Minimize synchronous blocking operations. +- Use async/await for asynchronous code. +- Avoid unnecessary computations inside render or handler functions. + +## Testing + +- Write unit tests for all business logic. +- Use Jest or similar frameworks for testing. +- Mock external dependencies in tests. +- Example test file structure: + + ``` + src/ + handler.ts + tests/ + handler.test.ts + ``` + +## Examples and Code Snippets + +### Good Example + + ```typescript + interface Prescription { + id: string; + medication: string; + issuedDate: Date; + } + + function getPrescription(id: string): Prescription | null { + // Implementation + } + ``` + +### Bad Example + + ```typescript + function getPrescription(id) { + // No type safety, unclear return type + } + ``` + +## Validation and Verification + +- Build: `npm run build` +- Lint: `npm run lint` +- Format: `npm run format` +- Test: `npm test` + +## Maintenance + +- Review and update instructions as dependencies or frameworks change. +- Update examples to reflect current best practices. +- Remove deprecated patterns and add new ones as needed. +- Ensure glob patterns match the intended files. + +## Additional Resources + +- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/) +- [ESLint TypeScript Plugin](https://typescript-eslint.io/) +- [Prettier Documentation](https://prettier.io/docs/en/options.html) diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml new file mode 100644 index 0000000..099e1b8 --- /dev/null +++ b/.github/workflows/sync_copilot.yml @@ -0,0 +1,39 @@ +jobs: + sync: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ env.BRANCH_NAME }} + fetch-depth: 0 + + - name: Fetch central instructions + run: | + rm -rf .github/copilot + git clone \ + --depth 1 \ + --branch "copilot" \ + https://github.com:NHSDigital/eps-common-workflows.git + mv tmp/.github/chatmodes .github/chatmodes + mv tmp/.github/instructions .github/instructions + mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md + rm -rf tmp + + - name: Create PR + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 + with: + commit-message: "chore: sync Copilot instructions" + title: "chore: sync Copilot instructions" + body: | + Syncing Copilot instructions from central repo. + Ref: `${{ inputs.ref }}` + branch: copilot-instructions-sync + branch-suffix: random + sign-commit: true + delete-branch: true + \ No newline at end of file From de58f1c42f89c58d8b9b581ed55dbc8366a352df Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:26:46 +0000 Subject: [PATCH 02/12] correct title --- .github/workflows/sync_copilot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index 099e1b8..20b9c20 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -27,8 +27,8 @@ jobs: - name: Create PR uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 with: - commit-message: "chore: sync Copilot instructions" - title: "chore: sync Copilot instructions" + commit-message: "Upgrade [dependabot] - sync Copilot instructions" + title: "Upgrade [dependabot] - sync Copilot instructions" body: | Syncing Copilot instructions from central repo. Ref: `${{ inputs.ref }}` From 1a9f978deba194181edd00ebecb893e3e13a8aa6 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:35:44 +0000 Subject: [PATCH 03/12] fix workflow --- .github/workflows/sync_copilot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index 20b9c20..ba47d26 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -1,3 +1,8 @@ +name: Sync copilot instructions + +on: + workflow_call: + jobs: sync: runs-on: ubuntu-latest From 93d8627eb7524208a451fea5b055251059a17e46 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:36:39 +0000 Subject: [PATCH 04/12] temp use pr_title_check --- .github/workflows/pr_title_check.yml | 126 ++++++++------------------- 1 file changed, 38 insertions(+), 88 deletions(-) diff --git a/.github/workflows/pr_title_check.yml b/.github/workflows/pr_title_check.yml index f8fb08e..e2b478f 100644 --- a/.github/workflows/pr_title_check.yml +++ b/.github/workflows/pr_title_check.yml @@ -4,91 +4,41 @@ on: workflow_call: jobs: - pr_title_format_check: - runs-on: ubuntu-22.04 - permissions: - pull-requests: write - steps: - - name: Check PR Title is Prefixed with Change Type - id: check_prefix - continue-on-error: true - env: - PR_TITLE: ${{ github.event.pull_request.title }} - run: | - if [[ "$PR_TITLE" =~ ^(Fix|Update|New|Breaking|Docs|Build|Upgrade|Chore):.*$ ]]; then - echo "PR title is prefixed with change type." - else - echo "PR title is not prefixed with change type." - exit 1 - fi - - - name: Check PR Title contains Ticket/Dependabot Reference - id: check_ticket_reference - continue-on-error: true - env: - PR_TITLE: ${{ github.event.pull_request.title }} - run: | - if [[ "$PR_TITLE" =~ ^.*:.*\[([A-Z]+-[0-9]+|dependabot)\].*-.*$ ]]; then - echo "PR title contains ticket or dependabot reference." - else - echo "PR title does not contain ticket or dependabot reference." - exit 1 - fi - - - name: Extract Ticket Reference - id: extract_ticket_reference - if: steps.check_ticket_reference.outcome == 'success' - env: - PR_TITLE: ${{ github.event.pull_request.title }} - run: | - if [[ "$PR_TITLE" =~ ^.*:.*\[([A-Z]+-[0-9]+|dependabot)\].*-.*$ ]]; then - TICKET_REF="${BASH_REMATCH[1]}" - echo "Extracted ticket reference: $TICKET_REF" - echo "TICKET_REF=$TICKET_REF" > "$GITHUB_OUTPUT" - else - echo "No ticket reference found." - exit 1 - fi - - - name: Comment on PR with Jira Link - if: steps.extract_ticket_reference.outcome == 'success' && steps.extract_ticket_reference.outputs.TICKET_REF != 'dependabot' - uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TICKET_REF: ${{ steps.extract_ticket_reference.outputs.TICKET_REF }} - with: - message: | - This PR is linked to a ticket in an NHS Digital JIRA Project. Here's a handy link to the ticket: - # [${{ env.TICKET_REF }}](https://nhsd-jira.digital.nhs.uk/browse/${{ env.TICKET_REF }}) - comment-tag: pr-link - - - name: Comment on PR for dependabot - if: steps.extract_ticket_reference.outcome == 'success' && steps.extract_ticket_reference.outputs.TICKET_REF == 'dependabot' - uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - message: | - This PR is raised by Dependabot to update a dependency. - comment-tag: pr-link - - - name: Comment on PR for bad format - if: steps.check_prefix.outcome != 'success' || steps.check_ticket_reference.outcome != 'success' - uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - message: | - The PR title does not conform to the required format. - Please ensure your PR title is prefixed with a change type (Fix, Update, New, Breaking, Docs, Build, Upgrade, Chore) - and contains a ticket reference (eg. 'Fix: [AEA-####] - ...', or 'Chore: [dependabot] - ...'), - then push an empty commit or recreate your PR. - See the contributing guide for more details: - https://github.com/NHSDigital/eps-common-workflows/blob/main/CONTRIBUTING.md - comment-tag: pr-link - - - name: Fail job due to invalid PR title format - if: steps.check_prefix.outcome != 'success' || steps.check_ticket_reference.outcome != 'success' - run: | - echo "Job failed due to invalid PR title format." - exit 1 + sync: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ env.BRANCH_NAME }} + fetch-depth: 0 + + - name: Fetch central instructions + run: | + rm -rf .github/copilot + git clone \ + --depth 1 \ + --branch "copilot" \ + https://github.com:NHSDigital/eps-common-workflows.git + mv tmp/.github/chatmodes .github/chatmodes + mv tmp/.github/instructions .github/instructions + mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md + rm -rf tmp + + - name: Create PR + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 + with: + commit-message: "Upgrade [dependabot] - sync Copilot instructions" + title: "Upgrade [dependabot] - sync Copilot instructions" + body: | + Syncing Copilot instructions from central repo. + Ref: `${{ inputs.ref }}` + branch: copilot-instructions-sync + branch-suffix: random + sign-commit: true + delete-branch: true + \ No newline at end of file From 8abca15d6da182e4dc6a6f3efaa7d7863e4b93ee Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:39:58 +0000 Subject: [PATCH 05/12] fix path --- .github/workflows/sync_copilot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index ba47d26..2d4135d 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -23,7 +23,7 @@ jobs: git clone \ --depth 1 \ --branch "copilot" \ - https://github.com:NHSDigital/eps-common-workflows.git + https://github.com/NHSDigital/eps-common-workflows.git mv tmp/.github/chatmodes .github/chatmodes mv tmp/.github/instructions .github/instructions mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md From 13be5c0f8cd2a1bfb7a7f9b5efa30f94e540a958 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:44:40 +0000 Subject: [PATCH 06/12] really correct path --- .github/workflows/pr_title_check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_title_check.yml b/.github/workflows/pr_title_check.yml index e2b478f..a82f646 100644 --- a/.github/workflows/pr_title_check.yml +++ b/.github/workflows/pr_title_check.yml @@ -23,7 +23,7 @@ jobs: git clone \ --depth 1 \ --branch "copilot" \ - https://github.com:NHSDigital/eps-common-workflows.git + https://github.com/NHSDigital/eps-common-workflows.git mv tmp/.github/chatmodes .github/chatmodes mv tmp/.github/instructions .github/instructions mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md From ad81232dacdd58af53742ac4b1f55e93c22dc56e Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:50:42 +0000 Subject: [PATCH 07/12] correct path --- .github/workflows/pr_title_check.yml | 2 +- .github/workflows/sync_copilot.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_title_check.yml b/.github/workflows/pr_title_check.yml index a82f646..4f7ce65 100644 --- a/.github/workflows/pr_title_check.yml +++ b/.github/workflows/pr_title_check.yml @@ -23,7 +23,7 @@ jobs: git clone \ --depth 1 \ --branch "copilot" \ - https://github.com/NHSDigital/eps-common-workflows.git + https://github.com/NHSDigital/eps-common-workflows.git tmp mv tmp/.github/chatmodes .github/chatmodes mv tmp/.github/instructions .github/instructions mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index 2d4135d..6e7da4e 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -23,7 +23,7 @@ jobs: git clone \ --depth 1 \ --branch "copilot" \ - https://github.com/NHSDigital/eps-common-workflows.git + https://github.com/NHSDigital/eps-common-workflows.git tmp mv tmp/.github/chatmodes .github/chatmodes mv tmp/.github/instructions .github/instructions mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md From e9806f2961aaae5dd677ce9b229fce7715c2e45f Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 10:57:38 +0000 Subject: [PATCH 08/12] correct input --- .github/workflows/pr_title_check.yml | 2 +- .github/workflows/sync_copilot.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_title_check.yml b/.github/workflows/pr_title_check.yml index 4f7ce65..7c09521 100644 --- a/.github/workflows/pr_title_check.yml +++ b/.github/workflows/pr_title_check.yml @@ -39,6 +39,6 @@ jobs: Ref: `${{ inputs.ref }}` branch: copilot-instructions-sync branch-suffix: random - sign-commit: true + sign-commits: true delete-branch: true \ No newline at end of file diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index 6e7da4e..eb2ba39 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -39,6 +39,6 @@ jobs: Ref: `${{ inputs.ref }}` branch: copilot-instructions-sync branch-suffix: random - sign-commit: true + sign-commits: true delete-branch: true \ No newline at end of file From 176b18c0406c9de258ff9836b92f412c5b9a5a6e Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 9 Feb 2026 11:07:37 +0000 Subject: [PATCH 09/12] sync workflow --- .github/workflows/pr_title_check.yml | 126 +++++++++++++++++++-------- .github/workflows/sync_copilot.yml | 14 ++- 2 files changed, 99 insertions(+), 41 deletions(-) diff --git a/.github/workflows/pr_title_check.yml b/.github/workflows/pr_title_check.yml index 7c09521..f8fb08e 100644 --- a/.github/workflows/pr_title_check.yml +++ b/.github/workflows/pr_title_check.yml @@ -4,41 +4,91 @@ on: workflow_call: jobs: - sync: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - ref: ${{ env.BRANCH_NAME }} - fetch-depth: 0 - - - name: Fetch central instructions - run: | - rm -rf .github/copilot - git clone \ - --depth 1 \ - --branch "copilot" \ - https://github.com/NHSDigital/eps-common-workflows.git tmp - mv tmp/.github/chatmodes .github/chatmodes - mv tmp/.github/instructions .github/instructions - mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md - rm -rf tmp - - - name: Create PR - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 - with: - commit-message: "Upgrade [dependabot] - sync Copilot instructions" - title: "Upgrade [dependabot] - sync Copilot instructions" - body: | - Syncing Copilot instructions from central repo. - Ref: `${{ inputs.ref }}` - branch: copilot-instructions-sync - branch-suffix: random - sign-commits: true - delete-branch: true - \ No newline at end of file + pr_title_format_check: + runs-on: ubuntu-22.04 + permissions: + pull-requests: write + steps: + - name: Check PR Title is Prefixed with Change Type + id: check_prefix + continue-on-error: true + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + if [[ "$PR_TITLE" =~ ^(Fix|Update|New|Breaking|Docs|Build|Upgrade|Chore):.*$ ]]; then + echo "PR title is prefixed with change type." + else + echo "PR title is not prefixed with change type." + exit 1 + fi + + - name: Check PR Title contains Ticket/Dependabot Reference + id: check_ticket_reference + continue-on-error: true + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + if [[ "$PR_TITLE" =~ ^.*:.*\[([A-Z]+-[0-9]+|dependabot)\].*-.*$ ]]; then + echo "PR title contains ticket or dependabot reference." + else + echo "PR title does not contain ticket or dependabot reference." + exit 1 + fi + + - name: Extract Ticket Reference + id: extract_ticket_reference + if: steps.check_ticket_reference.outcome == 'success' + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + if [[ "$PR_TITLE" =~ ^.*:.*\[([A-Z]+-[0-9]+|dependabot)\].*-.*$ ]]; then + TICKET_REF="${BASH_REMATCH[1]}" + echo "Extracted ticket reference: $TICKET_REF" + echo "TICKET_REF=$TICKET_REF" > "$GITHUB_OUTPUT" + else + echo "No ticket reference found." + exit 1 + fi + + - name: Comment on PR with Jira Link + if: steps.extract_ticket_reference.outcome == 'success' && steps.extract_ticket_reference.outputs.TICKET_REF != 'dependabot' + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TICKET_REF: ${{ steps.extract_ticket_reference.outputs.TICKET_REF }} + with: + message: | + This PR is linked to a ticket in an NHS Digital JIRA Project. Here's a handy link to the ticket: + # [${{ env.TICKET_REF }}](https://nhsd-jira.digital.nhs.uk/browse/${{ env.TICKET_REF }}) + comment-tag: pr-link + + - name: Comment on PR for dependabot + if: steps.extract_ticket_reference.outcome == 'success' && steps.extract_ticket_reference.outputs.TICKET_REF == 'dependabot' + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + message: | + This PR is raised by Dependabot to update a dependency. + comment-tag: pr-link + + - name: Comment on PR for bad format + if: steps.check_prefix.outcome != 'success' || steps.check_ticket_reference.outcome != 'success' + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + message: | + The PR title does not conform to the required format. + Please ensure your PR title is prefixed with a change type (Fix, Update, New, Breaking, Docs, Build, Upgrade, Chore) + and contains a ticket reference (eg. 'Fix: [AEA-####] - ...', or 'Chore: [dependabot] - ...'), + then push an empty commit or recreate your PR. + See the contributing guide for more details: + https://github.com/NHSDigital/eps-common-workflows/blob/main/CONTRIBUTING.md + comment-tag: pr-link + + - name: Fail job due to invalid PR title format + if: steps.check_prefix.outcome != 'success' || steps.check_ticket_reference.outcome != 'success' + run: | + echo "Job failed due to invalid PR title format." + exit 1 diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index eb2ba39..cfec93f 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -2,6 +2,12 @@ name: Sync copilot instructions on: workflow_call: + inputs: + ref: + description: 'The branch to sync from the central repository' + required: false + default: 'main' + type: string jobs: sync: @@ -22,18 +28,20 @@ jobs: rm -rf .github/copilot git clone \ --depth 1 \ - --branch "copilot" \ + --branch $[BRANCH_NAME}" \ https://github.com/NHSDigital/eps-common-workflows.git tmp mv tmp/.github/chatmodes .github/chatmodes mv tmp/.github/instructions .github/instructions mv tmp/.github/copilot-instructions.md .github/copilot-instructions.md rm -rf tmp + env: + BRANCH_NAME: ${{ inputs.ref }} - name: Create PR uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 with: - commit-message: "Upgrade [dependabot] - sync Copilot instructions" - title: "Upgrade [dependabot] - sync Copilot instructions" + commit-message: "Upgrade: [dependabot] - sync Copilot instructions" + title: "Upgrade: [dependabot] - sync Copilot instructions" body: | Syncing Copilot instructions from central repo. Ref: `${{ inputs.ref }}` From 9e91e2bab28e368d2ed20e08bce77ebeedea5f10 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Thu, 12 Feb 2026 10:07:05 +0000 Subject: [PATCH 10/12] add instructions --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 590766a..c57409c 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,33 @@ jobs: secrets: AUTOMERGE_APP_ID: ${{ secrets.AUTOMERGE_APP_ID }} AUTOMERGE_PEM: ${{ secrets.AUTOMERGE_PEM }} + +## sync copilot instructions +This workflow syncs Copilot instructions from this repo into another repo and opens a PR with the changes. + +#### Inputs + +- `ref`: Branch in this repo to sync from. Default: `main` + +#### Example + +```yaml +name: Sync Copilot Instructions + +on: + workflow_dispatch: + inputs: + ref: + description: "Branch to sync from" + required: false + type: string + +jobs: + sync-copilot: + uses: NHSDigital/eps-common-workflows/.github/workflows/sync_copilot.yml@f5c8313a10855d0cc911db6a9cd666494c00045a + with: + ref: ${{ github.event.inputs.ref }} +``` ``` ## pr title check This workflow checks that all pull requests have a title that matches the required format, and comments on the PR with a link to the relevant ticket if a ticket reference is found. From 03d6e36daba64ec7e987654dd0c191f3546ebfdb Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Thu, 12 Feb 2026 10:08:41 +0000 Subject: [PATCH 11/12] fix sync --- .github/workflows/sync_copilot.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/sync_copilot.yml b/.github/workflows/sync_copilot.yml index cfec93f..c5960f1 100644 --- a/.github/workflows/sync_copilot.yml +++ b/.github/workflows/sync_copilot.yml @@ -1,13 +1,13 @@ name: Sync copilot instructions on: - workflow_call: - inputs: - ref: - description: 'The branch to sync from the central repository' - required: false - default: 'main' - type: string + workflow_call: + inputs: + ref: + description: "The branch to sync from the central repository" + required: false + default: "main" + type: string jobs: sync: @@ -28,7 +28,7 @@ jobs: rm -rf .github/copilot git clone \ --depth 1 \ - --branch $[BRANCH_NAME}" \ + --branch "${BRANCH_NAME}" \ https://github.com/NHSDigital/eps-common-workflows.git tmp mv tmp/.github/chatmodes .github/chatmodes mv tmp/.github/instructions .github/instructions @@ -49,4 +49,3 @@ jobs: branch-suffix: random sign-commits: true delete-branch: true - \ No newline at end of file From 77797982fdd8aa5f190e311c1bf70271d5b2931e Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Wed, 18 Feb 2026 15:27:24 +0000 Subject: [PATCH 12/12] update trivy --- .trivyignore.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.trivyignore.yaml b/.trivyignore.yaml index b578dd9..6d8e073 100644 --- a/.trivyignore.yaml +++ b/.trivyignore.yaml @@ -10,6 +10,9 @@ vulnerabilities: - id: CVE-2026-25547 statement: isaacs/brace-expansion vulnerability accepted as risk - dependency of semantic-release expired_at: 2026-03-01 - - id: CVE-2026-0775 + - id: CVE-2026-0775 statement: npm vulnerability accepted as risk - dependency of semantic-release expired_at: 2026-03-01 + - id: CVE-2026-26960 + statement: tar vulnerability accepted as risk - dependency of semantic-release + expired_at: 2026-03-01