Skip to content

Update API Capability Map #2

Update API Capability Map

Update API Capability Map #2

name: Update API Capability Map
# Keeps Data/PfbCapabilityMap.json (and, when an SSOT API key is configured,
# Data/PfbVersionMap.json) in sync with newly-published FlashBlade REST API versions.
# See tools/lib/PfbSpecTools.ps1 for how the manifest is derived, and tools/README.md
# for the overall pipeline this feeds into.
#
# Note: tools/specs/ (the raw cached OpenAPI specs) is deliberately NOT committed to the
# repo — see .gitignore — instead it's persisted across runs via actions/cache below, keyed
# per-run-id with a prefix restore-key so each run restores the most recent prior cache.
# Update-PfbApiSpecs.ps1 skips any version already present on disk, so a cache hit means
# only newly-published versions get fetched instead of the full ~28-version history. Only
# the small derived Data/PfbCapabilityMap.json is tracked and diffed for the PR below.
on:
push:
branches: [main]
paths: ['Public/**']
schedule:
# Mondays 03:17 UTC - arbitrary off-peak time, avoids the top-of-hour thundering herd.
- cron: '17 3 * * 1'
workflow_dispatch: {}
permissions:
contents: write
pull-requests: write
jobs:
update-capability-map:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Capture previously-known REST versions
id: previous
shell: pwsh
run: |
$versions = @()
if (Test-Path 'Data/PfbCapabilityMap.json') {
$versions = (Get-Content 'Data/PfbCapabilityMap.json' -Raw | ConvertFrom-Json -Depth 5).generatedFrom
}
"versions=$($versions -join ',')" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Restore cached spec files
uses: actions/cache/restore@v6
with:
path: tools/specs
key: pfb-specs-${{ github.run_id }}
restore-keys: |
pfb-specs-
- name: Fetch all published REST API spec versions
shell: pwsh
run: ./tools/Update-PfbApiSpecs.ps1
- name: Save spec cache
uses: actions/cache/save@v6
if: always()
with:
path: tools/specs
key: pfb-specs-${{ github.run_id }}
- name: Build capability map
shell: pwsh
run: ./tools/Build-PfbCapabilityMap.ps1
- name: Update REST<->Purity version map (skips gracefully if not configured)
shell: pwsh
env:
SSOT_API_KEY: ${{ secrets.SSOT_API_KEY }}
SSOT_BASE_URI: ${{ secrets.SSOT_BASE_URI }}
SSOT_TOPIC_ID: ${{ secrets.SSOT_TOPIC_ID }}
run: ./tools/Update-PfbVersionMap.ps1
- name: Build value-enum map
shell: pwsh
run: ./tools/Build-PfbValueEnumMap.ps1
- name: Build field-cmdlet map
shell: pwsh
run: ./tools/Build-PfbFieldCmdletMap.ps1
# Position is load-bearing, do not reorder: this must run AFTER "Fetch all published
# REST API spec versions" (the generator walks tools/specs/ and never fetches) and
# BEFORE "Build API drift report" (the report reads Data/PfbResponseShapeMap.json).
# Placed after the report, CI would build the report from the stale committed map and
# then overwrite it -- a report and map that disagree, with no error raised.
- name: Build response shape map
shell: pwsh
run: ./tools/Build-PfbResponseShapeMap.ps1
- name: Build API drift report
shell: pwsh
run: ./tools/Build-PfbApiDriftReport.ps1
# Pinned + cached, with retry on the gallery path; see
# .github/actions/install-test-modules/action.yml (including why Posh-SSH is
# needed at all -- Pester can only mock a resolvable command).
- name: Install test dependencies
uses: ./.github/actions/install-test-modules
with:
shell: pwsh
- name: Run tests
shell: pwsh
run: |
Import-Module Pester -MinimumVersion 5.0 -Force
Import-Module Posh-SSH -Force
$cfg = New-PesterConfiguration
$cfg.Run.Path = 'Tests'
$cfg.Run.Exit = $true
$cfg.Output.Verbosity = 'Detailed'
Invoke-Pester -Configuration $cfg
- name: Check for changes
id: diff
run: |
if [ -n "$(git status --porcelain -- Data Reports)" ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Summarize new versions
id: summary
if: steps.diff.outputs.changed == 'true'
shell: pwsh
run: |
$oldVersions = @('${{ steps.previous.outputs.versions }}' -split ',' | Where-Object { $_ })
$newManifest = Get-Content 'Data/PfbCapabilityMap.json' -Raw | ConvertFrom-Json -Depth 5
$newlyAdded = $newManifest.generatedFrom | Where-Object { $oldVersions -notcontains $_ }
"new_versions=$($newlyAdded -join ', ')" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
# $drift.parameterGaps.Count counts ENDPOINTS that have at least one gap, not
# individual fields -- it was mislabeled "parameter gaps" here, which read as a
# field-level count. Query/body/read-only are separate categories on the report
# (see tools/Build-PfbApiDriftReport.ps1), so the field-level total is the sum of
# each category's own field count, computed honestly from the report rather than
# re-using the endpoint count under a field-shaped label.
$drift = Get-Content 'Reports/PfbApiDriftReport.json' -Raw | ConvertFrom-Json -Depth 20
$queryFieldCount = (@($drift.parameterGaps.missingQueryParameters) | Measure-Object).Count
$bodyFieldCount = (@($drift.parameterGaps.missingBodyProperties) | Measure-Object).Count
$readOnlyFieldCount = (@($drift.parameterGaps.readOnlyFields) | Measure-Object).Count
# Same @() guard as above, for the same reason: ConvertFrom-Json yields a bare
# object for a single-element array and $null for a missing key, so a direct
# .Count is unreliable for both.
$responseRemovalCount = (@($drift.responseFieldRemovals) | Measure-Object).Count
$responseRenameCount = (@($drift.responseFieldRenameCandidates) | Measure-Object).Count
$unhandledEnvelopeCount = (@($drift.unhandledResponseEnvelopeFields) | Measure-Object).Count
$driftSummary = "$($drift.uncoveredEndpoints.Count) uncovered endpoints, $($drift.parameterGaps.Count) endpoints with parameter gaps ($queryFieldCount missing query fields, $bodyFieldCount missing body fields, $readOnlyFieldCount read-only fields), $($drift.systemicGaps.Count) systemic gaps, $($drift.validateSetDrift.Count) ValidateSet drift findings, $($drift.newValidateSetCandidates.Count) new ValidateSet candidates, $responseRemovalCount response field removals (one per endpoint+field pair), $responseRenameCount response field rename candidates, $unhandledEnvelopeCount unhandled response envelope fields"
"drift_summary=$driftSummary" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Open pull request
if: steps.diff.outputs.changed == 'true' && github.repository == 'dmann000/fb-powershell'
# v8 for the node24 runtime (v6 and v7 both run on node20 and emit a deprecation
# warning). Safe jump despite skipping a major: v7's only breaking changes were
# renaming the `git-token` input to `branch-token` and removing the deprecated
# PULL_REQUEST_NUMBER output env var, neither of which this step uses.
uses: peter-evans/create-pull-request@v8
with:
commit-message: 'Update API capability map'
title: "Update API capability map${{ steps.summary.outputs.new_versions && format(' (new REST versions: {0})', steps.summary.outputs.new_versions) || '' }}"
body: |
Automated update from the `update-api-capability-map` workflow.
- Re-fetched every published REST API spec version from the FlashBlade
swagger index (not committed - see `.gitignore`) and rebuilt
`Data/PfbCapabilityMap.json` from the full history.
- Updated `Data/PfbVersionMap.json` if an `SSOT_API_KEY` secret is
configured; otherwise left untouched (see `tools/Update-PfbVersionMap.ps1`).
- Rebuilt `Data/PfbResponseShapeMap.json`, the response-side counterpart to the
capability map, which feeds the drift report's response-shape findings.
- Rebuilt `Reports/PfbValueEnumMap.json`, `Reports/PfbFieldCmdletMap.json`, and
`Reports/PfbApiDriftReport.json` (+ their Markdown companions) -- see
`Reports/README.md` for what each answers.
New REST versions detected: ${{ steps.summary.outputs.new_versions || 'none - see diff (e.g. a version-map-only update once an SSOT_API_KEY secret was configured, or new parameters/fields added to existing endpoints in a point release)' }}
API drift report: ${{ steps.summary.outputs.drift_summary || 'no drift-report changes this run' }}
branch: automated/update-api-capability-map
delete-branch: true
add-paths: |
Data
Reports