Skip to content

Commit de442e0

Browse files
feat(workflows): add orchestration workflows and documentation (#29)
* feat(workflows): add orchestration workflows and documentation Add orchestration workflows that coordinate all validation and security checks: - pr-validation.yml: Orchestrates all validation checks for pull requests including linting, spell checking, link validation, frontmatter validation, and PowerShell analysis - main.yml: Orchestrates security scans and maintenance workflows for main branch including Checkov, Gitleaks, SHA staleness checks, and weekly security maintenance - README.md: Comprehensive documentation for all workflows including usage examples, workflow descriptions, and configuration details These orchestration workflows call the reusable validation and security workflows created in previous PRs. They provide a single entry point for running all checks and enable consistent CI/CD automation across the repository. Resolves: #16 🚀 - Generated by Copilot * docs(workflows): fix job counts and lists in README - Updated pr-validation.yml description to correctly state 10 jobs (9 reusable workflows + 1 inline dependency-pinning-check) - Added dependency-pinning-check to pr-validation jobs list - Corrected main.yml to show 5 jobs instead of 9 - Updated main.yml jobs list to only include actual jobs - Added dependency-pinning-check to workflow architecture diagram 🔒 - Generated by Copilot * fix(build): remove references to non-existent gitleaks and checkov workflows - remove gitleaks-scan and checkov-scan jobs from pr-validation.yml - remove gitleaks-scan and checkov-scan jobs from main.yml - add codeql-analysis and dependency-pinning-scan to main.yml - workflows now reference only existing reusable workflows 🔧 - Generated by Copilot * docs(workflows): address PR review feedback and documentation issues - remove duplicate result publishing strategy section - remove non-existent workflow documentation (gitleaks, checkov) - correct workflow filename references (ps-script-analyzer.yml) - fix heading structure and section organization - move Security Workflows section to proper location - add backticks to configuration table workflow references 📚 - Generated by Copilot * feat(build): modernize workflow architecture for comprehensive PR validation - add explicit PR activity types (opened, synchronize, reopened) to pr-validation - add develop branch support for branch-to-branch PRs - integrate CodeQL security analysis into pr-validation and main orchestrators - move CodeQL to first job in pr-validation for early security feedback - remove standalone push/PR triggers from codeql-analysis (now orchestrator-only) - keep weekly scheduled CodeQL scan for continuous security monitoring 🏗️ - Generated by Copilot * fix(build): remove pull_request trigger from security-scan to prevent duplicate CodeQL runs - remove pull_request trigger (now handled by pr-validation.yml) - remove dependency-review job (conditional on pull_request) - security-scan now only runs on push to main/develop branches - prevents duplicate CodeQL execution on PRs 🔧 - Generated by Copilot * refactor(build): remove redundant security-scan workflow - delete security-scan.yml (CodeQL coverage now via orchestrators) - remove security-scan.yml from orchestrator workflows table in README - consolidate security scanning: PRs use pr-validation, main uses main.yml - weekly standalone CodeQL scan maintained for continuous monitoring 🗑️ - Generated by Copilot * docs(build): resolve PR #29 unresolved comments - standardize CodeQL job naming to codeql-analysis across all workflows - fix documentation accuracy for job counts and job lists - apply markdown style guidelines (asterisk markers, backticks) - enhance clarity with footnotes and improved explanations 📝 - Generated by Copilot * refactor(build): address PR #29 Copilot review feedback - clarify CodeQL execution strategy documentation - make cron comment more concise in codeql-analysis.yml - remove duplicate security-scan.yml workflow - extract inline dependency-pinning-check to reusable workflow 🔧 - Generated by Copilot * fix(build): resolve workflow failures for link check, dependency pinning, and CodeQL - fix markdown link check soft-fail by using step outcomes instead of env vars - fix dependency pinning SARIF generation and scan only github-actions (not npm) - remove CodeQL JavaScript scanning from orchestration workflows - add if-no-files-found: ignore to PSScriptAnalyzer artifact upload 🔧 - Generated by Copilot * fix(build): resolve PR review feedback on workflows - remove trailing blank line in security-scan.yml - add historical context to CodeQL architecture documentation 📝 - Generated by Copilot * feat(security): add compliance threshold enforcement to dependency pinning - add threshold parameter to Test-DependencyPinning.ps1 (default 95%) - implement threshold-based compliance checking in workflow and script - remove redundant workflow-level failure step in favor of script-level enforcement - add comprehensive examples for threshold usage 🔒 - Generated by Copilot
1 parent cf99cbf commit de442e0

13 files changed

Lines changed: 554 additions & 396 deletions

.github/workflows/README.md

Lines changed: 202 additions & 214 deletions
Large diffs are not rendered by default.

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
name: CodeQL Security Analysis
22

33
on:
4-
push:
5-
branches: [ main, develop, feature/* ]
6-
pull_request:
7-
branches: [ main, develop ]
84
schedule:
9-
- cron: '0 4 * * 0' # Sundays at 4 AM UTC
5+
# Weekly scan: Sundays at 4 AM UTC
6+
- cron: '0 4 * * 0'
107
workflow_call:
118

129
permissions:

.github/workflows/dependency-pinning-scan.yml

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ on:
1212
description: 'Comma-separated list of dependency types to check'
1313
required: false
1414
type: string
15-
default: 'actions,containers'
15+
default: 'github-actions'
1616
soft-fail:
1717
description: 'Whether to continue on compliance violations'
1818
required: false
@@ -71,45 +71,74 @@ jobs:
7171
run: |
7272
Write-Host "Validating dependency SHA pinning compliance..."
7373
74-
# Build parameter list
74+
# Ensure logs directory exists
75+
New-Item -ItemType Directory -Force -Path logs | Out-Null
76+
77+
# Build parameter list for JSON output (always generate)
7578
$params = @{
7679
Path = '.'
80+
Recursive = $true
7781
Format = 'json'
7882
OutputPath = 'logs/dependency-pinning-results.json'
7983
}
8084
85+
# Enable failure on threshold violations unless soft-fail is requested
86+
if ('${{ inputs.soft-fail }}' -ne 'true') {
87+
$params['FailOnUnpinned'] = $true
88+
}
89+
90+
# Pass dependency types filter to script
8191
if ('${{ inputs.dependency-types }}') {
82-
$params['DependencyTypes'] = '${{ inputs.dependency-types }}'
92+
$params['IncludeTypes'] = '${{ inputs.dependency-types }}'
8393
}
8494
95+
# Pass compliance threshold to script (script handles enforcement)
8596
if ('${{ inputs.threshold }}') {
8697
$params['Threshold'] = [int]'${{ inputs.threshold }}'
8798
}
8899
89-
# Run validation script
100+
# Run validation script (JSON format)
90101
& scripts/security/Test-DependencyPinning.ps1 @params
102+
$jsonExitCode = $LASTEXITCODE
103+
104+
# Generate SARIF format if requested
105+
if ('${{ inputs.upload-sarif }}' -eq 'true') {
106+
Write-Host "Generating SARIF format for Security tab..."
107+
$params['Format'] = 'sarif'
108+
$params['OutputPath'] = 'logs/dependency-pinning-results.sarif'
109+
110+
& scripts/security/Test-DependencyPinning.ps1 @params
111+
}
91112
92-
# Extract metrics from report
93-
$report = Get-Content logs/dependency-pinning-results.json | ConvertFrom-Json
94-
$complianceScore = $report.ComplianceScore
95-
$unpinnedCount = $report.UnpinnedDependencies
96-
$threshold = [int]'${{ inputs.threshold }}'
97-
$isCompliant = $complianceScore -ge $threshold
98-
99-
"compliance-score=$complianceScore" >> $env:GITHUB_OUTPUT
100-
"unpinned-count=$unpinnedCount" >> $env:GITHUB_OUTPUT
101-
"is-compliant=$($isCompliant.ToString().ToLower())" >> $env:GITHUB_OUTPUT
102-
103-
Write-Host "Compliance Score: $complianceScore%"
104-
Write-Host "Unpinned Dependencies: $unpinnedCount"
105-
Write-Host "Is Compliant (>=$threshold%): $isCompliant"
106-
107-
# Fire GitHub Actions warnings for each violation
108-
if ($unpinnedCount -gt 0) {
109-
foreach ($violation in $report.Violations) {
110-
Write-Output "::warning file=$($violation.File),line=$($violation.Line)::Unpinned $($violation.Type) dependency: $($violation.Name)@$($violation.Version) (Severity: $($violation.Severity))"
113+
# Extract metrics from JSON report
114+
if (Test-Path logs/dependency-pinning-results.json) {
115+
$report = Get-Content logs/dependency-pinning-results.json | ConvertFrom-Json
116+
$complianceScore = $report.ComplianceScore
117+
$unpinnedCount = $report.UnpinnedDependencies
118+
119+
# Extract threshold from report metadata (script calculated compliance)
120+
$threshold = $report.Metadata.ComplianceThreshold
121+
$isCompliant = $complianceScore -ge $threshold
122+
123+
"compliance-score=$complianceScore" >> $env:GITHUB_OUTPUT
124+
"unpinned-count=$unpinnedCount" >> $env:GITHUB_OUTPUT
125+
"is-compliant=$($isCompliant.ToString().ToLower())" >> $env:GITHUB_OUTPUT
126+
127+
Write-Host "Compliance Score: $complianceScore%"
128+
Write-Host "Unpinned Dependencies: $unpinnedCount"
129+
Write-Host "Is Compliant (>=$threshold%): $isCompliant"
130+
131+
# Fire GitHub Actions warnings for each violation
132+
if ($unpinnedCount -gt 0) {
133+
foreach ($violation in $report.Violations) {
134+
Write-Output "::warning file=$($violation.File),line=$($violation.Line)::Unpinned $($violation.Type) dependency: $($violation.Name)@$($violation.Version) (Severity: $($violation.Severity))"
135+
}
111136
}
112137
}
138+
else {
139+
Write-Error "Failed to generate dependency pinning report"
140+
exit 1
141+
}
113142
114143
- name: Upload SARIF to Security tab
115144
if: inputs.upload-sarif && always()
@@ -164,9 +193,3 @@ jobs:
164193
"@
165194
})
166195
"@ | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding UTF8
167-
168-
- name: Fail job if non-compliant
169-
if: steps.pinning.outputs.is-compliant == 'false' && !inputs.soft-fail
170-
run: |
171-
echo "Dependency pinning scan failed - compliance threshold not met"
172-
exit 1

.github/workflows/main.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Main Branch CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
# Minimal permissions for security
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
spell-check:
15+
name: Spell Check
16+
uses: ./.github/workflows/spell-check.yml
17+
permissions:
18+
contents: read
19+
with:
20+
soft-fail: false
21+
22+
markdown-lint:
23+
name: Markdown Lint
24+
uses: ./.github/workflows/markdown-lint.yml
25+
permissions:
26+
contents: read
27+
with:
28+
soft-fail: false
29+
30+
table-format:
31+
name: Table Format Check
32+
uses: ./.github/workflows/table-format.yml
33+
permissions:
34+
contents: read
35+
with:
36+
soft-fail: false
37+
38+
dependency-pinning-scan:
39+
name: Dependency Pinning Scan
40+
uses: ./.github/workflows/dependency-pinning-scan.yml
41+
permissions:
42+
contents: read
43+
security-events: write
44+
with:
45+
soft-fail: false
46+
upload-sarif: true
47+
upload-artifact: true

.github/workflows/markdown-link-check.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ jobs:
4444
New-Item -ItemType Directory -Force -Path logs | Out-Null
4545
4646
- name: Run markdown link check
47+
id: link-check
4748
shell: pwsh
4849
run: |
4950
& scripts/linting/Markdown-Link-Check.ps1
50-
continue-on-error: true
51+
continue-on-error: ${{ inputs.soft-fail }}
5152

5253
- name: Upload markdown link check results
5354
if: always()
@@ -58,10 +59,8 @@ jobs:
5859
retention-days: 30
5960

6061
- name: Check results and fail if needed
61-
if: ${{ !inputs.soft-fail }}
62+
if: ${{ !inputs.soft-fail && steps.link-check.outcome == 'failure' }}
6263
shell: pwsh
6364
run: |
64-
if ($env:MARKDOWN_LINK_CHECK_FAILED -eq 'true') {
65-
Write-Host "Markdown link check failed and soft-fail is false. Failing the job."
66-
exit 1
67-
}
65+
Write-Host "Markdown link check failed and soft-fail is false. Failing the job."
66+
exit 1
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: PR Validation
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
branches:
7+
- main
8+
- develop
9+
workflow_dispatch:
10+
11+
# Minimal permissions for security
12+
permissions:
13+
contents: read
14+
15+
jobs:
16+
spell-check:
17+
name: Spell Check
18+
uses: ./.github/workflows/spell-check.yml
19+
permissions:
20+
contents: read
21+
with:
22+
soft-fail: false
23+
24+
markdown-lint:
25+
name: Markdown Lint
26+
uses: ./.github/workflows/markdown-lint.yml
27+
permissions:
28+
contents: read
29+
with:
30+
soft-fail: false
31+
32+
table-format:
33+
name: Table Format Check
34+
uses: ./.github/workflows/table-format.yml
35+
permissions:
36+
contents: read
37+
with:
38+
soft-fail: false
39+
40+
psscriptanalyzer:
41+
name: PowerShell Lint
42+
uses: ./.github/workflows/ps-script-analyzer.yml
43+
permissions:
44+
contents: read
45+
with:
46+
soft-fail: false
47+
changed-files-only: true
48+
49+
frontmatter-validation:
50+
name: Frontmatter Validation
51+
uses: ./.github/workflows/frontmatter-validation.yml
52+
permissions:
53+
contents: read
54+
with:
55+
soft-fail: false
56+
changed-files-only: true
57+
skip-footer-validation: false
58+
warnings-as-errors: true
59+
60+
link-lang-check:
61+
name: Link Language Check
62+
uses: ./.github/workflows/link-lang-check.yml
63+
permissions:
64+
contents: read
65+
with:
66+
soft-fail: false
67+
68+
markdown-link-check:
69+
name: Markdown Link Check
70+
uses: ./.github/workflows/markdown-link-check.yml
71+
permissions:
72+
contents: read
73+
with:
74+
soft-fail: true
75+
76+
dependency-pinning-check:
77+
name: Validate Dependency Pinning
78+
uses: ./.github/workflows/dependency-pinning-scan.yml
79+
permissions:
80+
contents: read
81+
security-events: write
82+
with:
83+
soft-fail: false
84+
upload-sarif: true
85+
upload-artifact: false

.github/workflows/ps-script-analyzer.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jobs:
6060
logs/psscriptanalyzer-results.json
6161
logs/psscriptanalyzer-summary.json
6262
retention-days: 30
63+
if-no-files-found: ignore
6364

6465
- name: Check results
6566
if: "!inputs.soft-fail"

.github/workflows/security-scan.yml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ name: Security Scan
33
on:
44
push:
55
branches: [ main, develop ]
6-
pull_request:
7-
branches: [ main, develop ]
86

97
permissions:
108
contents: read
@@ -18,12 +16,4 @@ jobs:
1816
permissions:
1917
contents: read
2018
security-events: write
21-
actions: read
22-
23-
dependency-review:
24-
name: Dependency Review
25-
if: github.event_name == 'pull_request'
26-
uses: ./.github/workflows/dependency-review.yml
27-
permissions:
28-
contents: read
29-
pull-requests: write
19+
actions: read

.github/workflows/weekly-security-maintenance.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
max-age-days: ${{ inputs.max-age-days || 30 }}
3939

4040
# Job 3: Run CodeQL security analysis
41-
codeql-scan:
41+
codeql-analysis:
4242
name: CodeQL Security Analysis
4343
uses: ./.github/workflows/codeql-analysis.yml
4444
permissions:
@@ -49,7 +49,7 @@ jobs:
4949
# Job 4: Generate consolidated summary
5050
summary:
5151
name: Security Maintenance Summary
52-
needs: [validate-pinning, check-staleness, codeql-scan]
52+
needs: [validate-pinning, check-staleness, codeql-analysis]
5353
if: always()
5454
runs-on: ubuntu-latest
5555
permissions:
@@ -61,7 +61,7 @@ jobs:
6161
$pinningScore = '${{ needs.validate-pinning.outputs.compliance-score }}'
6262
$unpinnedCount = '${{ needs.validate-pinning.outputs.unpinned-count }}'
6363
$staleCount = '${{ needs.check-staleness.outputs.stale-count }}'
64-
$codeqlResult = '${{ needs.codeql-scan.result }}'
64+
$codeqlResult = '${{ needs.codeql-analysis.result }}'
6565
6666
# Determine overall status
6767
$hasIssues = $false

0 commit comments

Comments
 (0)