Skip to content

RHIDP-18434: add script to check plugin digests against tags [release-2.1] - #2719

Merged
nickboldt merged 10 commits into
release-2.1from
RHIDP-18434-check-plugin-digests
Nov 5, 2026
Merged

nickboldt merged 10 commits into
release-2.1from
RHIDP-18434-check-plugin-digests

Conversation

@nickboldt

Copy link
Copy Markdown
Member

Summary

Adds modules/extend_dynamic-plugins-reference/check-plugin-digests.sh for RHIDP-18434.

The generated dynamic plugin tables pin an immutable @sha256: digest per plugin and name the mutable artifact tag in the next column. When a tag is rebuilt, the pinned digest and build time no longer match what the tag publishes, so the tables are stale until the catalog indexes are rebuilt and the tables regenerated. Nothing currently reports that condition.

The script:

  • Pairs every pinned oci://…@sha256:… in ref-community-supported-plugins.adoc, ref-ga-plugins.adoc, ref-technology-preview-plugins.adoc, and ref-other-installable-plugins.adoc with the artifact tag in the following table cell.
  • Runs skopeo inspect on repo@digest and repo:tag in parallel (-j, default 8).
  • Compares the platform manifest digest, not the raw image digest: the tables pin single-architecture manifest digests while tags usually publish a multi-architecture index, so raw digests differ even when nothing is stale. --arch/--os select the manifest (default amd64/linux).
  • Reports OK, STALE_PIN, PIN_NEWER_THAN_TAG, DIGEST_MISMATCH, PINNED_UNRESOLVABLE, and TAG_UNRESOLVABLE rows with pinned and tag digests plus build times.
  • On drift, prints the call to action with links and commands: the community catalog index build, the downstream GitLab update index schedule play, and the documentation table update workflow, each in gh/glab and REST API form.
  • Exits 0 when clean, 1 on drift, 2 on usage or tool error; --warn-only reports without failing, --trigger dispatches the two GitHub workflows.

README.md documents the script.

Test plan

  • bash -n modules/extend_dynamic-plugins-reference/check-plugin-digests.sh
  • shellcheck --severity=style modules/extend_dynamic-plugins-reference/check-plugin-digests.sh
  • modules/extend_dynamic-plugins-reference/check-plugin-digests.sh --help
  • modules/extend_dynamic-plugins-reference/check-plugin-digests.sh -f ref-community-supported-plugins.adoc → reports the 36 community pins whose tags were rebuilt on 2026-10-30 against pins from 2026-10-28, exit 1
  • modules/extend_dynamic-plugins-reference/check-plugin-digests.sh --warn-only → 105 pinned artifacts checked; supported and tech-preview rows from quay.io match, community rows from ghcr.io are stale, exit 0
  • modules/extend_dynamic-plugins-reference/check-plugin-digests.sh -f /tmp/drift-fixture.adoc --all → per-row output verified against skopeo inspect of backstage-community-plugin-azure-devops-backend:bs_1.54.9__0.32.0

Assisted-by: pi (GPT 5.6 Sol High)

@rhdh-bot

rhdh-bot commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Shellcheck Analysis Results

Changed shell scripts in this PR: 1

Scripts analyzed:

  • modules/extend_dynamic-plugins-reference/check-plugin-digests.sh

Check the Files changed tab for detailed shellcheck suggestions.

All findings are reported as warnings and won't block the PR.


Automated shellcheck analysis • See shellcheck.net for details

@rhdh-bot

rhdh-bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

PR Build Results

Build passed -- 2/2 titles | 69s
Preview: https://redhat-developer.github.io/red-hat-developers-documentation-rhdh/pr-2719/


Content Quality Assessment

CQA Report

  • CQA-00a: Orphaned modules
  • CQA-00b: Directory structure
  • CQA-01: Vale AsciiDoc DITA compliance
  • CQA-02: Verify assembly structure
  • CQA-03: Verify content type metadata
  • CQA-04: Verify module templates
  • CQA-05: Verify required modular elements
  • CQA-06: Verify assemblies follow official template (one user story)
  • CQA-07: Verify TOC depth (max 3 levels)
  • CQA-08: Verify short description content quality
  • CQA-09: Verify short description format
  • CQA-10: Verify titles are brief, complete, and descriptive
  • CQA-11: Verify procedure prerequisites
  • CQA-12: Verify grammar and style (Vale)
  • CQA-13: Verify content matches declared type
  • CQA-14: Verify no broken links
  • CQA-14b: Inbound link stability (no ID changes for existing files)
  • CQA-15: Check redirects
  • CQA-16: Verify official product names
  • CQA-17: Verify legal disclaimers for preview features
  • CQA-jtbd-01: Verify navigation file structure
  • CQA-jtbd-02: Modules not yet in product_product (informational)

Summary

Checks: 22 total, 22 pass, 0 fail

Modules not yet in product_product

7 modules (proc, con, ref) are not yet included in the product_product title.

modules/extend_discovering-ai-assets-in-the-ai-catalog/ (7)
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/con-ai-catalog-architecture.adoc
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/con-ai-catalog-overview.adoc
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/proc-browse-ai-assets-in-the-catalog.adoc
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/proc-configure-ai-catalog-filters.adoc
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/proc-enable-ai-catalog-plugin.adoc
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/proc-view-ai-asset-details.adoc
  • modules/extend_discovering-ai-assets-in-the-ai-catalog/ref-ai-asset-entity-model.adoc

22 checks: 22 pass, 0 fail

Run node build/scripts/cqa/index.js --all --fix locally to review and auto-fix issues.


Updated 2026-11-05 18:46:05 UTC

@nickboldt
nickboldt force-pushed the RHIDP-18434-check-plugin-digests branch from 9995c7c to 4798053 Compare November 4, 2026 15:08
nickboldt and others added 9 commits November 5, 2026 11:15
The generated dynamic plugin tables pin an immutable @sha256: digest per
plugin and name the mutable artifact tag in the next column. Once a tag is
rebuilt, the pinned digest no longer matches what the tag publishes and the
tables are stale until the catalog indexes and the tables are regenerated.

check-plugin-digests.sh pairs each pinned digest with its artifact tag, runs
skopeo inspect on both references, and compares the platform manifest digest
because the tables pin single-architecture manifest digests while tags publish
multi-architecture indexes. Rows that differ are reported with both digests
and build times, followed by the call to action for the community catalog
index build, the downstream "update index" schedule, and the documentation
table update workflow.

Assisted-by: pi (GPT 5.6 Sol High)
Signed-off-by: Nick Boldt <nboldt@redhat.com>
The aligned fixed-width console table broke whenever an image or tag name
exceeded its column, and full ISO 8601 timestamps with nanoseconds dominated
the output. Render the report as a markdown table instead, truncate build times
to the minute in UTC, and add --output to write the report to a file.

Assisted-by: pi (GPT 5.6 Sol High)
Signed-off-by: Nick Boldt <nboldt@redhat.com>
Printing the markdown report alone left nothing to open in an editor. Write it
to report.md next to the working directory as well, keep stdout unchanged, and
let --output redirect the file. Ignore report.md so a run does not dirty the tree.

Assisted-by: pi (GPT 5.6 Sol High)
Signed-off-by: rhdh-bot service account <rhdh-bot@redhat.com>
Signed-off-by: rhdh-bot service account <rhdh-bot@redhat.com>
Sonar shell:S7679 flagged '"$1"' in extract_pins(). Same pattern in epoch();
both now copy the argument into a local, as inspect_ref() already did.

Generated-by: paseo (pi + DeepSeek V4.1 Flash (EnMaaS), thinking: off)
extract_pins() dropped a pin when no tag row followed it, so a table format
change would silently shrink the checked set. The pending pin is now emitted
with a "-" sentinel and reported as NO_TAG, have_pin resets at a |=== table
break, and the last-cell branch uses a counted split() instead of the
length(array) extension so it also parses with a POSIX awk.

Generated-by: paseo (pi + DeepSeek V4.1 Flash (EnMaaS), thinking: off)
The skopeo cache was tab separated and read with IFS=$'\t'. A tab is IFS
whitespace, so `read` collapses runs of it: an empty Created field shifted the
manifest digest into the Created slot and the comparison then reported
DIGEST_MISMATCH. Cached fields are now separated and read with $'\x1f'.

Generated-by: paseo (pi + DeepSeek V4.1 Flash (EnMaaS), thinking: off)
Signed-off-by: Nick Boldt <nboldt@redhat.com>
Signed-off-by: Nick Boldt <nboldt@redhat.com>
@nickboldt

Copy link
Copy Markdown
Member Author

/cherry-pick main

@openshift-cherrypick-robot

Copy link
Copy Markdown
Contributor

@nickboldt: once the present PR merges, I will cherry-pick it on top of main in a new PR and assign it to you.

Details

In response to this:

/cherry-pick main

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

…ck-plugin-digests.sh script

Signed-off-by: Nick Boldt <nboldt@redhat.com>
@nickboldt
nickboldt merged commit ecefcc5 into release-2.1 Nov 5, 2026
3 checks passed
@nickboldt
nickboldt deleted the RHIDP-18434-check-plugin-digests branch November 5, 2026 18:42
@sonarqubecloud

sonarqubecloud Bot commented Nov 5, 2026

Copy link
Copy Markdown

nickboldt added a commit that referenced this pull request Nov 5, 2026
…-2.1] (#2719)

* RHIDP-18434: add script to check plugin digests against tags

The generated dynamic plugin tables pin an immutable @sha256: digest per
plugin and name the mutable artifact tag in the next column. Once a tag is
rebuilt, the pinned digest no longer matches what the tag publishes and the
tables are stale until the catalog indexes and the tables are regenerated.

check-plugin-digests.sh pairs each pinned digest with its artifact tag, runs
skopeo inspect on both references, and compares the platform manifest digest
because the tables pin single-architecture manifest digests while tags publish
multi-architecture indexes. Rows that differ are reported with both digests
and build times, followed by the call to action for the community catalog
index build, the downstream "update index" schedule, and the documentation
table update workflow.

Assisted-by: pi (GPT 5.6 Sol High)
Signed-off-by: Nick Boldt <nboldt@redhat.com>

* RHIDP-18434: report plugin digest drift as a markdown table

The aligned fixed-width console table broke whenever an image or tag name
exceeded its column, and full ISO 8601 timestamps with nanoseconds dominated
the output. Render the report as a markdown table instead, truncate build times
to the minute in UTC, and add --output to write the report to a file.

Assisted-by: pi (GPT 5.6 Sol High)
Signed-off-by: Nick Boldt <nboldt@redhat.com>

* RHIDP-18434: always save the digest report to report.md

Printing the markdown report alone left nothing to open in an editor. Write it
to report.md next to the working directory as well, keep stdout unchanged, and
let --output redirect the file. Ignore report.md so a run does not dirty the tree.

Assisted-by: pi (GPT 5.6 Sol High)
Signed-off-by: rhdh-bot service account <rhdh-bot@redhat.com>

* simplify CTA for respinning index content

Signed-off-by: rhdh-bot service account <rhdh-bot@redhat.com>

* RHIDP-18434: assign positional parameters to local variables

Sonar shell:S7679 flagged '"$1"' in extract_pins(). Same pattern in epoch();
both now copy the argument into a local, as inspect_ref() already did.

Generated-by: paseo (pi + DeepSeek V4.1 Flash (EnMaaS), thinking: off)

* RHIDP-18434: report pins whose table row has no artifact tag

extract_pins() dropped a pin when no tag row followed it, so a table format
change would silently shrink the checked set. The pending pin is now emitted
with a "-" sentinel and reported as NO_TAG, have_pin resets at a |=== table
break, and the last-cell branch uses a counted split() instead of the
length(array) extension so it also parses with a POSIX awk.

Generated-by: paseo (pi + DeepSeek V4.1 Flash (EnMaaS), thinking: off)

* RHIDP-18434: keep empty cache fields in the digest check

The skopeo cache was tab separated and read with IFS=$'\t'. A tab is IFS
whitespace, so `read` collapses runs of it: an empty Created field shifted the
manifest digest into the Created slot and the comparison then reported
DIGEST_MISMATCH. Cached fields are now separated and read with $'\x1f'.

Generated-by: paseo (pi + DeepSeek V4.1 Flash (EnMaaS), thinking: off)

* revise CTA text

Signed-off-by: Nick Boldt <nboldt@redhat.com>

* improved CTA flow

Signed-off-by: Nick Boldt <nboldt@redhat.com>

* add disclaimer about the modules/extend_dynamic-plugins-reference/check-plugin-digests.sh script

Signed-off-by: Nick Boldt <nboldt@redhat.com>

---------

Signed-off-by: Nick Boldt <nboldt@redhat.com>
Signed-off-by: rhdh-bot service account <rhdh-bot@redhat.com>
Co-authored-by: rhdh-bot service account <rhdh-bot@redhat.com>
@openshift-cherrypick-robot

Copy link
Copy Markdown
Contributor

@nickboldt: #2719 failed to apply on top of branch "main":

Applying: RHIDP-18434: add script to check plugin digests against tags
.git/rebase-apply/patch:355: trailing whitespace.
     # THEN 
warning: 1 line adds whitespace errors.
Using index info to reconstruct a base tree...
M	modules/extend_dynamic-plugins-reference/README.md
Falling back to patching base and 3-way merge...
Auto-merging modules/extend_dynamic-plugins-reference/README.md
CONFLICT (content): Merge conflict in modules/extend_dynamic-plugins-reference/README.md
Auto-merging modules/extend_dynamic-plugins-reference/check-plugin-digests.sh
CONFLICT (add/add): Merge conflict in modules/extend_dynamic-plugins-reference/check-plugin-digests.sh
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 RHIDP-18434: add script to check plugin digests against tags

Details

In response to this:

/cherry-pick main

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

This branch was previously deployed

1 inactive deployment
internal — 25bb4826 Deployed Nov 5, 2026 by nickboldt via authorize #347
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants