Skip to content

Set the Docker Hub short description from the README intro - #32

Merged
ptr727 merged 0 commit into
developfrom
conformance/dockerhub-short-description
Aug 1, 2026
Merged

Set the Docker Hub short description from the README intro#32
ptr727 merged 0 commit into
developfrom
conformance/dockerhub-short-description

Conversation

@ptr727

@ptr727 ptr727 commented Aug 1, 2026

Copy link
Copy Markdown
Owner

The last finding from the post-merge re-audit of develop:

LETTER description: the Docker Hub short description ('Pre-process media files for import into photo management systems.') does not match the README intro ('An application that prepares photos and videos for import into photo managers.') - set it from the README (spec/readme-structure.md)

Why it drifted

There was no mechanism behind it. build-docker-task.yml pushed the Docker Hub overview from Docker/README.md, but never set the short description, so the tagline kept whatever was typed into the web UI once and no workflow could correct it. The audit could flag the drift indefinitely and nothing would ever fix it.

The fix

The step reads the intro from README.md rather than restating it. A literal in the workflow would be one more surface for the same sentence to drift on, and there are already six.

Two guards fail the publish loudly rather than pushing a blank or truncated tagline, because a restructured README that moves the intro line would otherwise silently blank the Docker Hub description on the next publish. That is the "gates fail loud, never narrow quietly" rule in GOVERNANCE.md "Verification Discipline": an empty result errors, and so does one over Docker Hub's 100-character cap.

The tr -d '\r' matters and is not cosmetic: README.md is CRLF, and a line holding only a carriage return is not empty to awk, so without the strip the extraction returns the blank line after the H1 instead of the intro.

Verification

Exercised the script over four inputs rather than assuming the guards work:

Input Result
Good CRLF README intro=Good short line., exit 0
Missing intro ::error::No intro line found, exit 1
120-character intro ::error::intro is 120 chars, cap is 100, exit 1
LF-only README intro=LF-only file works too., exit 0

actionlint (rhysd/actionlint, which bundles shellcheck for run: blocks) exits 0, and editorconfig-checker exits 0.

The step is gated inputs.push && inputs.branch == 'main', so the live Docker Hub value updates on the next main publish rather than on merge.

Follows the conformance sweep (audit run 2026-08-01T16:49:11Z | hub 82b9d5d, develop@95af4d7). The GitHub About description is already set and matches.

Copilot AI review requested due to automatic review settings August 1, 2026 16:51
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 44.96%. Comparing base (95af4d7) to head (d688904).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop      #32   +/-   ##
========================================
  Coverage    44.96%   44.96%           
========================================
  Files           25       25           
  Lines         3398     3398           
  Branches       259      259           
========================================
  Hits          1528     1528           
  Misses        1824     1824           
  Partials        46       46           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates the Docker image publishing workflow so Docker Hub’s short description is set automatically from the repository README intro line, preventing drift between the README and Docker Hub metadata.

Changes:

  • Adds a workflow step that extracts the README intro line (with CRLF handling) and exposes it as a step output.
  • Adds guards to fail the publish if the extracted line is empty or exceeds Docker Hub’s 100-character limit.
  • Passes the extracted intro to peter-evans/dockerhub-description via the short-description input on main publishes.

Comment thread .github/workflows/build-docker-task.yml Outdated
Copilot AI review requested due to automatic review settings August 1, 2026 16:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.github/workflows/build-docker-task.yml:113

  • The markdown-structure guard can be bypassed if the first non-empty line after the H1 starts with leading whitespace (e.g. " ## Heading" or " - item"). CommonMark allows up to 3 leading spaces for ATX headings and list markers, so the workflow could still publish README structure as the Docker Hub short description instead of failing loudly as intended. Trimming leading/trailing whitespace at extraction time makes the subsequent structure/link/length checks reliable.
          INTRO=$(tr -d '\r' < README.md | awk 'NR>1 && NF {print; exit}')

Copilot AI review requested due to automatic review settings August 1, 2026 17:00
@ptr727

ptr727 commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

The collapsed finding on the review of 6e3b84c was right, and is fixed in 27e09d6a32e5f19549e1851eb10e6c37bce7cb63.

CommonMark does allow up to three leading spaces before a heading or list marker, so the case guard read an untrimmed line and an indented ## would have slipped through as the tagline. Trailing space would have padded the published string.

Trimming in the awk extraction rather than at each check is the part worth keeping: every guard downstream now reads one normalized value, so the next check added inherits it instead of having to remember.

Exercised the bypasses directly rather than reasoning about them:

Input Result
## Indented heading (3 spaces) fails, markdown structure
- indented bullet fails, markdown structure
tab-indented ## Heading fails, markdown structure
Padded sentence. ok, published trimmed
whitespace-only line, real intro below ok, skips to the intro
This repo's real README ok

actionlint exits 0.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.github/workflows/build-docker-task.yml:131

  • The “markdown structure” guard doesn’t reject some common non-intro markdown constructs. In particular, an image/badge line (starts with !) or a tilde code fence (~~~) would currently pass the case check and could be published as the Docker Hub short description if the intro line is moved/removed. Also consider rejecting ordered list items (e.g. 1. / 1)) for the same reason.
          case "$INTRO" in
            '#'* | '>'* | '-'* | '*'* | '+'* | '<'* | '|'* | '`'* | '['*)
              echo "::error::Line after the README H1 is markdown structure, not the intro sentence: '$INTRO'"
              exit 1
              ;;
          esac

Copilot AI review requested due to automatic review settings August 1, 2026 17:04
@ptr727

ptr727 commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Fixed in d6889044cb19c9025f2597376b90c0bb7ed10ddf. All three accepted: ! (image or badge) and ~ (tilde fence) added to the case pattern, plus an ordered-list check.

The ordered list is a regex rather than a case glob, since a glob cannot express "one or more digits" without also swallowing an ordinary sentence that opens with a number. Verified in both directions, which matters more than the reject cases:

Input Result
![Badge](https://x.example/b.svg) fails, structure
~~~text fails, structure
1. First item fails, ordered list
12) Twelfth item fails, ordered list
2024 was the year it shipped. passes, no false positive
## Indented heading fails, structure
This repo's real README passes

Writing that test caught a real bug. Inline after =~, bash cannot parse the ) inside the bracket expression, and the step would have died with a syntax error at publish time. actionlint did not flag it. Holding the pattern in a variable is the form that parses, and the run block now also survives a syntax-only parse of the extracted script.

That is the third round on this step and each one found something real, so thanks. I am stopping the loop here rather than continuing to enumerate markdown constructs: the remaining space is unbounded, the guard now covers every block-opening character CommonMark defines plus links and the length cap, and the failure it exists to prevent (a moved intro silently publishing the next line) is closed. If a construct does slip through later, the step fails loudly on the length or link check or publishes something obviously wrong, rather than failing silently.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.github/workflows/build-docker-task.yml:150

  • The Docker Hub short description limit is enforced in bytes, but the guard uses ${#INTRO} (character count under UTF-8 locales). This can allow multi-byte Unicode intros (e.g., an en-dash or emoji) to pass the check while still exceeding Docker Hub’s 100-byte cap, leading to truncation or API failure despite the “fail loud” intent.
          if (( ${#INTRO} > 100 )); then
            echo "::error::README intro is ${#INTRO} characters; Docker Hub caps the short description at 100."
            exit 1
          fi

@ptr727
ptr727 merged this pull request into develop Aug 1, 2026
14 checks passed
@ptr727
ptr727 deleted the conformance/dockerhub-short-description branch August 1, 2026 17:07
ptr727 added a commit that referenced this pull request Aug 3, 2026
* Set the Docker Hub short description from the README intro

The audit's description finding had no mechanism behind it. The docker
task pushed the Docker Hub overview from Docker/README.md but never set
the short description, so nothing maintained the tagline and it kept
whatever was typed into the web UI once. That is why it drifted from the
README and why the audit could flag it but no workflow could fix it.

The step now reads the intro from README.md rather than restating it,
since a literal here would be one more surface to drift, and passes it to
the action's short-description input.

Two guards fail the publish loudly instead of pushing a blank or
truncated tagline, because a restructured README that moves the intro
would otherwise silently blank the Docker Hub description: an empty
result is an error, and so is one over Docker Hub's 100-character cap.
The CR strip matters because README.md is CRLF and a bare CR makes awk
read the blank line after the H1 as a non-empty line.

Verified: actionlint (which bundles shellcheck) exits 0, and the script
was exercised over four inputs - a good CRLF file, a missing intro, a
120-character intro, and an LF-only file - confirming both guards fire
rather than assuming they would.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Reject markdown structure and links in the extracted intro

Copilot caught that the empty guard covered the wrong failure. Deleting
an empty README is not the realistic case; moving or removing the intro
line is, and then awk picks up the next construct. Here that is the
'## Build and Distribution' heading, which is non-empty and under the
cap, so both existing guards pass and a heading ships as the Docker Hub
tagline. That is precisely the silent-narrowing shape this step claimed
to prevent, so the claim was wrong rather than the code merely thin.

Now a line opening a markdown block is rejected, covering headings,
lists, quotes, tables, fences, HTML, and link labels. A line carrying an
inline or reference link is rejected too, since readme-structure.md
requires the intro to be link-free and neither Docker Hub nor the About
panel renders markdown, so a link would ship as raw source.

Exercised over ten inputs rather than reasoning about them: CRLF and LF
happy paths, an absent intro, a heading, a list, an HTML comment, an
inline link, a reference link, a 120-character line, and the repo's real
README. Every guard fires on its own case and only on its own case.

Fixed a bug in the link check while testing it: the pattern was anchored
to the end of the string, so it would only have caught a link at the very
end of the line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Trim the extracted intro so the structure guard cannot be bypassed

Copilot caught that the structure guard read an untrimmed line while
CommonMark allows up to three leading spaces before a heading or list
marker, so an indented '## ...' would pass the case check and ship as the
Docker Hub tagline. Trailing space would pad the published string.

Trimming in the awk extraction rather than at each check means every
guard downstream reads the same normalized value, so the next check added
inherits it instead of having to remember.

Exercised the bypasses directly: a three-space heading, a two-space
bullet, and a tab-indented heading now all fail on structure, a padded
sentence is published trimmed, and a whitespace-only line is skipped in
favour of the real intro below it. actionlint exits 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Reject badges, tilde fences, and ordered lists in the intro

Copilot's third pass on this guard: a bare image or badge line starts
with '!', a tilde fence with '~', and neither was in the case pattern,
so either would have shipped as the tagline. Both added, along with an
ordered-list check.

The ordered list is a regex rather than a case glob, since a glob cannot
express "one or more digits" without also swallowing an ordinary sentence
that opens with a number. Verified both directions: "1. First item" and
"12) Twelfth item" fail, while "2024 was the year it shipped." passes.

Testing caught a real bug in that regex. Written inline after =~, bash
fails to parse the ')' inside the bracket expression and the step would
have died with a syntax error at publish time. actionlint did not flag
it. The pattern is held in a variable, which is the form that parses, and
the run block now also survives a bash syntax-only parse check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Aug 3, 2026
This reverts commit 90504d38e274dad1dd1894dee7fe47d1881ae91f (#32).

Nothing is wrong with the implementation. It is the wrong place for it.
Setting the Docker Hub short description from the README intro affects
every docker project in the fleet, and the hub already owns the audit
half: spec/audit.py flags a short description that has drifted from the
README intro. Adding the push half in one repo leaves that repo diverged
from every other docker project and pre-empts a decision that is not the
repo's to make.

The hub is working out a deterministic approach. This reverts to the
audit-only state so the repo matches the fleet while that happens, and
so a later develop -> main promotion cannot carry the mechanism silently.

The workflow is now byte-identical to its state before #32, and the
implementation with its ten test cases is preserved in #32 for whoever
picks the decision up.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants