From f8093c1c4f5dbdd3a2461317a921445d424a74d4 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:14:05 +0000 Subject: [PATCH 01/15] start of instructions --- .../chatmodes/create_instructions.chatmode.md | 0 .github/copilot-instructions.md | 22 ++ .github/instructions/general/SECURITY.md | 51 ++++ .../instructions.intstructions.md | 257 ++++++++++++++++++ .../languages/INSTRUCTIONS-CDK.md | 132 +++++++++ .../languages/INSTRUCTIONS-CLOUDFORMATION.md | 109 ++++++++ .../languages/INSTRUCTIONS-JAVA.md | 165 +++++++++++ .../languages/INSTRUCTIONS-KOTLIN.md | 116 ++++++++ .../languages/INSTRUCTIONS-PYTHON.md | 242 +++++++++++++++++ .../languages/INSTRUCTIONS-SAM.md | 199 ++++++++++++++ .../languages/INSTRUCTIONS-TERRAFORM.md | 173 ++++++++++++ .../languages/INSTRUCTIONS-TYPESCRIPT.md | 76 ++++++ README.md | 2 +- 13 files changed, 1543 insertions(+), 1 deletion(-) 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 diff --git a/.github/chatmodes/create_instructions.chatmode.md b/.github/chatmodes/create_instructions.chatmode.md new file mode 100644 index 0000000..e69de29 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..9f3c433 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-CDK.md @@ -0,0 +1,132 @@ +# Copilot Instructions (TypeScript AWS CDK for this repo) +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'packages/cdk/**' +--- +## Purpose +Guide generation of AWS CDK TypeScript code consistent with existing constructs, patterns, naming conventions, and compliance expectations in this repository. + +## Core Constructs (reuse over raw CDK) +- Prefer custom constructs: + - LambdaFunction -> wraps NodejsFunction with logging, layers, policies ([constructs/LambdaFunction.ts](packages/cdk/constructs/LambdaFunction.ts)). + - RestApiGateway + LambdaEndpoint / StateMachineEndpoint for API resources ([constructs/RestApiGateway.ts](packages/cdk/constructs/RestApiGateway.ts), [constructs/RestApiGateway/LambdaEndpoint.ts](packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts)). + - ExpressStateMachine for Step Functions ([constructs/StateMachine.ts](packages/cdk/constructs/StateMachine.ts)). + - State machine definition ([resources/StateMachineDefinitions/ClinicalView.ts](packages/cdk/resources/StateMachineDefinitions/ClinicalView.ts)). +- When adding a Lambda, use LambdaFunction; do NOT instantiate NodejsFunction directly unless enhancing the construct itself. + +## Naming & Environment +- Lambda function names: `${stackName}-` (see Functions.ts). +- Expose new functions via Functions construct; add environment variables into `lambdaDefaultEnvironmentVariables` ([resources/Functions.ts](packages/cdk/resources/Functions.ts)). +- Required env keys pattern: `LOG_LEVEL`, `NODE_OPTIONS="--enable-source-maps"`, `TargetSpineServer`, versioning (`VERSION_NUMBER`, `COMMIT_ID`), secrets layer (`AWS_LAMBDA_EXEC_WRAPPER="/opt/get-secrets-layer"`). +- Use `Fn.importValue` for cross-stack references (KMS keys, policies, secrets). +- Context keys consumed externally: `accountId`, `stackName`, `versionNumber`, `commitId`, `logRetentionInDays` (set via deployment script). + +## Lambda Defaults +Replicate defaults from getDefaultLambdaOptions: +- runtime: `Runtime.NODEJS_22_X` +- memorySize: 256 +- timeout: 50s +- architecture: `X86_64` +- handler: "handler" +- bundling: `target: es2022`, `minify: true`, `sourceMap: true`, `tsconfig` points to package's tsconfig + +## Logging & Retention +- Pass `logRetentionInDays` into constructs; never hard-code retention. +- Log groups encrypted via imported KMS key; maintain existing pattern. +- When adding subscription filters or streams, follow pattern used in LambdaFunction (Kinesis Stream, subscription roles). + +## Policies & Security +- Attach least-privilege managed policies; reuse imported managed policies. +- Suppress cdk-nag rules only via helper in nagSuppressions.ts; do not inline suppressions. +- Avoid wildcard resource ARNs unless justified; document justification consistent with existing reasoning style. + +## API Resources +- For new API route: + 1. Create LambdaFunction in Functions.ts. + 2. Use LambdaEndpoint under RestApiGateway with `resourceName` matching path segment. + 3. Attach execution policy via `restApiGatewayRole.addManagedPolicy(lambda.executionPolicy)` (see LambdaEndpoint construct). + +## Step Functions +- Compose definitions using existing patterns: Pass, Choice, TaskInput, Function.fromFunctionArn for imported lambdas. +- Return a chainable definition assigned to `this.definition`. + +## Layers & Secrets +- Always include secrets layer and Insights layer (see LambdaFunction: insightsLayerArn + getSecretLayer). +- Do not create additional layers unless absolutely needed; if needed, follow same RemovalPolicy and packaging style. + +## Removal Policies +- Log groups: `RemovalPolicy.RETAIN`. +- Layers: `RemovalPolicy.RETAIN`. +- Keep consistent to avoid accidental data loss. + +## Code Style +- Two-space indentation. +- Interfaces named `Props`. +- Public construct properties explicitly declared (`public readonly`). +- Avoid default exports. +- Use trailing commas only where existing codebase does (mostly arrays/objects in multiline). +- Enforce explicit access modifiers. + +## Import Ordering +1. aws-cdk-lib modules (grouped). +2. Third-party constructs. +3. Local relative imports. +4. Then interfaces & exports. + +## Error Handling +- Do not add try/catch in constructs unless wrapping unsafe dynamic operations; rely on CDK synthesis errors. + +## Adding New Functionality Example (Lambda + Endpoint) +Generate code that: +- Adds a new LambdaFunction in Functions.ts with proper env reuse. +- Adds endpoint in Apis.ts using LambdaEndpoint. + +## What to Avoid +- Direct instantiation of CloudWatch LogGroups outside constructs unless extending logging pattern. +- Hard-coded ARNs instead of `Fn.importValue`. +- Inconsistent environment variable casing. +- Adding inline IAM policies directly to Roles when a ManagedPolicy pattern exists. + +## Testing Expectations +- Any new construct should be testable via snapshot or fine-grained assertions (not provided here—keep design modular). + +## Performance & Bundling +- Keep bundle size small: rely on minification and es2022 target. +- Do not disable source maps (used for debugging with NODE_OPTIONS). + +## cdk-nag +- If new resources trigger nag findings needing suppression, add them to existing grouped calls in nagSuppressions.ts using helper functions; supply reason matching style. + +## Deployment Flow +- Remember deployment scripts set context keys: do not rely on undefined fallbacks; ensure constructs read from props rather than process.env (process.env used only inside Lambda runtime code). + +## Style Snippet Template (for new construct) +```ts +export interface MyFeatureProps { + readonly stackName: string + readonly logRetentionInDays: number + // additional props... +} + +export class MyFeature extends Construct { + public readonly lambda: LambdaFunction + public constructor(scope: Construct, id: string, props: MyFeatureProps) { + super(scope, id) + this.lambda = new LambdaFunction(this, "MyFeatureLambda", { + stackName: props.stackName, + functionName: `${props.stackName}-MyFeature`, + packageBasePath: "packages/myFeature", + entryPoint: "src/handler.ts", + environmentVariables: {/* extend from shared defaults */}, + logRetentionInDays: props.logRetentionInDays, + logLevel: "INFO" + }) + } +} +``` + +## Consistency +Generate code that composes existing constructs, avoids duplication, and maintains naming / environment conventions. + +## If Unsure +Prefer referencing or extending an existing construct rather than creating raw CDK resources. 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..2b91474 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md @@ -0,0 +1,242 @@ +# Python Copilot Instructions for EPS Assist Me + +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: '*.py*' +--- + +## Project Context +This is an AWS Lambda-based Slack bot application that integrates with Amazon Bedrock for AI-powered assistance. The Python code follows serverless patterns and AWS best practices. + +## Code Style & Conventions + +### Import Organization +Follow this import order: +1. Standard library imports +2. Third-party imports (boto3, slack_bolt, etc.) +3. AWS Lambda Powertools imports +4. mypy-boto3 type imports +5. Local application imports (app.*) + +Example: +```python +import json +import os +from typing import Any, Dict +import boto3 +from aws_lambda_powertools import Logger +from mypy_boto3_dynamodb.service_resource import Table +from app.core.config import get_logger +``` + +### Type Hints & mypy-boto3 +- Always use type hints for function parameters and return values +- Use mypy-boto3 types for AWS service clients and resources +- Import specific types from mypy_boto3_* packages + +```python +from mypy_boto3_bedrock_agent_runtime import AgentsforBedrockRuntimeClient +from mypy_boto3_bedrock_agent_runtime.type_defs import RetrieveAndGenerateResponseTypeDef + +def query_bedrock(user_query: str, session_id: str = None) -> RetrieveAndGenerateResponseTypeDef: +``` + +### Docstrings +Use descriptive docstrings that explain: +- What the function does +- Key business logic or AWS service interactions +- Important parameters and return values + +```python +def query_bedrock(user_query: str, session_id: str = None) -> RetrieveAndGenerateResponseTypeDef: + """ + Query Amazon Bedrock Knowledge Base using RAG (Retrieval-Augmented Generation) + + This function retrieves relevant documents from the knowledge base and generates + a response using the configured LLM model with guardrails for safety. + """ +``` + +### Module-level Docstrings +Include module-level docstrings for context: +```python +""" +Main Lambda handler - dual-purpose function for Slack bot operations + +This Lambda function serves two purposes: +1. Handles incoming Slack events (webhooks) via API Gateway +2. Processes async operations when invoked by itself to avoid timeouts +""" +``` + +## AWS Lambda Patterns + +### Handler Functions +- Use AWS Lambda Powertools for logging +- Include proper type hints with LambdaContext +- Use logger decorators for context injection + +```python +from aws_lambda_powertools.utilities.typing import LambdaContext + +@logger.inject_lambda_context(log_event=True, clear_state=True) +def handler(event: dict, context: LambdaContext) -> dict: +``` + +### Configuration Management +- Use @lru_cache() for expensive configuration operations +- Load AWS resources and credentials lazily +- Use environment variables for configuration + +```python +from functools import lru_cache + +@lru_cache() +def get_slack_bot_state_table() -> Table: + dynamodb = boto3.resource("dynamodb") + return dynamodb.Table(os.environ["SLACK_BOT_STATE_TABLE"]) +``` + +## AWS Service Integration + +### Boto3 Client Creation +Create typed clients with proper region configuration: +```python +client: AgentsforBedrockRuntimeClient = boto3.client( + service_name="bedrock-agent-runtime", + region_name=AWS_REGION, +) +``` + +### Error Handling +- Create custom exceptions for domain-specific errors +- Log errors with context before raising +- Use try/except blocks for AWS service calls + +```python +class ConfigurationError(Exception): + """Raised when there's a configuration issue""" + pass + +try: + response = client.retrieve_and_generate(**request_params) +except Exception as e: + logger.error("Failed to query Bedrock", extra={"error": str(e)}) + raise +``` + +## Logging + +### Logger Setup +- Use AWS Lambda Powertools Logger +- Set up logger with service name +- Use structured logging with extra fields + +```python +from aws_lambda_powertools import Logger + +logger = Logger(service="slackBotFunction") + +# In functions +logger.info("Processing request", extra={"session_id": session_id}) +logger.error("Operation failed", extra={"error": str(e), "user_id": user_id}) +``` + +## Testing + +### Test Structure +- Use pytest as the test framework +- Mock AWS services with unittest.mock +- Follow test_* naming convention +- Group related tests in classes + +```python +from unittest.mock import Mock, patch + +@patch("slack_bolt.adapter.aws_lambda.SlackRequestHandler") +def test_handler_normal_event( + mock_handler_class: Mock, + mock_slack_app: Mock, + lambda_context: Mock +): + """Test Lambda handler function for normal Slack events""" +``` + +### Test Coverage +- Maintain high test coverage (configured in pytest.ini) +- Test both success and error scenarios +- Mock external dependencies (AWS services, Slack API) + +## File Organization + +### Package Structure +Follow this directory structure: +``` +app/ +├── __init__.py +├── handler.py # Main Lambda entry point +├── core/ +│ ├── __init__.py +│ └── config.py # Configuration and AWS resource setup +├── services/ +│ ├── __init__.py +│ ├── app.py # Slack app configuration +│ ├── bedrock.py # AWS Bedrock integration +│ ├── dynamo.py # DynamoDB operations +│ ├── exceptions.py # Custom exceptions +│ └── *.py # Other service modules +├── slack/ +│ ├── __init__.py +│ ├── slack_events.py # Slack event handlers +│ └── slack_handlers.py # Slack command handlers +└── utils/ + ├── __init__.py + └── *.py # Utility functions +``` + +## Slack Bot Patterns + +### Event Handling +- Use slack-bolt framework patterns +- Implement lazy listeners for long-running operations +- Handle both events and interactive actions + +```python +from slack_bolt.adapter.aws_lambda import SlackRequestHandler + +app = get_app(logger=logger) +slack_handler = SlackRequestHandler(app) +return slack_handler.handle(event, context) +``` + +### Async Processing +- Use Lambda self-invocation for operations > 3 seconds +- Pass context in event payload for async processing +- Acknowledge Slack events quickly to avoid timeouts + +## Environment Variables +Common environment variables used in the project: +- `SLACK_BOT_TOKEN_PARAMETER` - SSM parameter for Slack bot token +- `SLACK_SIGNING_SECRET_PARAMETER` - SSM parameter for Slack signing secret +- `SLACK_BOT_STATE_TABLE` - DynamoDB table name +- `AWS_REGION` - AWS region +- `KNOWLEDGEBASE_ID` - Bedrock knowledge base ID +- `RAG_MODEL_ID` - Bedrock model ARN +- `GUARD_RAIL_ID` - Bedrock guardrail ID + +## Security & Best Practices +- Store secrets in AWS SSM Parameter Store, not environment variables +- Use IAM roles and policies for least privilege access +- Implement input validation for user queries +- Use Bedrock guardrails for AI safety +- Log sensitive operations for audit trails + +## Dependencies +Key dependencies used in the project: +- `boto3` - AWS SDK +- `mypy-boto3-*` - Type stubs for AWS services +- `slack-bolt` - Slack framework +- `aws-lambda-powertools` - AWS Lambda utilities +- `pytest` - Testing framework +- `pytest-mock` - Mocking utilities +- `moto` - AWS service mocking for tests \ No newline at end of file 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..155ae72 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md @@ -0,0 +1,173 @@ +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: 'terraform/**' +--- + +# Copilot Authoring Guide for Terraform in this Repository + +This guide tunes AI code completions for our Terraform code under `terraform/`. It encodes patterns and constraints we want Copilot to follow. Completions that violate these rules should be discarded or re-asked. + +## Core Principles +- Deterministic, explicit infrastructure: prefer explicit resource blocks over opaque modules unless a shared module already exists in `terraform/modules/`. +- Consistent tagging & naming: all AWS resources must include `default_tags` or explicit `tags` matching our local tags set (see below). +- Environment isolation via Terraform workspaces: never hardcode environment names; derive with `terraform.workspace` in locals. +- Minimise blast radius: use variables for mutable capacity / feature toggles; avoid inline wildcards except where already accepted (some IAM policies intentionally use `*`). +- Security first: prefer least privilege in new policies. Do NOT introduce new broad `"*"` actions unless strongly justified. +- Idempotent & import-friendly: avoid interpolations that create random strings; no `random_*` resources unless approved. + +## Standard Locals Pattern +Every stack defines: +```hcl +locals { + environment = terraform.workspace + production = startswith(local.environment, "live") + data_classification = local.production ? "5" : "1" + aws_account_id = data.aws_caller_identity.current.account_id + tags = { + TagVersion = "1" + Programme = "Clinicals" + Project = "EPS" + DataClassification = local.data_classification + Environment = local.environment + ServiceCategory = local.production ? "Platinum" : "N/A" + Tool = "terraform" + Domain = "clinicals" + map-migrated = "mig45780" + } +} + +data "aws_caller_identity" "current" {} +``` +Copilot should replicate this exact structure when creating a new stack folder. + +## Provider & Backend Block +Use the pattern: +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + required_version = ">=1.9.4" +} + +provider "aws" { + region = "eu-west-2" + allowed_account_ids = var.allowed_account_ids + default_tags { tags = local.tags } +} + +terraform { backend "s3" {} } +``` +Never add backend bucket/key details here; they are injected externally via init. + +## Variables Conventions +- Always define variables with `description` unless trivial and already established (e.g. repeated `allowed_account_ids`). +- Use concrete types (`list(string)`, `map(object({...}))`, `object({...})`); avoid `any`. +- Feature toggles use `bool` with default `false` unless enabling is safer (explicit exceptions: see existing patterns like `deploy_insights = true`). +- Capacity objects: follow `provisioned_capacity` shape from `storage/variables.tf` if adding similar scaling constructs. + +## Resource Naming +- Prefix environment only when conditional: use a `prepend_env` variable or local logic (see `iam.tf`). +- KMS alias pattern: `alias/${local.environment}-`. +- S3 bucket pattern: `${local.environment}-spine-eps-datastore-archive` etc. Always avoid uppercase and underscores. +- IAM roles/policies: include environment, service, purpose; keep consistent with existing examples (`eps-storage-${local.environment}-terraform-plan`). + +## Tagging +- Use provider `default_tags` except where AWS requires inline tags (e.g., DynamoDB `tags` or KMS policy-derived resources). Inline tags MUST include a `Name` following existing naming conventions. +- Do not add ad-hoc tag keys. Changes to tagging require team approval. + +## IAM Policies +- Prefer `data "aws_iam_policy_document"` to build JSON, then `aws_iam_policy`. +- Allowed wildcard: inside `Resource` when resource ARNs vary or for already broadly permitted deployment role (`codebuild_apply`). Don't extend wildcard scope in new policies. +- When referencing DynamoDB stream or table ARNs lists: use for-expressions as in `iam.tf`. + +## KMS Policies +- Keep the minimal root policy block pattern used in `kms.tf`; don't invent complex grants unless required. +- Reuse `aws_account_id` from locals; never hardcode account IDs. + +## DynamoDB Tables +- Table names come from `var.ddb_table_names`; don't hardcode names. +- Support both PAY_PER_REQUEST and PROVISIONED with the existing capacity structure. +- Enable encryption: always specify `server_side_encryption { enabled = true kms_key_arn = .arn }`. +- Conditional features (TTL, PITR, streams) are controlled by variables; replicate this approach for new features. + +## Autoscaling +- Use `for_each` with `toset(var.ddb_table_names)` or derived maps; replicate naming pattern for policies. +- Target tracking metric type should match read/write capacity utilization; keep target at `70` unless data-driven change requested. + +## Event Source Mappings +- Derive lambda ARN via locals; avoid duplicating account ID retrieval. +- Set batching window conditional by environment (`dev` = 0 else 60). +- Use filter criteria with jsonencode pattern like existing `event_source.tf`. + +## S3 Buckets +- Must set: versioning, encryption (KMS), public access block, lifecycle only for non-production (count trick `local.production ? 0 : 1`). +- Enforce SSL with a bucket policy using the `AllowSSLRequestsOnly` statement pattern. + +## CodeBuild / CodePipeline +- Environment variable injection should use interpolation of pipeline variables like shown (`#{variables.TerraformStack}`). +- Buildspec paths live under `terraform/deployment/scripts/`. +- Log groups names follow `/eps-storage-${local.environment}-codebuild/`. +- Use `execution_mode = "PARALLEL"` and include dynamic Approval stage only for non-dev envs. + +## Module Usage +- If adding a reusable construct, place it under `terraform/modules//` and accept inputs rather than hardcoding environment. Provide example usage in a stack. +- Keep modules small, single responsibility (e.g., backup-destination vs backup-source separation). + +## Workspaces & Environments +- Never assume workspace names beyond matching `live*` for production detection. +- Place per-env config in `env/*.tfvars.json` files; do not create new env-specific `.tf` logic. + +## Formatting & Style +- Indent with two spaces. +- Keep attribute ordering: identifiers (name/bucket/etc) first, then settings, then nested blocks, then `tags` last. +- Use snake_case for variable names; hyphen-separated for resource names. +- Use `jsonencode({...})` for inline JSON to prevent quoting mistakes. + +## tfsec & Security Exceptions +- Annotate intentional rule suppressions with inline comments directly above the resource (`# tfsec:ignore:`). +- Don't add new ignores without reason; if required, add short justification after the rule id. + +## Avoid +- Randomness (`random_id`, etc.) unless collision avoidance is critical and approved. +- Hardcoding account IDs, environment names, or ARNs (derive via data sources / locals). +- Unbounded resource growth (autoscaling caps required via variables). + +## When Copilot Generates Code +1. Include the standard locals and provider blocks for new stack folders. +2. Reuse existing variable patterns; if unsure, look at the closest analogous file. +3. Prefer `for_each` over `count` when keys are meaningful (use `count` only for conditional single resources). +4. Suggest feature toggles (bool vars) for optional capabilities. +5. Ensure encryption and logging defaults are present. + +## Review Checklist (Copilot Internal) +Before finalizing a completion: +- Are all new resources tagged? (Either via default_tags or inline Name) +- Are environment/account references dynamic? +- Are optional features behind variables? +- Are IAM/KMS policies least-privilege following patterns? +- Is formatting consistent (2 spaces, block ordering)? + +## Example Minimal New Stack Skeleton +```hcl +# terraform/newstack/locals.tf +locals { /* standard locals as above */ } + +data "aws_caller_identity" "current" {} + +# terraform/newstack/provider.tf +terraform { /* standard required_providers and required_version */ } +provider "aws" { /* region + allowed_account_ids + default_tags */ } +terraform { backend "s3" {} } + +# terraform/newstack/variables.tf +variable "allowed_account_ids" { type = list(string) } +``` + +## Extending This Guide +Raise a PR to modify this file for any new patterns. Don't drift without updating instructions. Keep the frontmatter `languages` array if adding multi-language guidance. + +--- diff --git a/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md b/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md new file mode 100644 index 0000000..83c74c6 --- /dev/null +++ b/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md @@ -0,0 +1,76 @@ +## Language: TypeScript +--- +description: 'Brief description of the instruction purpose and scope' +applyTo: '*.ts' +--- +### Project Style (clinicalView) +- Use named exports (no default) +- Prefer small pure functions; pass `logger: Logger` explicitly rather than using globals. + +### JSON Schema Pattern +- Define schemas with `as const satisfies JSONSchema` +- Export corresponding types using `FromSchema` right after the schema object. +- Reuse existing element schemas instead of inlining duplicates. + +### Coding / Mapping Conventions +- Reuse mapping constants for code/display pairs (e.g. prescription type, performer site, status maps); do not hardcode strings already present. +- For UUIDs use `randomUUID()` from `crypto` +- When adding new status or code systems, centralize enums in a schema file under `src/schema/` and update maps in `fhirMaps.ts`. + +### Error Handling +- Represent conditional outcomes using discriminated unions similar to `ParsedSpineResponse` in [`parseSpineResponse`](packages/clinicalView/src/parseSpineResponse.ts). +- Return `[result, errors]` tuples for validators (pattern in `validateRequest` declaration). + +### Logging & Middleware +- Instantiate `Logger` from `@aws-lambda-powertools/logger`; pass through layers/functions (see [`handler`](packages/clinicalView/src/handler.ts)). +- For new handlers wrap with `middy` and apply existing middlewares: `injectLambdaContext`, `httpHeaderNormalizer`, `inputOutputLogger`, shared error handler. + +### FHIR Resource Construction +- Use existing helper logic patterns in [`generateFhirResponse`](packages/clinicalView/src/generateFhirResponse.ts): derive `intent` from `INTENT_MAP`, map therapy/course codes from constants, conditionally include optional fields with object spread (`...(condition ? {field} : {})`). +- Maintain consistent array wrapping for singular FHIR arrays (e.g. `coding: [{ ... }]`, `identifier: [{ ... }]`, `dosageInstruction: [{ text }]`). + +### Extensions +- Follow existing structure: `{ url, valueCoding }` or `{ url, extension: [...] }` (see [`extensions`](packages/clinicalView/src/schema/extensions.ts)). +- Do not invent new URL namespaces; reuse `https://fhir.nhs.uk/...` patterns. + +### Testing +- Snapshot-like expectations in tests should mirror object shape used in generation; keep field order stable to reduce churn (see tests in `tests/testGenerateFhirResponse.test.ts`). + +### General TS Practices +- Use `as const` for literal enums & maps to preserve string literal types. +- Avoid `any`; prefer explicit interfaces or inferred types from schemas. +- Prefer `Record` for code/display maps (see `PRESCRIPTION_TYPE_MAP` etc.). +- Use union narrowing via presence checks instead of optional chaining inside tight loops for performance-critical parsing. + +### When Adding Code +1. Add schema first (if new structure). +2. Export type via `FromSchema`. +3. Extend maps in `fhirMaps.ts` if introducing coded values. +4. Update generator logic keeping ordering conventions. +5. Add/adjust tests in `packages/clinicalView/tests/`. +6. Ensure new exports are re-exported in index only if needed by other packages. + +### Avoid +- Duplicating code system enums already defined. +- Introducing default exports. +- Hardcoding display text strings when a map exists. +- Using mutable push patterns where direct literal construction is clearer. + +### Example Pattern (New Simple Schema) +```ts +import {FromSchema, JSONSchema} from "json-schema-to-ts" + +export const exampleResource = { + type: "object", + properties: { + resourceType: {type: "string", enum: ["Example"]}, + id: {type: "string"}, + status: {type: "string", enum: ["active", "inactive"]} + }, + required: ["resourceType", "id", "status"] +} as const satisfies JSONSchema + +export type ExampleResourceType = FromSchema +``` + +Keep additions consistent with existing clinicalView module structure. \ No newline at end of file diff --git a/README.md b/README.md index bdb3121..f0f7e69 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# EPS Copilot instructions +# EPS Copilot Instructions This project contains copilot instruction files that can be copied to each EPS repo as an initial instructions. From 785478d04f4cf3ab89ed57f903fba2bf21b48544 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:52:32 +0000 Subject: [PATCH 02/15] cdk and python instructions --- .../chatmodes/create_instructions.chatmode.md | 21 ++ .../languages/INSTRUCTIONS-CDK.md | 218 ++++++------- .../languages/INSTRUCTIONS-PYTHON.md | 305 ++++-------------- 3 files changed, 184 insertions(+), 360 deletions(-) diff --git a/.github/chatmodes/create_instructions.chatmode.md b/.github/chatmodes/create_instructions.chatmode.md index e69de29..9207211 100644 --- a/.github/chatmodes/create_instructions.chatmode.md +++ b/.github/chatmodes/create_instructions.chatmode.md @@ -0,0 +1,21 @@ +--- +description: 'Create instructions file' +tools: [] +--- +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 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/instructions/languages/INSTRUCTIONS-CDK.md b/.github/instructions/languages/INSTRUCTIONS-CDK.md index 9f3c433..cbff4e5 100644 --- a/.github/instructions/languages/INSTRUCTIONS-CDK.md +++ b/.github/instructions/languages/INSTRUCTIONS-CDK.md @@ -1,132 +1,104 @@ -# Copilot Instructions (TypeScript AWS CDK for this repo) --- -description: 'Brief description of the instruction purpose and scope' -applyTo: 'packages/cdk/**' +description: 'Guidelines for writing, reviewing, and maintaining AWS CDK (TypeScript) code in the cdk package' +applyTo: 'packages/cdk/**/*.ts' --- -## Purpose -Guide generation of AWS CDK TypeScript code consistent with existing constructs, patterns, naming conventions, and compliance expectations in this repository. - -## Core Constructs (reuse over raw CDK) -- Prefer custom constructs: - - LambdaFunction -> wraps NodejsFunction with logging, layers, policies ([constructs/LambdaFunction.ts](packages/cdk/constructs/LambdaFunction.ts)). - - RestApiGateway + LambdaEndpoint / StateMachineEndpoint for API resources ([constructs/RestApiGateway.ts](packages/cdk/constructs/RestApiGateway.ts), [constructs/RestApiGateway/LambdaEndpoint.ts](packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts)). - - ExpressStateMachine for Step Functions ([constructs/StateMachine.ts](packages/cdk/constructs/StateMachine.ts)). - - State machine definition ([resources/StateMachineDefinitions/ClinicalView.ts](packages/cdk/resources/StateMachineDefinitions/ClinicalView.ts)). -- When adding a Lambda, use LambdaFunction; do NOT instantiate NodejsFunction directly unless enhancing the construct itself. - -## Naming & Environment -- Lambda function names: `${stackName}-` (see Functions.ts). -- Expose new functions via Functions construct; add environment variables into `lambdaDefaultEnvironmentVariables` ([resources/Functions.ts](packages/cdk/resources/Functions.ts)). -- Required env keys pattern: `LOG_LEVEL`, `NODE_OPTIONS="--enable-source-maps"`, `TargetSpineServer`, versioning (`VERSION_NUMBER`, `COMMIT_ID`), secrets layer (`AWS_LAMBDA_EXEC_WRAPPER="/opt/get-secrets-layer"`). -- Use `Fn.importValue` for cross-stack references (KMS keys, policies, secrets). -- Context keys consumed externally: `accountId`, `stackName`, `versionNumber`, `commitId`, `logRetentionInDays` (set via deployment script). - -## Lambda Defaults -Replicate defaults from getDefaultLambdaOptions: -- runtime: `Runtime.NODEJS_22_X` -- memorySize: 256 -- timeout: 50s -- architecture: `X86_64` -- handler: "handler" -- bundling: `target: es2022`, `minify: true`, `sourceMap: true`, `tsconfig` points to package's tsconfig - -## Logging & Retention -- Pass `logRetentionInDays` into constructs; never hard-code retention. -- Log groups encrypted via imported KMS key; maintain existing pattern. -- When adding subscription filters or streams, follow pattern used in LambdaFunction (Kinesis Stream, subscription roles). - -## Policies & Security -- Attach least-privilege managed policies; reuse imported managed policies. -- Suppress cdk-nag rules only via helper in nagSuppressions.ts; do not inline suppressions. -- Avoid wildcard resource ARNs unless justified; document justification consistent with existing reasoning style. - -## API Resources -- For new API route: - 1. Create LambdaFunction in Functions.ts. - 2. Use LambdaEndpoint under RestApiGateway with `resourceName` matching path segment. - 3. Attach execution policy via `restApiGatewayRole.addManagedPolicy(lambda.executionPolicy)` (see LambdaEndpoint construct). - -## Step Functions -- Compose definitions using existing patterns: Pass, Choice, TaskInput, Function.fromFunctionArn for imported lambdas. -- Return a chainable definition assigned to `this.definition`. - -## Layers & Secrets -- Always include secrets layer and Insights layer (see LambdaFunction: insightsLayerArn + getSecretLayer). -- Do not create additional layers unless absolutely needed; if needed, follow same RemovalPolicy and packaging style. - -## Removal Policies -- Log groups: `RemovalPolicy.RETAIN`. -- Layers: `RemovalPolicy.RETAIN`. -- Keep consistent to avoid accidental data loss. - -## Code Style -- Two-space indentation. -- Interfaces named `Props`. -- Public construct properties explicitly declared (`public readonly`). -- Avoid default exports. -- Use trailing commas only where existing codebase does (mostly arrays/objects in multiline). -- Enforce explicit access modifiers. - -## Import Ordering -1. aws-cdk-lib modules (grouped). -2. Third-party constructs. -3. Local relative imports. -4. Then interfaces & exports. - -## Error Handling -- Do not add try/catch in constructs unless wrapping unsafe dynamic operations; rely on CDK synthesis errors. - -## Adding New Functionality Example (Lambda + Endpoint) -Generate code that: -- Adds a new LambdaFunction in Functions.ts with proper env reuse. -- Adds endpoint in Apis.ts using LambdaEndpoint. - -## What to Avoid -- Direct instantiation of CloudWatch LogGroups outside constructs unless extending logging pattern. -- Hard-coded ARNs instead of `Fn.importValue`. -- Inconsistent environment variable casing. -- Adding inline IAM policies directly to Roles when a ManagedPolicy pattern exists. - -## Testing Expectations -- Any new construct should be testable via snapshot or fine-grained assertions (not provided here—keep design modular). - -## Performance & Bundling -- Keep bundle size small: rely on minification and es2022 target. -- Do not disable source maps (used for debugging with NODE_OPTIONS). - -## cdk-nag -- If new resources trigger nag findings needing suppression, add them to existing grouped calls in nagSuppressions.ts using helper functions; supply reason matching style. - -## Deployment Flow -- Remember deployment scripts set context keys: do not rely on undefined fallbacks; ensure constructs read from props rather than process.env (process.env used only inside Lambda runtime code). - -## Style Snippet Template (for new construct) -```ts -export interface MyFeatureProps { - readonly stackName: string - readonly logRetentionInDays: number - // additional props... + +# 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... +}); +``` -export class MyFeature extends Construct { - public readonly lambda: LambdaFunction - public constructor(scope: Construct, id: string, props: MyFeatureProps) { - super(scope, id) - this.lambda = new LambdaFunction(this, "MyFeatureLambda", { - stackName: props.stackName, - functionName: `${props.stackName}-MyFeature`, - packageBasePath: "packages/myFeature", - entryPoint: "src/handler.ts", - environmentVariables: {/* extend from shared defaults */}, - logRetentionInDays: props.logRetentionInDays, - logLevel: "INFO" - }) +### Good Example - Stack Definition + +```typescript +export class CptsApiAppStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + // ...add constructs... } } ``` -## Consistency -Generate code that composes existing constructs, avoids duplication, and maintains naming / environment conventions. +## 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 -## If Unsure -Prefer referencing or extending an existing construct rather than creating raw CDK 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-PYTHON.md b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md index 2b91474..85dbfba 100644 --- a/.github/instructions/languages/INSTRUCTIONS-PYTHON.md +++ b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md @@ -1,242 +1,73 @@ -# Python Copilot Instructions for EPS Assist Me - ---- -description: 'Brief description of the instruction purpose and scope' -applyTo: '*.py*' ---- - -## Project Context -This is an AWS Lambda-based Slack bot application that integrates with Amazon Bedrock for AI-powered assistance. The Python code follows serverless patterns and AWS best practices. - -## Code Style & Conventions - -### Import Organization -Follow this import order: -1. Standard library imports -2. Third-party imports (boto3, slack_bolt, etc.) -3. AWS Lambda Powertools imports -4. mypy-boto3 type imports -5. Local application imports (app.*) - -Example: -```python -import json -import os -from typing import Any, Dict -import boto3 -from aws_lambda_powertools import Logger -from mypy_boto3_dynamodb.service_resource import Table -from app.core.config import get_logger -``` - -### Type Hints & mypy-boto3 -- Always use type hints for function parameters and return values -- Use mypy-boto3 types for AWS service clients and resources -- Import specific types from mypy_boto3_* packages - -```python -from mypy_boto3_bedrock_agent_runtime import AgentsforBedrockRuntimeClient -from mypy_boto3_bedrock_agent_runtime.type_defs import RetrieveAndGenerateResponseTypeDef - -def query_bedrock(user_query: str, session_id: str = None) -> RetrieveAndGenerateResponseTypeDef: -``` - -### Docstrings -Use descriptive docstrings that explain: -- What the function does -- Key business logic or AWS service interactions -- Important parameters and return values - -```python -def query_bedrock(user_query: str, session_id: str = None) -> RetrieveAndGenerateResponseTypeDef: - """ - Query Amazon Bedrock Knowledge Base using RAG (Retrieval-Augmented Generation) - - This function retrieves relevant documents from the knowledge base and generates - a response using the configured LLM model with guardrails for safety. - """ -``` - -### Module-level Docstrings -Include module-level docstrings for context: -```python +# Copilot Instructions for Python Files in `packages/slackBotFunction` + +## Purpose +These instructions guide GitHub Copilot to generate high-quality, maintainable Python code, with modular architecture, service layers, and test coverage. + +## General Guidelines +- Use Python 3.9+ syntax and typing. +- Follow PEP8 for formatting and naming conventions. +- Prefer explicit imports and avoid wildcard imports. +- Use docstrings for all public functions, classes, and modules. +- Organize code into logical modules: `core`, `services`, `utils`. +- Write modular, testable code with clear separation of concerns. +- Use dependency injection for services and external integrations. +- Handle exceptions gracefully and log errors using the standard `logging` module. +- NEVER hardcode secrets or configuration; use environment variables or config files. + +## File/Folder Specific Instructions +### `app/handler.py` +- Main entry point for AWS Lambda handler. +- Validate and parse incoming Slack events. +- Delegate business logic to service or core modules. +- Return AWS Lambda-compatible response objects. + +### `app/core/` +- Implement core business logic and reusable components. +- Avoid direct dependencies on Slack or AWS SDKs. +- Write pure functions where possible. + +### `app/services/` +- Integrate with external APIs (e.g., Bedrock, DynamoDB). +- Use abstraction layers for AWS services. +- Mock external dependencies in tests. + +### `app/utils/` +- Provide utility functions for common tasks (e.g., string manipulation, config loading). +- Keep utilities stateless and reusable. + +### `tests/` +- Use `pytest` for all tests. +- Name test files and functions descriptively (e.g., `test_handler_utils.py`, `test_forward_to_lambda`). +- Mock AWS and Slack APIs using `pytest-mock` or `unittest.mock`. +- Ensure coverage for error cases and edge conditions. + +## Best Practices +- Use type hints and `Optional` where appropriate. +- Prefer f-strings for string formatting. +- Use context managers for resource handling. +- Write unit tests for all new code and maintain high coverage. +- Document public APIs and expected input/output formats. +- Avoid global state; prefer passing dependencies explicitly. + +## Example Docstring """ -Main Lambda handler - dual-purpose function for Slack bot operations - -This Lambda function serves two purposes: -1. Handles incoming Slack events (webhooks) via API Gateway -2. Processes async operations when invoked by itself to avoid timeouts +Handles incoming Slack event and routes to appropriate service. +Args: + event (dict): Slack event payload. + context (LambdaContext): AWS Lambda context object. +Returns: + dict: AWS Lambda response object. +Raises: + ValueError: If event payload is invalid. """ -``` - -## AWS Lambda Patterns - -### Handler Functions -- Use AWS Lambda Powertools for logging -- Include proper type hints with LambdaContext -- Use logger decorators for context injection - -```python -from aws_lambda_powertools.utilities.typing import LambdaContext - -@logger.inject_lambda_context(log_event=True, clear_state=True) -def handler(event: dict, context: LambdaContext) -> dict: -``` - -### Configuration Management -- Use @lru_cache() for expensive configuration operations -- Load AWS resources and credentials lazily -- Use environment variables for configuration - -```python -from functools import lru_cache - -@lru_cache() -def get_slack_bot_state_table() -> Table: - dynamodb = boto3.resource("dynamodb") - return dynamodb.Table(os.environ["SLACK_BOT_STATE_TABLE"]) -``` - -## AWS Service Integration -### Boto3 Client Creation -Create typed clients with proper region configuration: -```python -client: AgentsforBedrockRuntimeClient = boto3.client( - service_name="bedrock-agent-runtime", - region_name=AWS_REGION, -) -``` +## Prohibited Patterns +- No direct AWS credentials or secrets in code. +- No print statements for logging (use `logging` instead). +- No business logic in handler files; delegate to services/core. -### Error Handling -- Create custom exceptions for domain-specific errors -- Log errors with context before raising -- Use try/except blocks for AWS service calls +## References +- [PEP8](https://peps.python.org/pep-0008/) +- [pytest Documentation](https://docs.pytest.org/en/stable/) -```python -class ConfigurationError(Exception): - """Raised when there's a configuration issue""" - pass - -try: - response = client.retrieve_and_generate(**request_params) -except Exception as e: - logger.error("Failed to query Bedrock", extra={"error": str(e)}) - raise -``` - -## Logging - -### Logger Setup -- Use AWS Lambda Powertools Logger -- Set up logger with service name -- Use structured logging with extra fields - -```python -from aws_lambda_powertools import Logger - -logger = Logger(service="slackBotFunction") - -# In functions -logger.info("Processing request", extra={"session_id": session_id}) -logger.error("Operation failed", extra={"error": str(e), "user_id": user_id}) -``` - -## Testing - -### Test Structure -- Use pytest as the test framework -- Mock AWS services with unittest.mock -- Follow test_* naming convention -- Group related tests in classes - -```python -from unittest.mock import Mock, patch - -@patch("slack_bolt.adapter.aws_lambda.SlackRequestHandler") -def test_handler_normal_event( - mock_handler_class: Mock, - mock_slack_app: Mock, - lambda_context: Mock -): - """Test Lambda handler function for normal Slack events""" -``` - -### Test Coverage -- Maintain high test coverage (configured in pytest.ini) -- Test both success and error scenarios -- Mock external dependencies (AWS services, Slack API) - -## File Organization - -### Package Structure -Follow this directory structure: -``` -app/ -├── __init__.py -├── handler.py # Main Lambda entry point -├── core/ -│ ├── __init__.py -│ └── config.py # Configuration and AWS resource setup -├── services/ -│ ├── __init__.py -│ ├── app.py # Slack app configuration -│ ├── bedrock.py # AWS Bedrock integration -│ ├── dynamo.py # DynamoDB operations -│ ├── exceptions.py # Custom exceptions -│ └── *.py # Other service modules -├── slack/ -│ ├── __init__.py -│ ├── slack_events.py # Slack event handlers -│ └── slack_handlers.py # Slack command handlers -└── utils/ - ├── __init__.py - └── *.py # Utility functions -``` - -## Slack Bot Patterns - -### Event Handling -- Use slack-bolt framework patterns -- Implement lazy listeners for long-running operations -- Handle both events and interactive actions - -```python -from slack_bolt.adapter.aws_lambda import SlackRequestHandler - -app = get_app(logger=logger) -slack_handler = SlackRequestHandler(app) -return slack_handler.handle(event, context) -``` - -### Async Processing -- Use Lambda self-invocation for operations > 3 seconds -- Pass context in event payload for async processing -- Acknowledge Slack events quickly to avoid timeouts - -## Environment Variables -Common environment variables used in the project: -- `SLACK_BOT_TOKEN_PARAMETER` - SSM parameter for Slack bot token -- `SLACK_SIGNING_SECRET_PARAMETER` - SSM parameter for Slack signing secret -- `SLACK_BOT_STATE_TABLE` - DynamoDB table name -- `AWS_REGION` - AWS region -- `KNOWLEDGEBASE_ID` - Bedrock knowledge base ID -- `RAG_MODEL_ID` - Bedrock model ARN -- `GUARD_RAIL_ID` - Bedrock guardrail ID - -## Security & Best Practices -- Store secrets in AWS SSM Parameter Store, not environment variables -- Use IAM roles and policies for least privilege access -- Implement input validation for user queries -- Use Bedrock guardrails for AI safety -- Log sensitive operations for audit trails - -## Dependencies -Key dependencies used in the project: -- `boto3` - AWS SDK -- `mypy-boto3-*` - Type stubs for AWS services -- `slack-bolt` - Slack framework -- `aws-lambda-powertools` - AWS Lambda utilities -- `pytest` - Testing framework -- `pytest-mock` - Mocking utilities -- `moto` - AWS service mocking for tests \ No newline at end of file +--- From 8e7cbcd503ef776b4258f1d5f90d1f1f184a13d6 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:06:38 +0000 Subject: [PATCH 03/15] update typescript --- .../chatmodes/create_instructions.chatmode.md | 3 +- .../languages/INSTRUCTIONS-TYPESCRIPT.md | 239 +++++++++++++----- 2 files changed, 171 insertions(+), 71 deletions(-) diff --git a/.github/chatmodes/create_instructions.chatmode.md b/.github/chatmodes/create_instructions.chatmode.md index 9207211..924f207 100644 --- a/.github/chatmodes/create_instructions.chatmode.md +++ b/.github/chatmodes/create_instructions.chatmode.md @@ -1,11 +1,12 @@ --- description: 'Create instructions file' -tools: [] +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. diff --git a/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md b/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md index 83c74c6..464e82e 100644 --- a/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md +++ b/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md @@ -1,76 +1,175 @@ -## Language: TypeScript --- -description: 'Brief description of the instruction purpose and scope' -applyTo: '*.ts' +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' --- -### Project Style (clinicalView) -- Use named exports (no default) -- Prefer small pure functions; pass `logger: Logger` explicitly rather than using globals. -### JSON Schema Pattern -- Define schemas with `as const satisfies JSONSchema` -- Export corresponding types using `FromSchema` right after the schema object. -- Reuse existing element schemas instead of inlining duplicates. +# TypeScript Development Guidelines -### Coding / Mapping Conventions -- Reuse mapping constants for code/display pairs (e.g. prescription type, performer site, status maps); do not hardcode strings already present. -- For UUIDs use `randomUUID()` from `crypto` -- When adding new status or code systems, centralize enums in a schema file under `src/schema/` and update maps in `fhirMaps.ts`. +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 -- Represent conditional outcomes using discriminated unions similar to `ParsedSpineResponse` in [`parseSpineResponse`](packages/clinicalView/src/parseSpineResponse.ts). -- Return `[result, errors]` tuples for validators (pattern in `validateRequest` declaration). - -### Logging & Middleware -- Instantiate `Logger` from `@aws-lambda-powertools/logger`; pass through layers/functions (see [`handler`](packages/clinicalView/src/handler.ts)). -- For new handlers wrap with `middy` and apply existing middlewares: `injectLambdaContext`, `httpHeaderNormalizer`, `inputOutputLogger`, shared error handler. - -### FHIR Resource Construction -- Use existing helper logic patterns in [`generateFhirResponse`](packages/clinicalView/src/generateFhirResponse.ts): derive `intent` from `INTENT_MAP`, map therapy/course codes from constants, conditionally include optional fields with object spread (`...(condition ? {field} : {})`). -- Maintain consistent array wrapping for singular FHIR arrays (e.g. `coding: [{ ... }]`, `identifier: [{ ... }]`, `dosageInstruction: [{ text }]`). - -### Extensions -- Follow existing structure: `{ url, valueCoding }` or `{ url, extension: [...] }` (see [`extensions`](packages/clinicalView/src/schema/extensions.ts)). -- Do not invent new URL namespaces; reuse `https://fhir.nhs.uk/...` patterns. - -### Testing -- Snapshot-like expectations in tests should mirror object shape used in generation; keep field order stable to reduce churn (see tests in `tests/testGenerateFhirResponse.test.ts`). - -### General TS Practices -- Use `as const` for literal enums & maps to preserve string literal types. -- Avoid `any`; prefer explicit interfaces or inferred types from schemas. -- Prefer `Record` for code/display maps (see `PRESCRIPTION_TYPE_MAP` etc.). -- Use union narrowing via presence checks instead of optional chaining inside tight loops for performance-critical parsing. - -### When Adding Code -1. Add schema first (if new structure). -2. Export type via `FromSchema`. -3. Extend maps in `fhirMaps.ts` if introducing coded values. -4. Update generator logic keeping ordering conventions. -5. Add/adjust tests in `packages/clinicalView/tests/`. -6. Ensure new exports are re-exported in index only if needed by other packages. - -### Avoid -- Duplicating code system enums already defined. -- Introducing default exports. -- Hardcoding display text strings when a map exists. -- Using mutable push patterns where direct literal construction is clearer. - -### Example Pattern (New Simple Schema) -```ts -import {FromSchema, JSONSchema} from "json-schema-to-ts" - -export const exampleResource = { - type: "object", - properties: { - resourceType: {type: "string", enum: ["Example"]}, - id: {type: "string"}, - status: {type: "string", enum: ["active", "inactive"]} - }, - required: ["resourceType", "id", "status"] -} as const satisfies JSONSchema - -export type ExampleResourceType = FromSchema -``` - -Keep additions consistent with existing clinicalView module structure. \ No newline at end of file + +- 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) From 3fa7c66ef234f01b5c57754316beae33885aaec1 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:09:11 +0000 Subject: [PATCH 04/15] update terraform --- .../languages/INSTRUCTIONS-TERRAFORM.md | 270 ++++++++---------- 1 file changed, 121 insertions(+), 149 deletions(-) diff --git a/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md b/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md index 155ae72..8237f51 100644 --- a/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md +++ b/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md @@ -1,173 +1,145 @@ --- -description: 'Brief description of the instruction purpose and scope' -applyTo: 'terraform/**' +description: 'Comprehensive guidelines for writing, organizing, and maintaining Terraform code in this repository.' +applyTo: 'terraform/**/*.tf' --- -# Copilot Authoring Guide for Terraform in this Repository +# Terraform Development Guidelines -This guide tunes AI code completions for our Terraform code under `terraform/`. It encodes patterns and constraints we want Copilot to follow. Completions that violate these rules should be discarded or re-asked. +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. -## Core Principles -- Deterministic, explicit infrastructure: prefer explicit resource blocks over opaque modules unless a shared module already exists in `terraform/modules/`. -- Consistent tagging & naming: all AWS resources must include `default_tags` or explicit `tags` matching our local tags set (see below). -- Environment isolation via Terraform workspaces: never hardcode environment names; derive with `terraform.workspace` in locals. -- Minimise blast radius: use variables for mutable capacity / feature toggles; avoid inline wildcards except where already accepted (some IAM policies intentionally use `*`). -- Security first: prefer least privilege in new policies. Do NOT introduce new broad `"*"` actions unless strongly justified. -- Idempotent & import-friendly: avoid interpolations that create random strings; no `random_*` resources unless approved. +## 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 -## Standard Locals Pattern -Every stack defines: ```hcl -locals { - environment = terraform.workspace - production = startswith(local.environment, "live") - data_classification = local.production ? "5" : "1" - aws_account_id = data.aws_caller_identity.current.account_id - tags = { - TagVersion = "1" - Programme = "Clinicals" - Project = "EPS" - DataClassification = local.data_classification - Environment = local.environment - ServiceCategory = local.production ? "Platinum" : "N/A" - Tool = "terraform" - Domain = "clinicals" - map-migrated = "mig45780" - } +variable "bucket_name" { + description = "Name of the S3 bucket" + type = string } -data "aws_caller_identity" "current" {} +resource "aws_s3_bucket" "archive" { + bucket = var.bucket_name + ... +} ``` -Copilot should replicate this exact structure when creating a new stack folder. -## Provider & Backend Block -Use the pattern: +### Using Locals + ```hcl -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } +locals { + tags = { + Environment = var.environment + Project = "eps-storage" } - required_version = ">=1.9.4" } -provider "aws" { - region = "eu-west-2" - allowed_account_ids = var.allowed_account_ids - default_tags { tags = local.tags } +resource "aws_s3_bucket" "archive" { + tags = local.tags + ... } +``` + +### Good Example - Using Modules -terraform { backend "s3" {} } +```hcl +module "archive" { + source = "../modules/aws-archive" + environment = var.environment + ... +} ``` -Never add backend bucket/key details here; they are injected externally via init. - -## Variables Conventions -- Always define variables with `description` unless trivial and already established (e.g. repeated `allowed_account_ids`). -- Use concrete types (`list(string)`, `map(object({...}))`, `object({...})`); avoid `any`. -- Feature toggles use `bool` with default `false` unless enabling is safer (explicit exceptions: see existing patterns like `deploy_insights = true`). -- Capacity objects: follow `provisioned_capacity` shape from `storage/variables.tf` if adding similar scaling constructs. - -## Resource Naming -- Prefix environment only when conditional: use a `prepend_env` variable or local logic (see `iam.tf`). -- KMS alias pattern: `alias/${local.environment}-`. -- S3 bucket pattern: `${local.environment}-spine-eps-datastore-archive` etc. Always avoid uppercase and underscores. -- IAM roles/policies: include environment, service, purpose; keep consistent with existing examples (`eps-storage-${local.environment}-terraform-plan`). - -## Tagging -- Use provider `default_tags` except where AWS requires inline tags (e.g., DynamoDB `tags` or KMS policy-derived resources). Inline tags MUST include a `Name` following existing naming conventions. -- Do not add ad-hoc tag keys. Changes to tagging require team approval. - -## IAM Policies -- Prefer `data "aws_iam_policy_document"` to build JSON, then `aws_iam_policy`. -- Allowed wildcard: inside `Resource` when resource ARNs vary or for already broadly permitted deployment role (`codebuild_apply`). Don't extend wildcard scope in new policies. -- When referencing DynamoDB stream or table ARNs lists: use for-expressions as in `iam.tf`. - -## KMS Policies -- Keep the minimal root policy block pattern used in `kms.tf`; don't invent complex grants unless required. -- Reuse `aws_account_id` from locals; never hardcode account IDs. - -## DynamoDB Tables -- Table names come from `var.ddb_table_names`; don't hardcode names. -- Support both PAY_PER_REQUEST and PROVISIONED with the existing capacity structure. -- Enable encryption: always specify `server_side_encryption { enabled = true kms_key_arn = .arn }`. -- Conditional features (TTL, PITR, streams) are controlled by variables; replicate this approach for new features. - -## Autoscaling -- Use `for_each` with `toset(var.ddb_table_names)` or derived maps; replicate naming pattern for policies. -- Target tracking metric type should match read/write capacity utilization; keep target at `70` unless data-driven change requested. - -## Event Source Mappings -- Derive lambda ARN via locals; avoid duplicating account ID retrieval. -- Set batching window conditional by environment (`dev` = 0 else 60). -- Use filter criteria with jsonencode pattern like existing `event_source.tf`. - -## S3 Buckets -- Must set: versioning, encryption (KMS), public access block, lifecycle only for non-production (count trick `local.production ? 0 : 1`). -- Enforce SSL with a bucket policy using the `AllowSSLRequestsOnly` statement pattern. - -## CodeBuild / CodePipeline -- Environment variable injection should use interpolation of pipeline variables like shown (`#{variables.TerraformStack}`). -- Buildspec paths live under `terraform/deployment/scripts/`. -- Log groups names follow `/eps-storage-${local.environment}-codebuild/`. -- Use `execution_mode = "PARALLEL"` and include dynamic Approval stage only for non-dev envs. - -## Module Usage -- If adding a reusable construct, place it under `terraform/modules//` and accept inputs rather than hardcoding environment. Provide example usage in a stack. -- Keep modules small, single responsibility (e.g., backup-destination vs backup-source separation). - -## Workspaces & Environments -- Never assume workspace names beyond matching `live*` for production detection. -- Place per-env config in `env/*.tfvars.json` files; do not create new env-specific `.tf` logic. - -## Formatting & Style -- Indent with two spaces. -- Keep attribute ordering: identifiers (name/bucket/etc) first, then settings, then nested blocks, then `tags` last. -- Use snake_case for variable names; hyphen-separated for resource names. -- Use `jsonencode({...})` for inline JSON to prevent quoting mistakes. - -## tfsec & Security Exceptions -- Annotate intentional rule suppressions with inline comments directly above the resource (`# tfsec:ignore:`). -- Don't add new ignores without reason; if required, add short justification after the rule id. - -## Avoid -- Randomness (`random_id`, etc.) unless collision avoidance is critical and approved. -- Hardcoding account IDs, environment names, or ARNs (derive via data sources / locals). -- Unbounded resource growth (autoscaling caps required via variables). - -## When Copilot Generates Code -1. Include the standard locals and provider blocks for new stack folders. -2. Reuse existing variable patterns; if unsure, look at the closest analogous file. -3. Prefer `for_each` over `count` when keys are meaningful (use `count` only for conditional single resources). -4. Suggest feature toggles (bool vars) for optional capabilities. -5. Ensure encryption and logging defaults are present. - -## Review Checklist (Copilot Internal) -Before finalizing a completion: -- Are all new resources tagged? (Either via default_tags or inline Name) -- Are environment/account references dynamic? -- Are optional features behind variables? -- Are IAM/KMS policies least-privilege following patterns? -- Is formatting consistent (2 spaces, block ordering)? - -## Example Minimal New Stack Skeleton + +### Bad Example - Hardcoding Values + ```hcl -# terraform/newstack/locals.tf -locals { /* standard locals as above */ } +resource "aws_s3_bucket" "archive" { + bucket = "my-hardcoded-bucket-name" + ... +} +``` -data "aws_caller_identity" "current" {} +## Security -# terraform/newstack/provider.tf -terraform { /* standard required_providers and required_version */ } -provider "aws" { /* region + allowed_account_ids + default_tags */ } -terraform { backend "s3" {} } +- 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. -# terraform/newstack/variables.tf -variable "allowed_account_ids" { type = list(string) } -``` +## Performance -## Extending This Guide -Raise a PR to modify this file for any new patterns. Don't drift without updating instructions. Keep the frontmatter `languages` array if adding multi-language guidance. +- 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/) From 150a2067737432dfd46571a2e30ccc9fe45276a1 Mon Sep 17 00:00:00 2001 From: Anthony Brown <121869075+anthony-nhs@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:15:44 +0000 Subject: [PATCH 05/15] update python --- .../languages/INSTRUCTIONS-PYTHON.md | 139 ++++++++++-------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/.github/instructions/languages/INSTRUCTIONS-PYTHON.md b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md index 85dbfba..be43352 100644 --- a/.github/instructions/languages/INSTRUCTIONS-PYTHON.md +++ b/.github/instructions/languages/INSTRUCTIONS-PYTHON.md @@ -1,73 +1,92 @@ -# Copilot Instructions for Python Files in `packages/slackBotFunction` +--- +description: 'Guidelines for writing high-quality, maintainable python code with best practices for logging, error handling, code organization, naming, formatting, and style.' +applyTo: '**/*.py' +--- -## Purpose -These instructions guide GitHub Copilot to generate high-quality, maintainable Python code, with modular architecture, service layers, and test coverage. +# Python Copilot Instructions -## General Guidelines -- Use Python 3.9+ syntax and typing. -- Follow PEP8 for formatting and naming conventions. -- Prefer explicit imports and avoid wildcard imports. -- Use docstrings for all public functions, classes, and modules. -- Organize code into logical modules: `core`, `services`, `utils`. -- Write modular, testable code with clear separation of concerns. -- Use dependency injection for services and external integrations. -- Handle exceptions gracefully and log errors using the standard `logging` module. -- NEVER hardcode secrets or configuration; use environment variables or config files. +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. -## File/Folder Specific Instructions -### `app/handler.py` -- Main entry point for AWS Lambda handler. -- Validate and parse incoming Slack events. -- Delegate business logic to service or core modules. -- Return AWS Lambda-compatible response objects. +## 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. -### `app/core/` -- Implement core business logic and reusable components. -- Avoid direct dependencies on Slack or AWS SDKs. -- Write pure functions where possible. +## 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. -### `app/services/` -- Integrate with external APIs (e.g., Bedrock, DynamoDB). -- Use abstraction layers for AWS services. -- Mock external dependencies in tests. +## 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. -### `app/utils/` -- Provide utility functions for common tasks (e.g., string manipulation, config loading). -- Keep utilities stateless and reusable. +## 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) + ``` -### `tests/` -- Use `pytest` for all tests. -- Name test files and functions descriptively (e.g., `test_handler_utils.py`, `test_forward_to_lambda`). -- Mock AWS and Slack APIs using `pytest-mock` or `unittest.mock`. -- Ensure coverage for error cases and edge conditions. +## 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 + ``` -## Best Practices -- Use type hints and `Optional` where appropriate. -- Prefer f-strings for string formatting. -- Use context managers for resource handling. -- Write unit tests for all new code and maintain high coverage. -- Document public APIs and expected input/output formats. -- Avoid global state; prefer passing dependencies explicitly. +## 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. -## Example Docstring -""" -Handles incoming Slack event and routes to appropriate service. -Args: - event (dict): Slack event payload. - context (LambdaContext): AWS Lambda context object. -Returns: - dict: AWS Lambda response object. -Raises: - ValueError: If event payload is invalid. -""" +## 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. -## Prohibited Patterns -- No direct AWS credentials or secrets in code. -- No print statements for logging (use `logging` instead). -- No business logic in handler files; delegate to services/core. +## 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. -## References -- [PEP8](https://peps.python.org/pep-0008/) -- [pytest Documentation](https://docs.pytest.org/en/stable/) +## 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. --- + From d0f60bed82c1b2e4fd39b02a1d44fbaaf689cb2c Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 16:08:58 +0100 Subject: [PATCH 06/15] fix it --- .../chatmodes/create_instructions.chatmode.md | 22 -- .github/copilot-instructions.md | 27 +- .../{SECURITY.md => security.instructions.md} | 2 +- .../instructions.intstructions.md | 257 ------------------ .../languages/INSTRUCTIONS-JAVA.md | 165 ----------- .../languages/INSTRUCTIONS-KOTLIN.md | 116 -------- ...NSTRUCTIONS-CDK.md => cdk.instructions.md} | 2 +- ...TION.md => cloudformation.instructions.md} | 5 +- ...TIONS-PYTHON.md => python.instructions.md} | 1 - ...NSTRUCTIONS-SAM.md => sam.instructions.md} | 13 +- ...TERRAFORM.md => terraform.instructions.md} | 0 ...PESCRIPT.md => typescript.instructions.md} | 35 ++- .github/instructions/project/instructions.md | 5 + .github/prompts/code_review.prompt.md | 59 ++++ 14 files changed, 112 insertions(+), 597 deletions(-) delete mode 100644 .github/chatmodes/create_instructions.chatmode.md rename .github/instructions/general/{SECURITY.md => security.instructions.md} (99%) delete mode 100644 .github/instructions/instructions.intstructions.md delete mode 100644 .github/instructions/languages/INSTRUCTIONS-JAVA.md delete mode 100644 .github/instructions/languages/INSTRUCTIONS-KOTLIN.md rename .github/instructions/languages/{INSTRUCTIONS-CDK.md => cdk.instructions.md} (98%) rename .github/instructions/languages/{INSTRUCTIONS-CLOUDFORMATION.md => cloudformation.instructions.md} (97%) rename .github/instructions/languages/{INSTRUCTIONS-PYTHON.md => python.instructions.md} (99%) rename .github/instructions/languages/{INSTRUCTIONS-SAM.md => sam.instructions.md} (94%) rename .github/instructions/languages/{INSTRUCTIONS-TERRAFORM.md => terraform.instructions.md} (100%) rename .github/instructions/languages/{INSTRUCTIONS-TYPESCRIPT.md => typescript.instructions.md} (73%) create mode 100644 .github/instructions/project/instructions.md create mode 100644 .github/prompts/code_review.prompt.md diff --git a/.github/chatmodes/create_instructions.chatmode.md b/.github/chatmodes/create_instructions.chatmode.md deleted file mode 100644 index 924f207..0000000 --- a/.github/chatmodes/create_instructions.chatmode.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -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 index e85b702..5f48b1b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,20 +3,19 @@ - Write comprehensive tests - Use meaningful variable names +## Project-Specific instructions +Check the following files for any project-specific coding standards or guidelines: +- .github/instructions/project/instructions.md +- If no project-specific conventions are defined there, use the general and language-specific best practices referenced below. +- Language-specific instructions may also be found in the language-specific instruction files listed below. Always check those for any additional guidelines or standards that may apply to your codebase. + ## Language-Specific Instructions Always follow security best practices as outlined in: -- .github/instructions/general/SECURITY.md +- .github/instructions/general/security.instructions.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 +- .github/instructions/languages/cdk.instructions.md +- .github/instructions/languages/cloudformation.instructions.md +- .github/instructions/languages/python.instructions.md +- .github/instructions/languages/terraform.instructions.md +- .github/instructions/languages/sam.instructions.md +- .github/instructions/languages/typescript.instructions.md diff --git a/.github/instructions/general/SECURITY.md b/.github/instructions/general/security.instructions.md similarity index 99% rename from .github/instructions/general/SECURITY.md rename to .github/instructions/general/security.instructions.md index 53a7a62..c4fb7ea 100644 --- a/.github/instructions/general/SECURITY.md +++ b/.github/instructions/general/security.instructions.md @@ -1,5 +1,5 @@ --- -applyTo: '*' +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 diff --git a/.github/instructions/instructions.intstructions.md b/.github/instructions/instructions.intstructions.md deleted file mode 100644 index bcdc46c..0000000 --- a/.github/instructions/instructions.intstructions.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -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-JAVA.md b/.github/instructions/languages/INSTRUCTIONS-JAVA.md deleted file mode 100644 index 5205f73..0000000 --- a/.github/instructions/languages/INSTRUCTIONS-JAVA.md +++ /dev/null @@ -1,165 +0,0 @@ -# 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 deleted file mode 100644 index b21a37d..0000000 --- a/.github/instructions/languages/INSTRUCTIONS-KOTLIN.md +++ /dev/null @@ -1,116 +0,0 @@ -# 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-CDK.md b/.github/instructions/languages/cdk.instructions.md similarity index 98% rename from .github/instructions/languages/INSTRUCTIONS-CDK.md rename to .github/instructions/languages/cdk.instructions.md index cbff4e5..213dd6f 100644 --- a/.github/instructions/languages/INSTRUCTIONS-CDK.md +++ b/.github/instructions/languages/cdk.instructions.md @@ -90,7 +90,7 @@ export class CptsApiAppStack extends Stack { ## Validation and Verification - Build: `make cdk-synth` -- Lint: `npm run lint --workspace packges/cdk` +- Lint: `npm run lint --workspace packages/cdk` ## Maintenance diff --git a/.github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md b/.github/instructions/languages/cloudformation.instructions.md similarity index 97% rename from .github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md rename to .github/instructions/languages/cloudformation.instructions.md index 3c5ca1e..b33c7c8 100644 --- a/.github/instructions/languages/INSTRUCTIONS-CLOUDFORMATION.md +++ b/.github/instructions/languages/cloudformation.instructions.md @@ -1,6 +1,5 @@ -# Copilot instructions for CloudFormation YAML in this repo --- -description: 'Brief description of the instruction purpose and scope' +description: 'Guidelines for writing, reviewing, and maintaining cloudformation templates' applyTo: 'cloudformation/**' --- @@ -106,4 +105,4 @@ applyTo: 'cloudformation/**' - 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 +Use these rules to guide completions for any new or modified CloudFormation template in this repository. diff --git a/.github/instructions/languages/INSTRUCTIONS-PYTHON.md b/.github/instructions/languages/python.instructions.md similarity index 99% rename from .github/instructions/languages/INSTRUCTIONS-PYTHON.md rename to .github/instructions/languages/python.instructions.md index be43352..37388dc 100644 --- a/.github/instructions/languages/INSTRUCTIONS-PYTHON.md +++ b/.github/instructions/languages/python.instructions.md @@ -89,4 +89,3 @@ These instructions are designed to guide GitHub Copilot in generating effective, - Avoid global variables; use function arguments or class attributes. --- - diff --git a/.github/instructions/languages/INSTRUCTIONS-SAM.md b/.github/instructions/languages/sam.instructions.md similarity index 94% rename from .github/instructions/languages/INSTRUCTIONS-SAM.md rename to .github/instructions/languages/sam.instructions.md index e8b1b90..60e8089 100644 --- a/.github/instructions/languages/INSTRUCTIONS-SAM.md +++ b/.github/instructions/languages/sam.instructions.md @@ -1,6 +1,5 @@ -# SAM Template Instructions for GitHub Copilot --- -description: 'Brief description of the instruction purpose and scope' +description: 'Guidelines for writing, reviewing, and maintaining SAM templates' applyTo: 'SAMtemplates/**' --- ## Scope @@ -28,7 +27,7 @@ This is a healthcare API service deployed using AWS SAM (Serverless Application ### Standard Parameters Always include these common parameters in templates: ```yaml -Parameters +Parameters: StackName: Type: String Default: none @@ -142,7 +141,7 @@ MutualTlsAuthentication: - 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 +- Include guard rules suppression only where necessary. By default these should not be added. If they are added an explanation should be included to say why we are overriding them ### Environment Variables and Secrets ```yaml @@ -188,12 +187,12 @@ Environment: 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 +8. **Build Methods**: Use esbuild for Node.js Lambda functions +9. **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 +When generating CloudFormation/SAM templates, follow these patterns and ensure compliance with NHS Digital standards and AWS security best practices. diff --git a/.github/instructions/languages/INSTRUCTIONS-TERRAFORM.md b/.github/instructions/languages/terraform.instructions.md similarity index 100% rename from .github/instructions/languages/INSTRUCTIONS-TERRAFORM.md rename to .github/instructions/languages/terraform.instructions.md diff --git a/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md b/.github/instructions/languages/typescript.instructions.md similarity index 73% rename from .github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md rename to .github/instructions/languages/typescript.instructions.md index 464e82e..d3b8ba1 100644 --- a/.github/instructions/languages/INSTRUCTIONS-TYPESCRIPT.md +++ b/.github/instructions/languages/typescript.instructions.md @@ -1,6 +1,6 @@ --- 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' +applyTo: '**/*.{ts,tsx}' --- # TypeScript Development Guidelines @@ -43,8 +43,9 @@ This document provides instructions for generating, reviewing, and maintaining T - 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. +- Never use semicolons for line termination. +- Avoid trailing commas in multiline objects and arrays. +- Avoid spaces at start and end of single line braces. - Use ESLint and Prettier for consistent formatting. ## Architecture/Structure @@ -64,10 +65,10 @@ This document provides instructions for generating, reviewing, and maintaining T - Example: ```typescript - import { logger } from './utils/logger'; + import {logger} from './utils/logger'; - logger.info('Fetching user data', { userId }); - logger.error('Failed to fetch user', { error }); + logger.info('Fetching user data', {userId}); + logger.error('Failed to fetch user', {error}); ``` ### Error Handling @@ -81,14 +82,14 @@ This document provides instructions for generating, reviewing, and maintaining T try { const result = await fetchData(); } catch (error) { - logger.error('Data fetch failed', { error }); + logger.error('Data fetch failed', {error}); throw new DataFetchError('Unable to fetch data'); } ``` ### Type Safety -- Prefer interfaces and types over `any`. +- Prefer interfaces and types. You MUST NOT use `any`. - Use type guards and assertions when necessary. - Example: @@ -98,7 +99,7 @@ This document provides instructions for generating, reviewing, and maintaining T name: string; } - function isUser(obj: any): obj is User { + function isUser(obj: object): obj is User { return typeof obj.id === 'string' && typeof obj.name === 'string'; } ``` @@ -119,7 +120,7 @@ This document provides instructions for generating, reviewing, and maintaining T ## Testing - Write unit tests for all business logic. -- Use Jest or similar frameworks for testing. +- Use the existing framework for testing and vitest for new packages. - Mock external dependencies in tests. - Example test file structure: @@ -130,6 +131,20 @@ This document provides instructions for generating, reviewing, and maintaining T handler.test.ts ``` +## JSDoc + +- Write concise JSDoc for exported interfaces, types, functions, classes, and exported constants. +- Prefer short phrase-style summaries; avoid long narrative prose. +- Avoid stating information that is obvious from function signatures. +- Consider @param and @returns for every exported function, then include them only when they add meaning not obvious from the signature. +- Skip @param when it only repeats parameter name/type; keep it when documenting constraints, defaults, units, side effects, or domain context. +- It is acceptable to use only @returns in a JSDoc block when that tag carries all useful context. +- Omit a free-text summary line when it only restates the @returns content. +- Provide @example on constructors of exported types/classes and on non-trivial exported types. +- Use @default only when the property is optional in the type and is defaulted in implementation. +- Keep JSDoc defaults aligned with both type signatures and runtime behaviour. +- For construct props interfaces, include a top-level summary and property docs only when intent is non-obvious. + ## Examples and Code Snippets ### Good Example diff --git a/.github/instructions/project/instructions.md b/.github/instructions/project/instructions.md new file mode 100644 index 0000000..039874f --- /dev/null +++ b/.github/instructions/project/instructions.md @@ -0,0 +1,5 @@ +# EPS Copilot instructions + +## Overview +This contains copilot instructions for all EPS repos, and an action to sync these to all repos + diff --git a/.github/prompts/code_review.prompt.md b/.github/prompts/code_review.prompt.md new file mode 100644 index 0000000..6e544aa --- /dev/null +++ b/.github/prompts/code_review.prompt.md @@ -0,0 +1,59 @@ +--- +description: "Perform a comprehensive code review" +--- + +## Role + +You're a senior software engineer conducting a thorough code review. Provide constructive, actionable feedback. + +## Review Areas + +Analyze the selected code for: + +1. **Security Issues** + - Input validation and sanitization + - Authentication and authorization + - Data exposure risks + - Injection vulnerabilities + +2. **Performance & Efficiency** + - Algorithm complexity + - Memory usage patterns + - Database query optimization + - Unnecessary computations + +3. **Code Quality** + - Readability and maintainability + - Proper naming conventions + - Function/class size and responsibility + - Code duplication + +4. **Architecture & Design** + - Design pattern usage + - Separation of concerns + - Dependency management + - Error handling strategy + +5. **Testing & Documentation** + - Test coverage and quality + - Documentation completeness + - Comment clarity and necessity + +## Output Format + +Provide feedback as: + +**🔴 Critical Issues** - Must fix before merge +**🟡 Suggestions** - Improvements to consider +**✅ Good Practices** - What's done well + +For each issue: + +- Specific line references +- Clear explanation of the problem +- Suggested solution with code example +- Rationale for the change + +Focus on: ${input:focus:Any specific areas to emphasize in the review?} + +Be constructive and educational in your feedback. From fd4b2194adc25c5810af115ef31d91b6ad1223e3 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:22:00 +0000 Subject: [PATCH 07/15] create action --- .devcontainer/Dockerfile | 14 ++++++ .devcontainer/devcontainer.json | 48 +++++++++++++++++++++ .github/workflows/ci.yml | 44 +++++++++++++++++++ .github/workflows/pull_request.yml | 44 +++++++++++++++++++ .github/workflows/release.yml | 44 +++++++++++++++++++ README.md | 4 +- action.yml | 69 ++++++++++++++++++++++++++++++ 7 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pull_request.yml create mode 100644 .github/workflows/release.yml create mode 100644 action.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..3903c81 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,14 @@ +ARG IMAGE_NAME=node_24_python_3_14 +ARG IMAGE_VERSION=latest +FROM ghcr.io/nhsdigital/eps-devcontainers/${IMAGE_NAME}:${IMAGE_VERSION} + +USER root +# specify DOCKER_GID to force container docker group id to match host +RUN if [ -n "${DOCKER_GID}" ]; then \ + if ! getent group docker; then \ + groupadd -g ${DOCKER_GID} docker; \ + else \ + groupmod -g ${DOCKER_GID} docker; \ + fi && \ + usermod -aG docker vscode; \ + fi diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..fce5a4d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,48 @@ +{ + "name": "eps-copilot-instructions", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + "DOCKER_GID": "${env:DOCKER_GID:}", + "IMAGE_NAME": "node_24_python_3_14", + "IMAGE_VERSION": "v1.2.0", + "USER_UID": "${localEnv:USER_ID:}", + "USER_GID": "${localEnv:GROUP_ID:}" + }, + "updateRemoteUserUID": false + }, + "postAttachCommand": "git-secrets --register-aws; git-secrets --add-provider -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt", + "mounts": [ + "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.gnupg,target=/home/vscode/.gnupg,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.npmrc,target=/home/vscode/.npmrc,type=bind" + ], + "containerUser": "vscode", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "features": {}, + "customizations": { + "vscode": { + "extensions": [ + "AmazonWebServices.aws-toolkit-vscode", + "redhat.vscode-yaml", + "eamodio.gitlens", + "github.vscode-pull-request-github", + "streetsidesoftware.code-spell-checker", + "timonwong.shellcheck", + "github.vscode-github-actions" + ], + "settings": { + "cSpell.words": [ + "fhir", + "Formik", + "pino", + "serialisation" + ] + } + } + } +} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..758079c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: pull_request + +on: + pull_request: + branches: [main] + +env: + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + +jobs: + get_config_values: + uses: NHSDigital/eps-common-workflows/.github/workflows/get-repo-config.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + with: + verify_published_from_main_image: false + + dependabot-auto-approve-and-merge: + uses: NHSDigital/eps-common-workflows/.github/workflows/dependabot-auto-approve-and-merge.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + secrets: + AUTOMERGE_APP_ID: ${{ secrets.AUTOMERGE_APP_ID }} + AUTOMERGE_PEM: ${{ secrets.AUTOMERGE_PEM }} + + quality_checks: + uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + needs: [get_config_values] + with: + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + pr_title_format_check: + uses: NHSDigital/eps-common-workflows/.github/workflows/pr_title_check.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + + tag_release: + needs: [get_config_values] + uses: NHSDigital/eps-common-workflows/.github/workflows/tag-release-devcontainer.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + permissions: + id-token: "write" + contents: "write" + with: + dry_run: true + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + branch_name: ${{ github.event.pull_request.head.ref }} + tag_format: ${{ needs.get_config_values.outputs.tag_format }} + secrets: inherit diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000..758079c --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,44 @@ +name: pull_request + +on: + pull_request: + branches: [main] + +env: + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + +jobs: + get_config_values: + uses: NHSDigital/eps-common-workflows/.github/workflows/get-repo-config.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + with: + verify_published_from_main_image: false + + dependabot-auto-approve-and-merge: + uses: NHSDigital/eps-common-workflows/.github/workflows/dependabot-auto-approve-and-merge.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + secrets: + AUTOMERGE_APP_ID: ${{ secrets.AUTOMERGE_APP_ID }} + AUTOMERGE_PEM: ${{ secrets.AUTOMERGE_PEM }} + + quality_checks: + uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + needs: [get_config_values] + with: + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + pr_title_format_check: + uses: NHSDigital/eps-common-workflows/.github/workflows/pr_title_check.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + + tag_release: + needs: [get_config_values] + uses: NHSDigital/eps-common-workflows/.github/workflows/tag-release-devcontainer.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + permissions: + id-token: "write" + contents: "write" + with: + dry_run: true + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + branch_name: ${{ github.event.pull_request.head.ref }} + tag_format: ${{ needs.get_config_values.outputs.tag_format }} + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2cc85b4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,44 @@ +name: pull_request + +on: + pull_request: + branches: [main] + +env: + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + +jobs: + get_config_values: + uses: NHSDigital/eps-common-workflows/.github/workflows/get-repo-config.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + with: + verify_published_from_main_image: false + + dependabot-auto-approve-and-merge: + uses: NHSDigital/eps-common-workflows/.github/workflows/dependabot-auto-approve-and-merge.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + secrets: + AUTOMERGE_APP_ID: ${{ secrets.AUTOMERGE_APP_ID }} + AUTOMERGE_PEM: ${{ secrets.AUTOMERGE_PEM }} + + quality_checks: + uses: NHSDigital/eps-common-workflows/.github/workflows/quality-checks-devcontainer.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + needs: [get_config_values] + with: + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + pr_title_format_check: + uses: NHSDigital/eps-common-workflows/.github/workflows/pr_title_check.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + + tag_release: + needs: [get_config_values] + uses: NHSDigital/eps-common-workflows/.github/workflows/tag-release-devcontainer.yml@f3d19a678a725917a5c59cae4d76db621bb7c9c7 + permissions: + id-token: "write" + contents: "write" + with: + dry_run: false + pinned_image: ${{ needs.get_config_values.outputs.pinned_image }} + branch_name: ${{ github.event.pull_request.head.ref }} + tag_format: ${{ needs.get_config_values.outputs.tag_format }} + secrets: inherit diff --git a/README.md b/README.md index f0f7e69..d1b3cb1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # EPS Copilot Instructions -This project contains copilot instruction files that can be copied to each EPS repo as an initial instructions. +This project contains copilot instruction files that are copied to each EPS repo as an initial instructions. -It also contains language specific instruction files that are kept in sync in each EPS project using git submodules +It also contains an action that are used to cepy the files to each repo \ No newline at end of file diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..a8fdf0e --- /dev/null +++ b/action.yml @@ -0,0 +1,69 @@ +name: Sync Copilot Instructions +description: "Syncs Copilot instructions from the repository to the GitHub Action's metadata file." +inputs: + common_workflows_ref: + description: "The ref to sync from the central repository" + required: false + default: "main" + type: string + calling_repo_base_branch: + description: "The base branch from the calling repository that should be merged into" + required: false + type: string + default: main +runs: + using: "composite" + steps: + - name: Checkout calling repo code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ inputs.calling_repo_base_branch }} + fetch-depth: 0 + + - name: Checkout central repo code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ inputs.common_workflows_ref }} + fetch-depth: 0 + path: eps-common-workflows + repository: NHSDigital/eps-common-workflows + sparse-checkout: | + .github/instructions/general + .github/instructions/languages + .github/copilot-instructions.md + .github/prompts + + - name: Copy central instructions + run: | + rm -rf .github/instructions/general + rm -rf .github/instructions/languages + rm -rf .github/copilot-instructions.md + rm -rf .github/prompts + mkdir -p .github/instructions/ + cp -R eps-common-workflows/.github/instructions/general .github/instructions/general + cp -R eps-common-workflows/.github/instructions/languages .github/instructions/languages + cp eps-common-workflows/.github/copilot-instructions.md .github/copilot-instructions.md + cp -R eps-common-workflows/.github/prompts .github/prompts + rm -rf eps-common-workflows + + - name: Create GitHub App Token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 + id: generate-token + with: + app-id: "${{ secrets.CREATE_PULL_REQUEST_APP_ID }}" + private-key: "${{ secrets.CREATE_PULL_REQUEST_PEM }}" + + - name: Create Pull Request + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 + with: + token: "${{ steps.generate-token.outputs.token }}" + commit-message: "Upgrade: [dependabot] - sync Copilot instructions" + title: "Upgrade: [dependabot] - sync Copilot instructions" + body: | + Syncing Copilot instructions from central repo. + Ref: `${{ inputs.common_workflows_ref }}` + branch: copilot-instructions-sync + base: ${{ inputs.calling_repo_base_branch }} + branch-suffix: random + sign-commits: true + delete-branch: true From cd9583840da172835f479e948c867dd7c20f0020 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:23:27 +0000 Subject: [PATCH 08/15] add config --- .github/config/settings.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/config/settings.yml diff --git a/.github/config/settings.yml b/.github/config/settings.yml new file mode 100644 index 0000000..05dbcda --- /dev/null +++ b/.github/config/settings.yml @@ -0,0 +1 @@ +TAG_FORMAT: "v${version}" From 0ff6443437875153a1b09e2ae610856c6dc3c527 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:24:53 +0000 Subject: [PATCH 09/15] use common makefile targets --- Makefile | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ef8138a --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +%: + @$(MAKE) -f /usr/local/share/eps/Mk/common.mk $@ From af125e40f1699daf55b4cfd7c1230a0602d1f094 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:26:43 +0000 Subject: [PATCH 10/15] fix workflows --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 758079c..15ae899 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ name: pull_request on: - pull_request: + push: branches: [main] env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2cc85b4..6c372c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,9 @@ name: pull_request on: - pull_request: - branches: [main] + workflow_dispatch: + schedule: + - cron: "0 8 * * 3" env: BRANCH_NAME: ${{ github.event.pull_request.head.ref }} From 8fc4cff858bf945a91919e54566026210982d259 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:28:34 +0000 Subject: [PATCH 11/15] add makefile targets --- Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Makefile b/Makefile index ef8138a..9742c66 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,13 @@ +.PHONY: install install-node compile lint test +install: + echo "Nothing to install" +install-node: + echo "Nothing to install" +compile: + echo "Nothing to compile" +lint: + echo "Nothing to lint" +test: + echo "Nothing to test" %: @$(MAKE) -f /usr/local/share/eps/Mk/common.mk $@ From 419493e04280eeda58651a5761afa10f48938d9f Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:36:56 +0000 Subject: [PATCH 12/15] update it --- action.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/action.yml b/action.yml index a8fdf0e..8733953 100644 --- a/action.yml +++ b/action.yml @@ -5,11 +5,9 @@ inputs: description: "The ref to sync from the central repository" required: false default: "main" - type: string calling_repo_base_branch: description: "The base branch from the calling repository that should be merged into" required: false - type: string default: main runs: using: "composite" From 72384bfa3518d1cf4041ff38365168e6cda88de8 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:39:21 +0000 Subject: [PATCH 13/15] update it --- action.yml | 105 ++++++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/action.yml b/action.yml index 8733953..6fb166b 100644 --- a/action.yml +++ b/action.yml @@ -9,59 +9,66 @@ inputs: description: "The base branch from the calling repository that should be merged into" required: false default: main + CREATE_PULL_REQUEST_APP_ID: + description: "GitHub App ID for creating pull requests" + required: true + CREATE_PULL_REQUEST_PEM: + description: "Private key for the GitHub App in PEM format" + required: true runs: using: "composite" steps: - - name: Checkout calling repo code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - ref: ${{ inputs.calling_repo_base_branch }} - fetch-depth: 0 + - name: Checkout calling repo code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ inputs.calling_repo_base_branch }} + fetch-depth: 0 - - name: Checkout central repo code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - with: - ref: ${{ inputs.common_workflows_ref }} - fetch-depth: 0 - path: eps-common-workflows - repository: NHSDigital/eps-common-workflows - sparse-checkout: | - .github/instructions/general - .github/instructions/languages - .github/copilot-instructions.md - .github/prompts + - name: Checkout central repo code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ inputs.common_workflows_ref }} + fetch-depth: 0 + path: eps-common-workflows + repository: NHSDigital/eps-common-workflows + sparse-checkout: | + .github/instructions/general + .github/instructions/languages + .github/copilot-instructions.md + .github/prompts - - name: Copy central instructions - run: | - rm -rf .github/instructions/general - rm -rf .github/instructions/languages - rm -rf .github/copilot-instructions.md - rm -rf .github/prompts - mkdir -p .github/instructions/ - cp -R eps-common-workflows/.github/instructions/general .github/instructions/general - cp -R eps-common-workflows/.github/instructions/languages .github/instructions/languages - cp eps-common-workflows/.github/copilot-instructions.md .github/copilot-instructions.md - cp -R eps-common-workflows/.github/prompts .github/prompts - rm -rf eps-common-workflows + - name: Copy central instructions + shell: bash + run: | + rm -rf .github/instructions/general + rm -rf .github/instructions/languages + rm -rf .github/copilot-instructions.md + rm -rf .github/prompts + mkdir -p .github/instructions/ + cp -R eps-common-workflows/.github/instructions/general .github/instructions/general + cp -R eps-common-workflows/.github/instructions/languages .github/instructions/languages + cp eps-common-workflows/.github/copilot-instructions.md .github/copilot-instructions.md + cp -R eps-common-workflows/.github/prompts .github/prompts + rm -rf eps-common-workflows - - name: Create GitHub App Token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 - id: generate-token - with: - app-id: "${{ secrets.CREATE_PULL_REQUEST_APP_ID }}" - private-key: "${{ secrets.CREATE_PULL_REQUEST_PEM }}" + - name: Create GitHub App Token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 + id: generate-token + with: + app-id: "${{ inputs.CREATE_PULL_REQUEST_APP_ID }}" + private-key: "${{ inputs.CREATE_PULL_REQUEST_PEM }}" - - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 - with: - token: "${{ steps.generate-token.outputs.token }}" - commit-message: "Upgrade: [dependabot] - sync Copilot instructions" - title: "Upgrade: [dependabot] - sync Copilot instructions" - body: | - Syncing Copilot instructions from central repo. - Ref: `${{ inputs.common_workflows_ref }}` - branch: copilot-instructions-sync - base: ${{ inputs.calling_repo_base_branch }} - branch-suffix: random - sign-commits: true - delete-branch: true + - name: Create Pull Request + uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 + with: + token: "${{ steps.generate-token.outputs.token }}" + commit-message: "Upgrade: [dependabot] - sync Copilot instructions" + title: "Upgrade: [dependabot] - sync Copilot instructions" + body: | + Syncing Copilot instructions from central repo. + Ref: `${{ inputs.common_workflows_ref }}` + branch: copilot-instructions-sync + base: ${{ inputs.calling_repo_base_branch }} + branch-suffix: random + sign-commits: true + delete-branch: true From b18a2c3bb86c9b14087faab7e7df786c3cad9b49 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:44:18 +0000 Subject: [PATCH 14/15] better readme --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d1b3cb1..c15e408 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,91 @@ # EPS Copilot Instructions -This project contains copilot instruction files that are copied to each EPS repo as an initial instructions. +This repository contains the shared GitHub Copilot instruction files used across EPS repositories, along with a composite GitHub Action that copies those files into a target repository and opens a pull request with the changes. -It also contains an action that are used to cepy the files to each repo \ No newline at end of file +## What this repository contains + +- Shared instruction files under `.github/instructions` +- A shared `.github/copilot-instructions.md` +- Shared prompts under `.github/prompts` +- A composite GitHub Action defined in `action.yml` + +## Action overview + +The action is intended to be called from another repository. It: + +1. Checks out the calling repository at the requested base branch +2. Checks out the central source repository at the requested ref +3. Replaces the target repository's Copilot instruction files with the shared versions +4. Creates a signed pull request containing the sync changes + +The action currently syncs content from `NHSDigital/eps-common-workflows`. + +## Files synced by the action + +The action copies the following paths into the calling repository: + +- `.github/instructions/general` +- `.github/instructions/languages` +- `.github/copilot-instructions.md` +- `.github/prompts` + +Existing copies of those paths in the calling repository are removed before the new content is copied in. + +## Inputs + +| Input | Required | Default | Description | +| --- | --- | --- | --- | +| `common_workflows_ref` | No | `main` | Git ref to check out from the central source repository | +| `calling_repo_base_branch` | No | `main` | Base branch in the calling repository that the pull request should target | +| `CREATE_PULL_REQUEST_APP_ID` | Yes | None | GitHub App ID used to generate a token for pull request creation | +| `CREATE_PULL_REQUEST_PEM` | Yes | None | GitHub App private key in PEM format | + +## Example usage + +```yaml +name: Sync Copilot Instructions + +on: + workflow_dispatch: + schedule: + - cron: '0 6 * * 1' + +jobs: + sync-copilot-instructions: + runs-on: ubuntu-latest + environment: create_pull_request + permissions: + contents: write + pull-requests: write + + steps: + - name: Sync shared instructions + uses: NHSDigital/eps-copilot-instructions@95118f6746ca7081258cc7f651dca1c5bb7339f1 + with: + common_workflows_ref: main + calling_repo_base_branch: main + CREATE_PULL_REQUEST_APP_ID: ${{ secrets.CREATE_PULL_REQUEST_APP_ID }} + CREATE_PULL_REQUEST_PEM: ${{ secrets.CREATE_PULL_REQUEST_PEM }} +``` + +## Pull request behavior + +When changes are detected, the action creates a pull request with: + +- Branch prefix: `copilot-instructions-sync` +- Commit message: `Upgrade: [dependabot] - sync Copilot instructions` +- Pull request title: `Upgrade: [dependabot] - sync Copilot instructions` +- Signed commits enabled +- Automatic branch cleanup enabled + +The pull request body includes the ref used for the sync. + +## Prerequisites + +Before using the action in a repository, ensure that https://github.com/NHSDigital/electronic-prescription-service-account-resources/blob/main/scripts/set_github_secrets.py has been run to create the environment and secrets + +## Notes + +- The action is implemented as a composite action in `action.yml` +- It uses pinned action SHAs for its external action dependencies +- The action opens a pull request instead of pushing directly to the base branch \ No newline at end of file From e32bb1d28b2672c923695363e5173dffee7b8c05 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Mon, 30 Mar 2026 15:49:57 +0000 Subject: [PATCH 15/15] better --- .github/dependabot.yml | 57 ++++++++++++++++++++++++++++++ .github/pull_request_template.md | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..69c719e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,57 @@ +######################################################################### +# Dependabot configuration file +######################################################################### + +version: 2 +registries: + npm-github: + type: npm-registry + url: https://npm.pkg.github.com + token: ${{secrets.DEPENDABOT_TOKEN}} + +updates: + - package-ecosystem: "github-actions" + # Workflow files stored in the + # default location of `.github/workflows` + directory: "/" + schedule: + interval: "weekly" + day: "friday" + time: "18:00" # UTC + open-pull-requests-limit: 20 + commit-message: + prefix: "Upgrade: [dependabot] - " + cooldown: + default-days: 3 + ################################### + # Poetry ######################### + ################################### + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "friday" + time: "20:00" # UTC + open-pull-requests-limit: 20 + commit-message: + prefix: "Upgrade: [dependabot] - " + versioning-strategy: increase + cooldown: + default-days: 3 + ################################### + # NPM workspace ################## + ################################### + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "friday" + time: "22:00" # UTC + open-pull-requests-limit: 20 + versioning-strategy: increase + commit-message: + prefix: "Upgrade: [dependabot] - " + registries: + - npm-github + cooldown: + default-days: 3 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..203df63 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,59 @@ +## Summary + +**Remove items from this list if they are not relevant. Remove this line once this has been done** + +- Routine Change +- :exclamation: Breaking Change +- :robot: Operational or Infrastructure Change +- :sparkles: New Feature +- :warning: Potential issues that might be caused by this change + +### Details + +Add any summary information of what is in the change. **Remove this line if you have nothing to add.** + +## Pull Request Naming + +Pull requests should be named using the following format: + +```text +Tag: [AEA-NNNN] - Short description +``` + +Tag can be one of: + +- `Fix` - for a bug fix. (Patch release) +- `Update` - either for a backwards-compatible enhancement or for a rule change that adds reported problems. (Patch release) +- `New` - implemented a new feature. (Minor release) +- `Breaking` - for a backwards-incompatible enhancement or feature. (Major release) +- `Docs` - changes to documentation only. (Patch release) +- `Build` - changes to build process only. (No release) +- `Upgrade` - for a dependency upgrade. (Patch release) +- `Chore` - for refactoring, adding tests, etc. (anything that isn't user-facing). (Patch release) + +If the current release is x.y.z then +- a patch release increases z by 1 +- a minor release increases y by 1 +- a major release increases x by 1 + +Correct tagging is necessary for our automated versioning and release process. + +The description of your pull request will be used as the commit message for the merge, and also be included in the changelog. Please ensure that your title is sufficiently descriptive. + +### Rerunning Checks + +If you need to rename your pull request, you can restart the checks by either: + +- Closing and reopening the pull request +- pushing an empty commit + ```bash + git commit --allow-empty -m 'trigger build' + git push + ``` +- Amend your last commit and force push to the branch + ```bash + git commit --amend --no-edit + git push --force + ``` + +Rerunning the checks from within the pull request will not use the updated title.