Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7a7e150
Harden API docs layout and source-link preflight
PrzemyslawKlys Mar 25, 2026
8652823
Improve PowerShell API docs fallback examples
PrzemyslawKlys Mar 25, 2026
84cc125
Warn with sampled API source links
PrzemyslawKlys Mar 25, 2026
30914a7
Warn on generated-only PowerShell examples
PrzemyslawKlys Mar 25, 2026
66ebcce
Prefer command-specific PowerShell examples
PrzemyslawKlys Mar 25, 2026
92ec980
Gate generated-only PowerShell examples
PrzemyslawKlys Mar 25, 2026
1c1bb72
Track PowerShell example provenance in API coverage
PrzemyslawKlys Mar 25, 2026
12a08a2
Add API docs thresholds for PowerShell example origins
PrzemyslawKlys Mar 25, 2026
bc3af3c
Add example media support to API docs
PrzemyslawKlys Mar 25, 2026
9ae587d
Add PowerShell example validation for API docs
PrzemyslawKlys Mar 25, 2026
0fc2cd4
Add git freshness metadata to API docs
PrzemyslawKlys Mar 25, 2026
7d9d311
Add PowerShell example execution reporting
PrzemyslawKlys Mar 25, 2026
ee229ae
Add PowerShell example execution artifacts
PrzemyslawKlys Mar 25, 2026
5be87de
Show API example provenance in docs
PrzemyslawKlys Mar 25, 2026
f552994
Attach PowerShell transcripts to docs examples
PrzemyslawKlys Mar 25, 2026
efa5a6d
Stage PowerShell playback assets for docs
PrzemyslawKlys Mar 25, 2026
55cb8f1
Add PowerShell playback media coverage gates
PrzemyslawKlys Mar 25, 2026
56faf9d
Warn on unhealthy PowerShell playback assets
PrzemyslawKlys Mar 25, 2026
61e907b
Add playback asset health coverage thresholds
PrzemyslawKlys Mar 25, 2026
ff428c8
Show API example media capture recency
PrzemyslawKlys Mar 25, 2026
7c6bf1a
Emit PowerShell example media manifest
PrzemyslawKlys Mar 25, 2026
2e7b211
Fix doctor SEO audit for BOM noindex pages
PrzemyslawKlys Mar 25, 2026
604363a
Simplify scaffolded website workflow lock resolution
PrzemyslawKlys Mar 25, 2026
68f079e
Add reusable website workflows
PrzemyslawKlys Mar 25, 2026
a9f52fd
Harden reusable website workflows
PrzemyslawKlys Mar 25, 2026
16d9c95
Deduplicate reusable website workflow logic
PrzemyslawKlys Mar 25, 2026
1814482
Add CLI engine lock resolve mode
PrzemyslawKlys Mar 25, 2026
16db966
Revert "Add CLI engine lock resolve mode"
PrzemyslawKlys Mar 25, 2026
5ac8f5e
Harden website workflow maintenance wrappers
PrzemyslawKlys Mar 25, 2026
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
77 changes: 77 additions & 0 deletions .github/workflows/powerforge-website-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: PowerForge Website CI

on:
workflow_call:
inputs:
website_root:
required: true
type: string
pipeline_config:
required: true
type: string
runner_labels_json:
required: false
type: string
default: '["ubuntu-latest"]'
timeout_minutes:
required: false
type: number
default: 20
dotnet_version:
required: false
type: string
default: "10.0.x"
report_artifact_name:
required: false
type: string
default: "powerforge-website-reports"
powerforge_lock_path:
required: false
type: string
default: ""
powerforge_repository:
required: false
type: string
default: ""
powerforge_ref:
required: false
type: string
default: ""
powerforge_repository_override:
required: false
type: string
default: ""
powerforge_ref_override:
required: false
type: string
default: ""
secrets:
INDEXNOW_KEY:
required: false

permissions:
contents: read

concurrency:
group: powerforge-website-ci-${{ github.repository }}-${{ github.ref }}-${{ inputs.website_root }}
cancel-in-progress: true

jobs:
website-ci:
uses: ./.github/workflows/powerforge-website-run.yml
with:
website_root: ${{ inputs.website_root }}
pipeline_config: ${{ inputs.pipeline_config }}
runner_labels_json: ${{ inputs.runner_labels_json }}
timeout_minutes: ${{ inputs.timeout_minutes }}
dotnet_version: ${{ inputs.dotnet_version }}
report_artifact_name: ${{ inputs.report_artifact_name }}
powerforge_lock_path: ${{ inputs.powerforge_lock_path }}
powerforge_repository: ${{ inputs.powerforge_repository }}
powerforge_ref: ${{ inputs.powerforge_ref }}
powerforge_repository_override: ${{ inputs.powerforge_repository_override }}
powerforge_ref_override: ${{ inputs.powerforge_ref_override }}
pipeline_mode: ci
use_playwright_cache: true
secrets:
indexnow_key: ${{ secrets.INDEXNOW_KEY }}
77 changes: 77 additions & 0 deletions .github/workflows/powerforge-website-maintenance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: PowerForge Website Maintenance

on:
workflow_call:
inputs:
website_root:
required: true
type: string
pipeline_config:
required: true
type: string
runner_labels_json:
required: false
type: string
default: '["ubuntu-latest"]'
timeout_minutes:
required: false
type: number
default: 20
dotnet_version:
required: false
type: string
default: "10.0.x"
report_artifact_name:
required: false
type: string
default: "powerforge-website-maintenance-reports"
powerforge_lock_path:
required: false
type: string
default: ""
powerforge_repository:
required: false
type: string
default: ""
powerforge_ref:
required: false
type: string
default: ""
powerforge_repository_override:
required: false
type: string
default: ""
powerforge_ref_override:
required: false
type: string
default: ""

permissions:
contents: read
# Required for maintenance tasks such as GitHub artifact pruning.
actions: write

concurrency:
group: powerforge-website-maintenance-${{ github.repository }}-${{ github.ref }}-${{ inputs.website_root }}
# Let scheduled/manual maintenance finish once started.
cancel-in-progress: false

jobs:
website-maintenance:
uses: ./.github/workflows/powerforge-website-run.yml
with:
Comment on lines +60 to +62

Choose a reason for hiding this comment

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

P2 Badge Forward IndexNow secret in maintenance workflow call

The maintenance wrapper calls powerforge-website-run.yml without passing indexnow_key, and this workflow also does not declare an INDEXNOW_KEY input secret. Because powerforge-website-run.yml reads secrets.indexnow_key into INDEXNOW_KEY, maintenance runs cannot receive the key even when callers provide it, so maintenance pipelines that include IndexNow submission will run with an empty key and fail/skip publishing unexpectedly.

Useful? React with 👍 / 👎.

website_root: ${{ inputs.website_root }}
pipeline_config: ${{ inputs.pipeline_config }}
runner_labels_json: ${{ inputs.runner_labels_json }}
timeout_minutes: ${{ inputs.timeout_minutes }}
dotnet_version: ${{ inputs.dotnet_version }}
report_artifact_name: ${{ inputs.report_artifact_name }}
powerforge_lock_path: ${{ inputs.powerforge_lock_path }}
powerforge_repository: ${{ inputs.powerforge_repository }}
powerforge_ref: ${{ inputs.powerforge_ref }}
powerforge_repository_override: ${{ inputs.powerforge_repository_override }}
powerforge_ref_override: ${{ inputs.powerforge_ref_override }}
# Maintenance stays on strict CI mode; the selected pipeline config decides which tasks run.
pipeline_mode: ci
use_playwright_cache: true
maintenance_mode_note: true
184 changes: 184 additions & 0 deletions .github/workflows/powerforge-website-run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: PowerForge Website Run

on:
workflow_call:
inputs:
website_root:
required: true
type: string
pipeline_config:
required: true
type: string
runner_labels_json:
required: false
type: string
default: '["ubuntu-latest"]'
timeout_minutes:
required: false
type: number
default: 20
dotnet_version:
required: false
type: string
default: "10.0.x"
report_artifact_name:
required: false
type: string
default: "powerforge-website-reports"
powerforge_lock_path:
required: false
type: string
default: ""
powerforge_repository:
required: false
type: string
default: ""
powerforge_ref:
required: false
type: string
default: ""
powerforge_repository_override:
required: false
type: string
default: ""
powerforge_ref_override:
required: false
type: string
default: ""
pipeline_mode:
required: false
type: string
default: "ci"
use_playwright_cache:
required: false
type: boolean
default: true
maintenance_mode_note:
required: false
type: boolean
default: false
secrets:
indexnow_key:
required: false

jobs:
website-run:
runs-on: ${{ fromJson(inputs.runner_labels_json) }}
timeout-minutes: ${{ inputs.timeout_minutes }}
env:
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
steps:
- name: Checkout website
uses: actions/checkout@v4
with:
token: ${{ github.token }}

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.dotnet_version }}

- name: Resolve PowerForge engine source
id: powerforge
shell: pwsh
run: |
$websiteRoot = [string]'${{ inputs.website_root }}'
$lockPath = [string]'${{ inputs.powerforge_lock_path }}'
if ([string]::IsNullOrWhiteSpace($lockPath)) {
$lockPath = Join-Path $websiteRoot '.powerforge/engine-lock.json'
}

$resolvedRepository = [string]'${{ inputs.powerforge_repository }}'
$resolvedRef = [string]'${{ inputs.powerforge_ref }}'

if (([string]::IsNullOrWhiteSpace($resolvedRepository) -or [string]::IsNullOrWhiteSpace($resolvedRef)) -and (Test-Path -LiteralPath $lockPath)) {
$lock = Get-Content -LiteralPath $lockPath -Raw | ConvertFrom-Json
if ($null -eq $lock) {
throw "Invalid engine lock JSON: $lockPath"
}

if ([string]::IsNullOrWhiteSpace($resolvedRepository)) {
$resolvedRepository = [string]$lock.repository
}

if ([string]::IsNullOrWhiteSpace($resolvedRef)) {
$resolvedRef = [string]$lock.ref
}
}

if ([string]::IsNullOrWhiteSpace($resolvedRepository) -or [string]::IsNullOrWhiteSpace($resolvedRef)) {
throw "Provide either powerforge_repository + powerforge_ref or a valid engine lock file at '$lockPath'."
}

$repoOverride = [string]'${{ inputs.powerforge_repository_override }}'
$refOverride = [string]'${{ inputs.powerforge_ref_override }}'

$finalRepository = if ([string]::IsNullOrWhiteSpace($repoOverride)) { $resolvedRepository } else { $repoOverride }
$finalRef = if ([string]::IsNullOrWhiteSpace($refOverride)) { $resolvedRef } else { $refOverride }

if ($finalRepository -ne $resolvedRepository -or $finalRef -ne $resolvedRef) {
Write-Warning "Using PowerForge override instead of resolved source (${resolvedRepository}@${resolvedRef})."
}

if ($finalRef -notmatch '^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$') {
throw "PowerForge ref must be an immutable commit SHA (40/64 hex): '$finalRef'."
}

"repository=$finalRepository" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
"ref=$finalRef" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

- name: Checkout PowerForge engine
uses: actions/checkout@v4
with:
repository: ${{ steps.powerforge.outputs.repository }}
ref: ${{ steps.powerforge.outputs.ref }}
path: ./.powerforge-engine
token: ${{ github.token }}

- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets', '**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-

- name: Initialize Playwright cache directory
if: ${{ inputs.use_playwright_cache }}
shell: bash
run: |
set -euo pipefail
mkdir -p "${PLAYWRIGHT_BROWSERS_PATH}"

- name: Run website pipeline
shell: pwsh
env:
GITHUB_TOKEN: ${{ github.token }}
INDEXNOW_KEY: ${{ secrets.indexnow_key }}
run: |
$pipelineConfig = [string]'${{ inputs.pipeline_config }}'
$pipelineMode = [string]'${{ inputs.pipeline_mode }}'

if ('${{ inputs.maintenance_mode_note }}' -eq 'true') {
Write-Host 'Maintenance uses the same strict CI mode; the selected pipeline config controls which tasks run.'
}

dotnet run --project ./.powerforge-engine/PowerForge.Web.Cli -- pipeline --config "./$pipelineConfig" --mode $pipelineMode

- name: Upload website reports
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.report_artifact_name }}
path: |
./${{ inputs.website_root }}/_reports/**
./${{ inputs.website_root }}/_site/_reports/**
if-no-files-found: ignore

- name: Cleanup Playwright cache
if: ${{ always() && inputs.use_playwright_cache }}
shell: bash
run: |
if [ -n "${PLAYWRIGHT_BROWSERS_PATH:-}" ] && [ -d "${PLAYWRIGHT_BROWSERS_PATH}" ]; then
rm -rf "${PLAYWRIGHT_BROWSERS_PATH}"
fi
Loading
Loading