Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 3 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
<!--
Copyright 2025 Kyle J. Coder

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# GitHub Copilot Enterprise Setup Guide (VA)

Expand All @@ -29,17 +14,13 @@ Interactive Reveal.js slide deck and printable guide to help VA employees (clini
- `prompts/` – Role and domain-specific prompt template collections (clinical, analytics, security, etc.)
- `dependencies/` – Bundled Reveal.js assets and Apache 2.0 license

## Quick Start (GitHub Pages Hosting)
## Quick Start

1. Fork or clone this repository.
2. Enable GitHub Pages in repository settings: Source = `main` (root).
3. Visit: `https://<your-username>.github.io/GitHub-Copilot-Setup-Guide/`.
4. Open `index.html` for the interactive version. For printing/distribution, use the PDF at `docs/GitHub Copilot Setup Guide (for VA Employees).pdf`.
### Open [`index.html`](https://kcoderva.github.io/GitHub-Copilot-Setup-Guide/) for the interactive guide. OR, open [`docs/GitHub Copilot Setup Guide (for VA Employees).pdf`](https://github.com/KCoderVA/GitHub-Copilot-Setup-Guide/blob/main/docs/GitHub%20Copilot%20Setup%20Guide%20(for%20VA%20Employees).pdf) for a printable/distributable guide.

## Local Workspace Setup (High Priority)
## Local Workspace Setup (High Recommendation)

Run these steps in the workspace where you actually do your work so Copilot follows your local rules.

1) Download or clone this repository.
2) Open your own active VS Code workspace (the project you want Copilot to obey).
3) Run `copilot-instructions/Install-Copilot-Instructions.bat` from this repo in that workspace.
Expand All @@ -61,10 +42,6 @@ Why this matters:
- Reduces setup time for teammates; gives everyone the same guardrails and shortcuts
- Validates a healthy workspace structure quickly and non-destructively

## Printable Version

Use the "Printable Version" button in the top-right of `index.html` to open the PDF (`docs/GitHub Copilot Setup Guide (for VA Employees).pdf`).

## Automation & Scripts
All automation lives under `copilot-instructions/`.

Expand Down Expand Up @@ -111,8 +88,5 @@ Apache 2.0 – see `LICENSE`. Attribution required in derivatives. Embedded Reve
## Security & Compliance Reminder
No PHI/PII or sensitive VA data should be placed in this repository. All examples are generic. Follow local ISSO guidance before operationalizing any automation.

## Author
Kyle J. Coder – Edward Hines Jr. VA Hospital / Clinical Informatics / Advanced Analytics

---
*If you build a derivative focused on another VA role or specialty, please contribute a link back or open a PR so others can benefit.*
153 changes: 89 additions & 64 deletions copilot-instructions/Add-LicenseHeaders.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
Team: Clinical Informatics & Advanced Analytics
Email: Kyle.Coder@va.gov
Created: July 24, 2025
Version: 1.0.0
Version: 1.0.1

VA Compliance:
- No administrator privileges required
Expand All @@ -61,13 +61,22 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $false, HelpMessage = 'File extensions to process')]
[string]$FileTypes = 'ps1,sql,js,css,html',
[string]$FileTypes = 'ps1,sql,js,css,html,md,txt,bat,psm,json',

[Parameter(Mandatory = $false, HelpMessage = 'Skip files that already have headers')]
[switch]$SkipExisting,

[Parameter(Mandatory = $false, HelpMessage = 'Show changes without applying them')]
[switch]$DryRun
[switch]$DryRun,

# Metadata to inject into headers (auto-detected if omitted)
[Parameter(Mandatory = $false)] [string]$AuthorName,
[Parameter(Mandatory = $false)] [string]$Email,
[Parameter(Mandatory = $false)] [string]$Organization,
[Parameter(Mandatory = $false)] [string]$Team,
[Parameter(Mandatory = $false)] [string]$ProjectName,
[Parameter(Mandatory = $false)] [string]$LicenseType = 'Apache-2.0',
[Parameter(Mandatory = $false)] [string]$ComplianceNote = 'Internal Use Only'
)

# Script initialization
Expand All @@ -83,96 +92,105 @@ if (-not (Test-Path $LogPath)) {
New-Item -ItemType Directory -Path $LogPath -Force | Out-Null
}

# License header templates
# Helper: attempt to read Git config (if available)
function Get-GitConfigValue {
param([Parameter(Mandatory=$true)][string]$Key)
try {
$git = Get-Command git -ErrorAction SilentlyContinue
if ($null -eq $git) { return $null }
$val = git config --get $Key 2>$null
if ([string]::IsNullOrWhiteSpace($val)) { return $null }
return $val.Trim()
} catch { return $null }
}

# Auto-detect metadata if not provided
if (-not $AuthorName -or [string]::IsNullOrWhiteSpace($AuthorName)) { $AuthorName = (Get-GitConfigValue 'user.name'); if (-not $AuthorName) { $AuthorName = $env:USERNAME } }
if (-not $Email -or [string]::IsNullOrWhiteSpace($Email)) { $Email = (Get-GitConfigValue 'user.email'); if (-not $Email) { $Email = '' } }
if (-not $Organization) { $Organization = '' }
if (-not $Team) { $Team = '' }
if (-not $ProjectName -or [string]::IsNullOrWhiteSpace($ProjectName)) { $ProjectName = Split-Path -Leaf (Split-Path -Parent $ScriptPath) }
if (-not $LicenseType) { $LicenseType = 'Apache-2.0' }
if (-not $ComplianceNote) { $ComplianceNote = 'Internal Use Only' }
Comment on lines +107 to +114
Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These auto-detection lines are difficult to read and maintain due to complex nested conditionals on single lines. Consider refactoring into a helper function or breaking into multiple lines for better readability.

Suggested change
# Auto-detect metadata if not provided
if (-not $AuthorName -or [string]::IsNullOrWhiteSpace($AuthorName)) { $AuthorName = (Get-GitConfigValue 'user.name'); if (-not $AuthorName) { $AuthorName = $env:USERNAME } }
if (-not $Email -or [string]::IsNullOrWhiteSpace($Email)) { $Email = (Get-GitConfigValue 'user.email'); if (-not $Email) { $Email = '' } }
if (-not $Organization) { $Organization = '' }
if (-not $Team) { $Team = '' }
if (-not $ProjectName -or [string]::IsNullOrWhiteSpace($ProjectName)) { $ProjectName = Split-Path -Leaf (Split-Path -Parent $ScriptPath) }
if (-not $LicenseType) { $LicenseType = 'Apache-2.0' }
if (-not $ComplianceNote) { $ComplianceNote = 'Internal Use Only' }
# Helper: Get metadata value with fallback logic
function Get-MetadataValue {
param(
[Parameter(Mandatory=$true)][string]$CurrentValue,
[Parameter(Mandatory=$true)][ScriptBlock]$Fallback
)
if (-not $CurrentValue -or [string]::IsNullOrWhiteSpace($CurrentValue)) {
return & $Fallback
}
return $CurrentValue
}
# Auto-detect metadata if not provided
$AuthorName = Get-MetadataValue $AuthorName {
$name = Get-GitConfigValue 'user.name'
if ($name) { return $name }
return $env:USERNAME
}
$Email = Get-MetadataValue $Email {
$email = Get-GitConfigValue 'user.email'
if ($email) { return $email }
return ''
}
$Organization = Get-MetadataValue $Organization { return '' }
$Team = Get-MetadataValue $Team { return '' }
$ProjectName = Get-MetadataValue $ProjectName { Split-Path -Leaf (Split-Path -Parent $ScriptPath) }
$LicenseType = Get-MetadataValue $LicenseType { return 'Apache-2.0' }
$ComplianceNote = Get-MetadataValue $ComplianceNote { return 'Internal Use Only' }

Copilot uses AI. Check for mistakes.

# License header templates (generic, populated with the user's info)
$LicenseHeaders = @{
'ps1' = @"
<#
* VA Power Platform Development
* Project: $ProjectName
* Author: $AuthorName
* Organization: $Organization
* Team: $Team
* Email: $Email
*
* Author: Kyle J. Coder
* Organization: Edward Hines Jr. VA Hospital (Hines VAMC)
* Team: Clinical Informatics & Advanced Analytics
* Email: Kyle.Coder@va.gov
*
* This file is part of the VA Power Platform workspace template.
* Licensed under MIT License - see LICENSE file for details.
*
* VA Compliance: No PHI/ePHI, Internal Use Only
* Licensed under $LicenseType - see LICENSE file for details.
* Compliance: $ComplianceNote
* Created: $(Get-Date -Format 'yyyy-MM-dd')
* Generated by Add-LicenseHeaders.ps1
#>

"@

'sql' = @"
/*
* VA Power Platform Development
*
* Author: Kyle J. Coder
* Organization: Edward Hines Jr. VA Hospital (Hines VAMC)
* Team: Clinical Informatics & Advanced Analytics
* Email: Kyle.Coder@va.gov
* Project: $ProjectName
* Author: $AuthorName
* Organization: $Organization
* Team: $Team
* Email: $Email
*
* This file is part of the VA Power Platform workspace template.
* Licensed under MIT License - see LICENSE file for details.
*
* VA Database: VhaCdwDwhSql33.vha.med.va.gov
* Target Database: D03_VISN12Collab
* Classification: Internal Use Only, No PHI/ePHI
* Licensed under $LicenseType - see LICENSE file for details.
* Compliance: $ComplianceNote
* Created: $(Get-Date -Format 'yyyy-MM-dd')
* Generated by Add-LicenseHeaders.ps1
*/

"@

'js' = @"
/**
* VA Power Platform Development
*
* @author Kyle J. Coder
* @organization Edward Hines Jr. VA Hospital (Hines VAMC)
* @team Clinical Informatics & Advanced Analytics
* @email Kyle.Coder@va.gov
* Project: $ProjectName
* @author $AuthorName
* @organization $Organization
* @team $Team
* @email $Email
*
* This file is part of the VA Power Platform workspace template.
* Licensed under MIT License - see LICENSE file for details.
*
* VA Compliance: Section 508 accessible, Internal Use Only
* Licensed under $LicenseType - see LICENSE file for details.
* Compliance: $ComplianceNote
* Created: $(Get-Date -Format 'yyyy-MM-dd')
* Generated by Add-LicenseHeaders.ps1
*/

"@

'css' = @"
/*
* VA Power Platform Development
*
* Author: Kyle J. Coder
* Organization: Edward Hines Jr. VA Hospital (Hines VAMC)
* Team: Clinical Informatics & Advanced Analytics
* Email: Kyle.Coder@va.gov
* Project: $ProjectName
* Author: $AuthorName
* Organization: $Organization
* Team: $Team
* Email: $Email
*
* This file is part of the VA Power Platform workspace template.
* Licensed under MIT License - see LICENSE file for details.
*
* VA Branding: Official colors and accessibility compliance
* Licensed under $LicenseType - see LICENSE file for details.
* Compliance: $ComplianceNote
* Created: $(Get-Date -Format 'yyyy-MM-dd')
* Generated by Add-LicenseHeaders.ps1
*/

"@

'html' = @"
<!--
* VA Power Platform Development
*
* Author: Kyle J. Coder
* Organization: Edward Hines Jr. VA Hospital (Hines VAMC)
* Team: Clinical Informatics & Advanced Analytics
* Email: Kyle.Coder@va.gov
*
* This file is part of the VA Power Platform workspace template.
* Licensed under MIT License - see LICENSE file for details.
* Project: $ProjectName
* Author: $AuthorName
* Organization: $Organization
* Team: $Team
* Email: $Email
*
* VA Compliance: Section 508 accessible, Internal Use Only
* Licensed under $LicenseType - see LICENSE file for details.
* Compliance: $ComplianceNote
* Created: $(Get-Date -Format 'yyyy-MM-dd')
* Generated by Add-LicenseHeaders.ps1
-->

"@
Expand Down Expand Up @@ -206,10 +224,12 @@ function Test-ExistingHeader {
$ContentText = $Content -join "`n"

# Check for existing license indicators
return ($ContentText -match 'Kyle J\. Coder' -or
$ContentText -match 'Edward Hines Jr\. VA Hospital' -or
$ContentText -match 'MIT License' -or
$ContentText -match 'VA Power Platform Development')
return (
$ContentText -match 'Licensed under .+ - see LICENSE file' -or
$ContentText -match 'Generated by Add-LicenseHeaders\.ps1' -or
$ContentText -match '(^/\*.*?(Licensed under|Copyright).*?\*/)' -or # block comment with license
$ContentText -match '(^<#.*?(Licensed under|Copyright).*?#>)' # ps1 block comment with license
Comment on lines +230 to +231
Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex patterns use ^ anchor which matches start of string, but $ContentText is a multiline string joined with newlines. These patterns will only match if the license header is at the very beginning of the file. Consider using multiline mode (?m)^ or \n to match line starts within the content.

Suggested change
$ContentText -match '(^/\*.*?(Licensed under|Copyright).*?\*/)' -or # block comment with license
$ContentText -match '(^<#.*?(Licensed under|Copyright).*?#>)' # ps1 block comment with license
$ContentText -match '(?m)^/\*.*?(Licensed under|Copyright).*?\*/' -or # block comment with license
$ContentText -match '(?m)^<#.*?(Licensed under|Copyright).*?#>' # ps1 block comment with license

Copilot uses AI. Check for mistakes.
)
}
catch {
Write-LogMessage -Message "Error reading file $FilePath`: $($_.Exception.Message)" -Level 'WARNING'
Expand Down Expand Up @@ -255,8 +275,10 @@ function Add-HeaderToFile {
try {
Write-LogMessage -Message '========================================' -Level 'INFO'
Write-LogMessage -Message "$ScriptName v$ScriptVersion" -Level 'INFO'
Write-LogMessage -Message 'Author: Kyle J. Coder' -Level 'INFO'
Write-LogMessage -Message 'Organization: Edward Hines Jr. VA Hospital' -Level 'INFO'
Write-LogMessage -Message "Author: $AuthorName" -Level 'INFO'
Write-LogMessage -Message "Organization: $Organization" -Level 'INFO'
Write-LogMessage -Message "Team: $Team" -Level 'INFO'
Write-LogMessage -Message "Email: $Email" -Level 'INFO'
Write-LogMessage -Message '========================================' -Level 'INFO'

if ($DryRun) {
Expand All @@ -276,8 +298,11 @@ try {
Write-LogMessage -Message "Processing .$Extension files..." -Level 'INFO'

$SearchPattern = "*.$Extension"
$Files = Get-ChildItem -Path $WorkspacePath -Filter $SearchPattern -Recurse -File |
Where-Object { $_.FullName -notmatch '\\\.git\\|\\node_modules\\|\\logs\\' }
# Materialize as an array to avoid $null when there are zero matches (so .Count is safe)
$Files = @(
Get-ChildItem -Path $WorkspacePath -Filter $SearchPattern -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notmatch '\\\.git\\|\\node_modules\\|\\logs\\' }
)

Write-LogMessage -Message "Found $($Files.Count) .$Extension files" -Level 'INFO'

Expand Down