Skip to content

Merge pull request #146 from juemerson-at-purestorage/tests/sweep-sho… #24

Merge pull request #146 from juemerson-at-purestorage/tests/sweep-sho…

Merge pull request #146 from juemerson-at-purestorage/tests/sweep-sho… #24

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
# Issue #63 follow-up: no CI job has ever compared a regenerated artifact against the
# committed one, so a generator change committed without regenerating passes silently.
# This had to be done by hand for #96 and #97. Hash before the build steps overwrite them.
- name: Capture committed artifact hashes
id: committed_hashes
shell: pwsh
run: |
foreach ($pair in @(@('capmap', 'Data/PfbCapabilityMap.json'), @('shapemap', 'Data/PfbResponseShapeMap.json'))) {
$hash = ''
if (Test-Path $pair[1]) { $hash = (Get-FileHash -Path $pair[1] -Algorithm SHA256).Hash }
"$($pair[0])=$hash" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
Write-Host "$($pair[1]): $hash"
}
- 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
# Reports the outcome; does NOT fail the run. A legitimate diff is this workflow's whole
# purpose -- a new REST version changes both maps -- so a hard failure here would break
# the scheduled run every time a version is published. The value is that the drift is
# now NAMED in the run summary instead of being inferred from the PR body, which is how
# a stale committed artifact went unnoticed (issue #63's follow-up).
- name: Report regeneration diff
shell: pwsh
run: |
$rows = @()
foreach ($pair in @(
@('Data/PfbCapabilityMap.json', '${{ steps.committed_hashes.outputs.capmap }}'),
@('Data/PfbResponseShapeMap.json', '${{ steps.committed_hashes.outputs.shapemap }}')
)) {
$now = (Get-FileHash -Path $pair[0] -Algorithm SHA256).Hash
$state = 'byte-identical'
if ($pair[1] -ne $now) { $state = 'CHANGED -- committed copy was stale, or the inputs moved' }
$rows += "| ``$($pair[0])`` | $state |"
Write-Host "$($pair[0]): $state"
}
$lines = @('### Regeneration check', '', '| Artifact | Result |', '|---|---|') + $rows
($lines -join [Environment]::NewLine) | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append
- name: Build API drift report
shell: pwsh
run: ./tools/Build-PfbApiDriftReport.ps1
- name: Build dead-key report
shell: pwsh
run: ./tools/Build-PfbDeadKeyReport.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
# The third copy of this block, now retired. It could not be replaced by a workflow_call
# to cross-platform-tests.yml: a reusable workflow runs on a FRESH runner with a FRESH
# checkout, so it would test the COMMITTED Data/ and Reports/ rather than the ones this
# job just regenerated -- which is the entire reason the step is inline here. A script
# runs in this job's workspace and has no such problem.
- name: Run tests
shell: pwsh
run: ./scripts/Invoke-PfbCiPester.ps1 -Edition pwsh7
- 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.
- Rebuilt `Reports/PfbDeadKeyReport.json`, the inventory of query keys that
`Public/` cmdlets send but the endpoint does not declare for that HTTP verb --
see `Reports/README.md`.
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