feat(actions): add publish-docs and share one library between both actions - #82
Merged
oto-macenauer-absa merged 1 commit intoSep 4, 2026
Conversation
oto-macenauer-absa
force-pushed
the
chore/publish-docs-action
branch
from
September 4, 2026 13:33
35056ef to
f15278a
Compare
…tions
Packaged docs repos assembled the release asset by hand. The example repo's
pack.yml validated the manifest with an inline Python snippet, grepped the HTML
for the headless marker, ran `tar -czf dist.tar.gz dist/ marketplace.json` and
uploaded through a third-party action. Every new docs repo copied that file, so
every contract change had to be chased across N repositories by hand — and the
reusable validate-doc-app.yml did not help, because it assumed
`npm run build -- --headless`, which the Python example repo cannot run.
Single-page docs already had this solved. Packaged sites now do too.
actions/publish-docs takes an already-built headless site and owns everything
after it: validate the manifest against the contract schema, verify the HTML,
pack kb-docs.tar.gz, upload, optionally notify a deployment repo. It never
builds the site — doc repos use mkdocs, Starlight, Jekyll and hand-rolled
scripts, and the contract is about the output, not the toolchain.
What it rejects, all reported at once rather than one per CI run: a manifest
that fails the schema, an app with no built output, a missing entryPoint, a
`pages` entry naming a file that was not built, a page without the headless
marker (naming the pre-v1 marker specifically when it finds that instead), a
<base> element, and root-relative URLs. Inline scripts are a warning, not a
failure: the knowledge base hoists them, but the repo should know they are there.
Both actions now run the same code for the parts that are the contract —
actions/lib/ holds manifest validation, HTML verification, deterministic packing
and the runner plumbing — so the two cannot drift the way the build's two
install paths did. publish-single-page-docs keeps its own markdown pipeline and
emits the unified manifest through the shared builder; its bundle.json is gone.
Packing is deterministic: --sort=name plus a fixed mtime and uid/gid. Without
that, republishing an unchanged doc set produced a different asset every time and
nobody could tell a real change from a rebuild. Members are named explicitly
rather than packing `.`, so anything that reached the staging directory without
being part of the artifact is left out.
The schema is read from the action's own checkout rather than fetched at
runtime. A remote `uses:` checks out this whole repository at the ref the caller
pinned, so the schema always matches the action's version and a publish never
depends on raw.githubusercontent being reachable.
Also here:
* One pinned dependency tree in actions/, replacing the per-action one. Both
actions install from it; CI caches and runs against it.
* release-actions.yml moves the floating major tag, so consuming repos pin @v1
rather than @master. A branch means every doc repo picks up an unreleased
change the moment it merges, which is what a contract must not do.
* validate-doc-app.yml deleted. #74 had already reduced it to a deprecation
notice; the action it points at now exists.
Self-tests: 18 for publish-docs, 19 for publish-single-page-docs, both run by
CI. They assert the error messages as much as the happy path, because those
messages are the whole interface a docs repo has with the contract.
Suites green: 118 embedded, 22 standalone, both self-tests, all YAML manifests
parse.
Part of #73. Closes #76.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PYtmxSeSXFg4wEXRcKCmAi
oto-macenauer-absa
force-pushed
the
chore/publish-docs-action
branch
from
September 4, 2026 13:36
f15278a to
bd1d0cd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #76. Part of #73.
Stacked on #81 (
chore/source-only-registry) → #80 → #79. Review in that order; the diff here is only the actions work.Why
Packaged docs repos assembled the release asset by hand. The example repo's
pack.ymlvalidated the manifest with an inline Python snippet, grepped the HTML for the headless marker, rantar -czf dist.tar.gz dist/ marketplace.jsonand uploaded through a third-party action. Every new docs repo copied that file, so every contract change had to be chased across N repositories.The reusable
validate-doc-app.ymldid not help: it assumednpm run build -- --headless, which the Python example repo cannot run, so it was not what any real doc repo used.Single-page docs already had this solved. Packaged sites now do too.
actions/publish-docsTakes an already-built headless site and owns everything after it. It never builds the site: doc repos use mkdocs, Starlight, Jekyll and hand-rolled scripts, and the contract is about the output, not the toolchain.
What it rejects, all reported at once rather than one per CI run:
entryPointpagesentry naming a file that was not built<base>element, and root-relativehref/src/action/posterInline
<script>is a warning, not a failure. The knowledge base hoists them so it can servescript-src 'self', but the repo should know they are there.One library, so the two actions cannot drift
actions/lib/holds manifest validation, HTML verification, deterministic packing and the runner plumbing. Both actions run that same code for the parts that are the contract.publish-single-page-docskeeps its own markdown pipeline and emits the unified manifest through the shared builder; itsbundle.jsonis gone.This is the same failure mode #75 fixed in the build, one layer out: two implementations of one idea, drifting.
Three details worth a look in review
Packing is deterministic.
--sort=nameplus a fixed mtime and uid/gid. Without it, republishing an unchanged doc set produced different bytes every time and nobody could tell a real change from a rebuild. Members are named explicitly rather than packing., so anything that reached the staging directory without being part of the artifact is left out.The schema is read from the checkout, not fetched. A remote
uses:checks out this whole repository at the ref the caller pinned, so the schema always matches the action's own version and a publish never depends onraw.githubusercontentbeing reachable. The oldvalidate-doc-app.ymlfetched it and skipped validation with a warning when the fetch failed.distis the app's directory when the manifest declares one app. A repo publishing a single site should not have to invent a subdirectory named after its own slug. With several apps,distholds one subdirectory per slug, and the error says which one is missing.Also here
actions/, replacing the per-action one. Both actions install from it; CI caches and runs against it.release-actions.ymlmoves the floating major tag, so consuming repos pin@v1. A branch means every doc repo picks up an unreleased change the moment it merges, which is what a contract must not do. A breaking change ships asv2alongside akbVersionbump.validate-doc-app.ymldeleted. Contract v1: rename dist.tar.gz to kb-docs.tar.gz and unify marketplace.json + bundle.json into kb-docs.json #74 had already reduced it to a deprecation notice; the action it points at now exists.notify-repo/notify-tokenfire akb-docs-publishedrepository_dispatchso a deployment repo rebuilds on publish (Deployment: reusable build-image workflow, --strict registry mode, and the private deployment repo definition #78 consumes it). A failed notify never fails the publish — the artifact is already released.Verification
publish-docsself-testpublish-single-page-docsself-testnpm test(embedded)playwright.config.ci.js(standalone)action.yml/ workflow YAML parsesThe self-tests assert the error messages as much as the happy path. This action is the whole interface a docs repo has with the contract, so a message that does not say which file is wrong costs somebody a CI round trip.
Not in this PR
Migrating
AbsaOSS/knowledge-base-docs-exampleonto the action, tracked in AbsaOSS/knowledge-base-docs-example#3. Until that lands and av1tag is cut, the@v1references in the contract docs point at a tag that does not exist yet.🤖 Generated with Claude Code
https://claude.ai/code/session_01PYtmxSeSXFg4wEXRcKCmAi