From db352c60596ecfc5ecdc1ec786f40ba056e9c39f Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Fri, 26 Jun 2026 14:59:16 -0400 Subject: [PATCH] docs: native project documentation Adds native project documentation that will be rendered with Zensical and published through GitHub Pages. Signed-off-by: Ryan Johnson --- Makefile | 71 +++- README.md | 6 +- Taskfile.yml | 111 ++++++ docs-site/.gitignore | 4 + docs-site/assets/header-logo.png | Bin 0 -> 98619 bytes docs-site/extra/builders/index.md | 11 + docs-site/extra/community/code-of-conduct.md | 136 +++++++ docs-site/extra/community/contributing.md | 144 +++++++ docs-site/extra/community/index.md | 10 + docs-site/extra/community/issues-guidance.md | 27 ++ docs-site/extra/community/license.md | 1 + docs-site/extra/community/support.md | 17 + docs-site/extra/data-sources/index.md | 11 + docs-site/extra/intro-lower.md | 8 + docs-site/extra/intro-upper.md | 10 + docs-site/extra/post-processors/index.md | 11 + docs-site/javascripts/version-slot.js | 108 ++++++ docs-site/overrides/main.html | 36 ++ docs-site/overrides/partials/header.html | 72 ++++ docs-site/overrides/partials/nav-item.html | 174 +++++++++ docs-site/overrides/partials/nav.html | 32 ++ docs-site/requirements.txt | 2 + docs-site/scripts/README.md | 74 ++++ docs-site/scripts/lib/convert-admonitions.sh | 228 +++++++++++ .../scripts/lib/convert-github-alerts.sh | 111 ++++++ .../scripts/lib/deepen-asterisk-sublists.sh | 42 ++ docs-site/scripts/lib/fix-internal-links.sh | 81 ++++ .../scripts/lib/format-example-labels.sh | 282 ++++++++++++++ docs-site/scripts/lib/generate-nav.sh | 102 +++++ docs-site/scripts/lib/group-example-tabs.sh | 354 +++++++++++++++++ .../scripts/lib/inject-home-data-sources.sh | 72 ++++ docs-site/scripts/lib/inject-home-sections.sh | 40 ++ docs-site/scripts/lib/mike-env.sh | 71 ++++ .../scripts/lib/normalize-list-spacing.sh | 86 +++++ docs-site/scripts/lib/paths.sh | 19 + docs-site/scripts/lib/repair-code-fences.sh | 59 +++ .../scripts/lib/rewrite-integration-links.sh | 33 ++ docs-site/scripts/lib/stage-markdown.sh | 69 ++++ .../scripts/lib/strip-codegen-comments.sh | 23 ++ docs-site/scripts/lib/sync-web-docs.sh | 24 ++ docs-site/scripts/mike-backfill.sh | 141 +++++++ docs-site/scripts/mike-deploy.sh | 114 ++++++ docs-site/scripts/mike-preview.sh | 264 +++++++++++++ docs-site/scripts/prepare-docs.sh | 181 +++++++++ docs-site/scripts/test/assertions.sh | 31 ++ docs-site/scripts/test/test-all.sh | 32 ++ .../scripts/test/test-convert-admonitions.sh | 200 ++++++++++ .../test/test-convert-github-alerts.sh | 65 ++++ .../test/test-deepen-asterisk-sublists.sh | 38 ++ .../scripts/test/test-fix-internal-links.sh | 58 +++ .../test/test-format-example-labels.sh | 336 ++++++++++++++++ docs-site/scripts/test/test-generate-nav.sh | 43 +++ .../scripts/test/test-group-example-tabs.sh | 236 ++++++++++++ .../test/test-inject-home-data-sources.sh | 93 +++++ .../test/test-normalize-list-spacing.sh | 102 +++++ .../scripts/test/test-repair-code-fences.sh | 53 +++ .../test/test-rewrite-integration-links.sh | 64 +++ docs-site/scripts/test/test-stage-markdown.sh | 60 +++ .../test/test-strip-codegen-comments.sh | 43 +++ docs-site/stylesheets/extra.css | 364 ++++++++++++++++++ docs-site/zensical.toml | 91 +++++ 61 files changed, 5376 insertions(+), 5 deletions(-) create mode 100644 docs-site/.gitignore create mode 100644 docs-site/assets/header-logo.png create mode 100644 docs-site/extra/builders/index.md create mode 100644 docs-site/extra/community/code-of-conduct.md create mode 100644 docs-site/extra/community/contributing.md create mode 100644 docs-site/extra/community/index.md create mode 100644 docs-site/extra/community/issues-guidance.md create mode 100644 docs-site/extra/community/license.md create mode 100644 docs-site/extra/community/support.md create mode 100644 docs-site/extra/data-sources/index.md create mode 100644 docs-site/extra/intro-lower.md create mode 100644 docs-site/extra/intro-upper.md create mode 100644 docs-site/extra/post-processors/index.md create mode 100644 docs-site/javascripts/version-slot.js create mode 100644 docs-site/overrides/main.html create mode 100644 docs-site/overrides/partials/header.html create mode 100644 docs-site/overrides/partials/nav-item.html create mode 100644 docs-site/overrides/partials/nav.html create mode 100644 docs-site/requirements.txt create mode 100644 docs-site/scripts/README.md create mode 100755 docs-site/scripts/lib/convert-admonitions.sh create mode 100755 docs-site/scripts/lib/convert-github-alerts.sh create mode 100755 docs-site/scripts/lib/deepen-asterisk-sublists.sh create mode 100755 docs-site/scripts/lib/fix-internal-links.sh create mode 100755 docs-site/scripts/lib/format-example-labels.sh create mode 100755 docs-site/scripts/lib/generate-nav.sh create mode 100755 docs-site/scripts/lib/group-example-tabs.sh create mode 100644 docs-site/scripts/lib/inject-home-data-sources.sh create mode 100755 docs-site/scripts/lib/inject-home-sections.sh create mode 100755 docs-site/scripts/lib/mike-env.sh create mode 100755 docs-site/scripts/lib/normalize-list-spacing.sh create mode 100755 docs-site/scripts/lib/paths.sh create mode 100755 docs-site/scripts/lib/repair-code-fences.sh create mode 100755 docs-site/scripts/lib/rewrite-integration-links.sh create mode 100755 docs-site/scripts/lib/stage-markdown.sh create mode 100755 docs-site/scripts/lib/strip-codegen-comments.sh create mode 100644 docs-site/scripts/lib/sync-web-docs.sh create mode 100755 docs-site/scripts/mike-backfill.sh create mode 100755 docs-site/scripts/mike-deploy.sh create mode 100755 docs-site/scripts/mike-preview.sh create mode 100755 docs-site/scripts/prepare-docs.sh create mode 100755 docs-site/scripts/test/assertions.sh create mode 100755 docs-site/scripts/test/test-all.sh create mode 100755 docs-site/scripts/test/test-convert-admonitions.sh create mode 100755 docs-site/scripts/test/test-convert-github-alerts.sh create mode 100755 docs-site/scripts/test/test-deepen-asterisk-sublists.sh create mode 100755 docs-site/scripts/test/test-fix-internal-links.sh create mode 100755 docs-site/scripts/test/test-format-example-labels.sh create mode 100755 docs-site/scripts/test/test-generate-nav.sh create mode 100755 docs-site/scripts/test/test-group-example-tabs.sh create mode 100755 docs-site/scripts/test/test-inject-home-data-sources.sh create mode 100755 docs-site/scripts/test/test-normalize-list-spacing.sh create mode 100755 docs-site/scripts/test/test-repair-code-fences.sh create mode 100755 docs-site/scripts/test/test-rewrite-integration-links.sh create mode 100755 docs-site/scripts/test/test-stage-markdown.sh create mode 100755 docs-site/scripts/test/test-strip-codegen-comments.sh create mode 100644 docs-site/stylesheets/extra.css create mode 100644 docs-site/zensical.toml diff --git a/Makefile b/Makefile index 7c9c00ed..ca434dab 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ COUNT?=1 TEST?=$(shell go list ./...) HASHICORP_PACKER_PLUGIN_SDK_VERSION?=$(shell go list -m github.com/hashicorp/packer-plugin-sdk | cut -d " " -f2) -.PHONY: dev +.PHONY: dev build test install-packer-sdc plugin-check testacc generate docs-deps docs-prepare docs-test docs-test-links docs-test-internal-links docs-test-admonitions docs-test-example-labels docs-test-group-example-tabs docs-test-normalize docs-test-github-alerts docs-build docs-serve docs-serve-version docs-serve-mike docs-serve-mike-only docs-backfill build: @go build -o ${BINARY} @@ -18,7 +18,7 @@ dev: test: @go test -race -count $(COUNT) $(TEST) -timeout=3m -install-packer-sdc: # Install packer software development command. +install-packer-sdc: ## Install packer sofware development command @go install github.com/hashicorp/packer-plugin-sdk/cmd/packer-sdc@${HASHICORP_PACKER_PLUGIN_SDK_VERSION} plugin-check: install-packer-sdc build @@ -29,7 +29,74 @@ testacc: dev generate: install-packer-sdc @go generate ./... + @go fmt ./... @rm -rf .docs @packer-sdc renderdocs -src "docs" -partials docs-partials/ -dst ".docs/" @./.web-docs/scripts/compile-to-webdocs.sh "." ".docs" ".web-docs" "hashicorp" @rm -r ".docs" + +DOCS_VENV?=$(CURDIR)/docs-site/.venv +DOCS_PYTHON=$(DOCS_VENV)/bin/python +DOCS_PIP=$(DOCS_VENV)/bin/pip + +docs-deps: + @test -d "$(DOCS_VENV)" || python3 -m venv "$(DOCS_VENV)" + @"$(DOCS_PIP)" install -r docs-site/requirements.txt + +docs-prepare: + @./docs-site/scripts/prepare-docs.sh + +docs-test: + @./docs-site/scripts/test/test-all.sh + +docs-test-links: + @./docs-site/scripts/test/test-rewrite-integration-links.sh + +docs-test-internal-links: + @./docs-site/scripts/test/test-fix-internal-links.sh + +docs-test-admonitions: + @./docs-site/scripts/test/test-convert-admonitions.sh + +docs-test-example-labels: + @./docs-site/scripts/test/test-format-example-labels.sh + +docs-test-group-example-tabs: + @./docs-site/scripts/test/test-group-example-tabs.sh + +docs-test-normalize: + @./docs-site/scripts/test/test-normalize-list-spacing.sh + +docs-test-strip-codegen: + @./docs-site/scripts/test/test-strip-codegen-comments.sh + +docs-test-repair-fences: + @./docs-site/scripts/test/test-repair-code-fences.sh + +docs-test-stage-markdown: + @./docs-site/scripts/test/test-stage-markdown.sh + +docs-test-github-alerts: + @./docs-site/scripts/test/test-convert-github-alerts.sh + +docs-build: generate docs-deps docs-prepare + @cd docs-site && "$(DOCS_VENV)/bin/zensical" build --config-file zensical.build.toml + +docs-serve: generate docs-deps docs-prepare + @cd docs-site && "$(DOCS_VENV)/bin/zensical" serve --config-file zensical.build.toml + +docs-serve-version: docs-deps + @test -n "$(VERSION)" || (echo "VERSION is required, e.g. make docs-serve-version VERSION=2.1.0" && exit 1) + @rm -rf .web-docs + @git checkout "v$(VERSION)" -- .web-docs + @INCLUDE_EXTRA=true ./docs-site/scripts/prepare-docs.sh + @cd docs-site && "$(DOCS_VENV)/bin/zensical" serve --config-file zensical.build.toml + +docs-serve-mike: generate docs-deps + @[ -z "$(VERSIONS)" ] || export MIKE_PREVIEW_VERSIONS="$(VERSIONS)"; ./docs-site/scripts/mike-preview.sh + +docs-serve-mike-only: docs-deps + @./docs-site/scripts/mike-preview.sh --serve-only + +docs-backfill: docs-deps + @./docs-site/scripts/mike-backfill.sh $(VERSIONS) diff --git a/README.md b/README.md index af2e33ad..15e3df1a 100644 --- a/README.md +++ b/README.md @@ -131,9 +131,9 @@ Licensed under the [Mozilla Public License, version 2.0][license]. [desktop-hypervisors]: https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion [docs-packer-init]: https://developer.hashicorp.com/packer/docs/commands/init [docs-packer-plugin-install]: https://developer.hashicorp.com/packer/docs/plugins/install-plugins -[docs-vmware-plugin]: https://developer.hashicorp.com/packer/integrations/vmware/vmware/latest/ -[docs-vmware-iso]: https://developer.hashicorp.com/packer/integrations/vmware/vmware/latest/components/builder/iso -[docs-vmware-vmx]: https://developer.hashicorp.com/packer/integrations/vmware/vmware/latest/components/builder/vmx +[docs-vmware-plugin]: https://vmware.github.io/packer-plugin-vmware/latest/ +[docs-vmware-iso]: https://vmware.github.io/packer-plugin-vmware/latest/builders/iso/ +[docs-vmware-vmx]: https://vmware.github.io/packer-plugin-vmware/latest/builders/vmx/ [golang-install]: https://golang.org/doc/install [releases-vmware-plugin]: https://github.com/vmware/packer-plugin-vmware/releases [packer-plugin-vsphere]: https://developer.hashicorp.com/packer/integrations/vmware/vsphere diff --git a/Taskfile.yml b/Taskfile.yml index cf1bfdf4..7a42f103 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -59,3 +59,114 @@ tasks: packer-sdc renderdocs -src "docs" -partials docs-partials/ -dst ".docs/" ./.web-docs/scripts/compile-to-webdocs.sh "." ".docs" ".web-docs" "hashicorp" rm -r ".docs" + + docs-deps: + desc: Install Zensical and mike into docs-site/.venv for local docs preview. + cmds: + - test -d docs-site/.venv || python3 -m venv docs-site/.venv + - docs-site/.venv/bin/pip install -r docs-site/requirements.txt + + docs-prepare: + desc: Stage .web-docs into docs-site/.build/docs for Zensical. + cmds: + - ./docs-site/scripts/prepare-docs.sh + + docs-test: + desc: Run all docs-site script unit tests. + cmds: + - ./docs-site/scripts/test/test-all.sh + + docs-test-links: + desc: Run integration link rewrite tests for staged documentation. + cmds: + - ./docs-site/scripts/test/test-rewrite-integration-links.sh + + docs-test-internal-links: + desc: Run internal link and anchor fix tests for staged documentation. + cmds: + - ./docs-site/scripts/test/test-fix-internal-links.sh + + docs-test-admonitions: + desc: Run Material-to-Zensical admonition conversion tests. + cmds: + - ./docs-site/scripts/test/test-convert-admonitions.sh + + docs-test-example-labels: + desc: Run HCL/JSON example label formatting tests. + cmds: + - ./docs-site/scripts/test/test-format-example-labels.sh + + docs-test-group-example-tabs: + desc: Run HCL/JSON content tab grouping tests. + cmds: + - ./docs-site/scripts/test/test-group-example-tabs.sh + + docs-test-normalize: + desc: Run staged markdown list spacing tests. + cmds: + - ./docs-site/scripts/test/test-normalize-list-spacing.sh + + docs-test-strip-codegen: + desc: Run strip-codegen-comments tests. + cmds: + - ./docs-site/scripts/test/test-strip-codegen-comments.sh + + docs-test-repair-fences: + desc: Run repair-code-fences tests. + cmds: + - ./docs-site/scripts/test/test-repair-code-fences.sh + + docs-test-stage-markdown: + desc: Run staged markdown pipeline integration tests. + cmds: + - ./docs-site/scripts/test/test-stage-markdown.sh + + docs-test-github-alerts: + desc: Run GitHub alert to admonition conversion tests. + cmds: + - ./docs-site/scripts/test/test-convert-github-alerts.sh + + docs-build: + desc: Preview what documentation will look like at the next release (build). + deps: [generate, docs-deps, docs-prepare] + cmds: + - cd docs-site && ../docs-site/.venv/bin/zensical build --config-file zensical.build.toml + + docs-serve: + desc: Preview what documentation will look like at the next release (live). + deps: [generate, docs-deps, docs-prepare] + cmds: + - cd docs-site && ../docs-site/.venv/bin/zensical serve --config-file zensical.build.toml + + docs-serve-version: + desc: "Preview documentation from a released version." + deps: [docs-deps] + cmds: + - | + test -n "{{.VERSION}}" || (echo "VERSION is required" && exit 1) + rm -rf .web-docs + git checkout "v{{.VERSION}}" -- .web-docs + INCLUDE_EXTRA=true ./docs-site/scripts/prepare-docs.sh + cd docs-site && ../docs-site/.venv/bin/zensical serve --config-file zensical.build.toml + + docs-serve-mike: + desc: Preview versioned documentation locally with mike (latest=last tag, development=branch name). + deps: [generate, docs-deps] + env: + MIKE_PREVIEW_VERSIONS: "{{.VERSIONS}}" + cmds: + - ./docs-site/scripts/mike-preview.sh + + docs-serve-mike-only: + desc: Serve an existing local mike preview without redeploying. + deps: [docs-deps] + cmds: + - ./docs-site/scripts/mike-preview.sh --serve-only + + docs-backfill: + desc: Backfill versioned documentation to GitHub Pages. + deps: [docs-deps] + env: + MIKE_BACKFILL_VERSIONS: "{{.VERSIONS}}" + cmds: + - ./docs-site/scripts/mike-backfill.sh {{.CLI_ARGS}} diff --git a/docs-site/.gitignore b/docs-site/.gitignore new file mode 100644 index 00000000..d30e3d9a --- /dev/null +++ b/docs-site/.gitignore @@ -0,0 +1,4 @@ +.build/ +site/ +.venv/ +zensical.build.toml diff --git a/docs-site/assets/header-logo.png b/docs-site/assets/header-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f8eddb9625f7b0d390d836caaa6f9427da955d75 GIT binary patch literal 98619 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 z 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/.git +git checkout -b feat/add-x main +git commit --signoff --message "feat: add support for x + Added support for x. + + Signed-off-by: Jane Doe + + Ref: #123" +git push origin feat/add-x +``` + +**Formatting Commit Messages** + +Follow the conventions on [How to Write a Git Commit Message][git-commit] and use +[Conventional Commits][conventional-commits]. + +Be sure to include any related GitHub issue references in the commit message. + +Example: + +```markdown +feat: add support for x + +Added support for x. + +Signed-off-by: Jane Doe + +Ref: #123 +``` + +**Stay In Sync With Upstream** + +When your branch gets out of sync with the `upstream/main` branch, use the +following to update: + +```shell +git checkout feat/add-x +git fetch --all +git pull --rebase upstream main +git push --force-with-lease origin feat/add-x +``` + +**Updating Pull Requests** + +If your pull request fails to pass or needs changes based on code review, you'll +most likely want to squash these changes into existing commits. + +If your pull request contains a single commit or your changes are related to the +most recent commit, you can simply amend the commit. + +```shell +git add . +git commit --amend +git push --force-with-lease origin feat/add-x +``` + +If you need to squash changes into an earlier commit, you can use: + +```shell +git add . +git commit --fixup +git rebase --interactive --autosquash upstream/main +git push --force-with-lease origin feat/add-x +``` + +When resolving review comments, mark the conversation as resolved and note the commit +SHA that addresses the review comment. This helps maintainers verify the issue has been +resolved. + +Request a review from the maintainers when you are ready for a follow-up review. + +[conventional-commits]: https://conventionalcommits.org +[gh-discussions]: https://github.com/vmware/packer-plugin-vmware/discussions +[gh-forks]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo +[gh-issues]: https://github.com/vmware/packer-plugin-vmware/issues +[gh-pull-requests]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request +[git-commit]: https://cbea.ms/git-commit +[product-lifecycle]: https://support.broadcom.com/group/ecx/productlifecycle diff --git a/docs-site/extra/community/index.md b/docs-site/extra/community/index.md new file mode 100644 index 00000000..8ca1ac47 --- /dev/null +++ b/docs-site/extra/community/index.md @@ -0,0 +1,10 @@ +--- +icon: lucide/users +title: Community +--- + +# Community + +We greatly value feedback and contributions from our community. + +Select a section from the navigation to engage with project maintainers and the plugin community. diff --git a/docs-site/extra/community/issues-guidance.md b/docs-site/extra/community/issues-guidance.md new file mode 100644 index 00000000..bcc5b5f4 --- /dev/null +++ b/docs-site/extra/community/issues-guidance.md @@ -0,0 +1,27 @@ +## Issues + +Use [GitHub issues][gh-issues] to report bugs or suggest enhancements using the +following guidelines. + +!!! warning + + Issues that do not follow the guidelines may be closed by the maintainers without + further investigation. + +Before opening an issue, please [search existing issues](https://github.com/vmware/packer-plugin-vmware/issues?q=is%3Aissue+is%3Aopen+label%3Abug) +to avoid duplicates. + +When opening an issue, use the provided issue form to ensure that you provide all the +necessary details. These details are important for maintainers to understand and +reproduce the issue. + +!!! warning + + - Ensure that you are using a recent version of the plugin. + - Ensure that you are using a supported version of VMware vSphere. The plugin supports versions in accordance with the [Broadcom Product Lifecycle][product-lifecycle]. + +!!! tip + + - Learn about [formatting code on GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code). + - Learn about [referencing issues](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#referencing-issues-and-pull-requests). + - Learn about [creating a GitHub Gist](https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists). diff --git a/docs-site/extra/community/license.md b/docs-site/extra/community/license.md new file mode 100644 index 00000000..160fd8ed --- /dev/null +++ b/docs-site/extra/community/license.md @@ -0,0 +1 @@ +--8<-- "./LICENSE" diff --git a/docs-site/extra/community/support.md b/docs-site/extra/community/support.md new file mode 100644 index 00000000..77a44fb0 --- /dev/null +++ b/docs-site/extra/community/support.md @@ -0,0 +1,17 @@ +--- +title: Support +--- + +# Support + +While this plugin is **not supported** by Broadcom Support, it is +supported by the project maintainers and the plugin community. + +--8<-- "community/issues-guidance.md" + +:lucide-messages-square: You can also start a [discussion][discussions] to ask +questions or share ideas. + +[discussions]: https://github.com/vmware/packer-plugin-vmware/discussions +[gh-issues]: https://github.com/vmware/packer-plugin-vmware/issues +[product-lifecycle]: https://support.broadcom.com/group/ecx/productlifecycle diff --git a/docs-site/extra/data-sources/index.md b/docs-site/extra/data-sources/index.md new file mode 100644 index 00000000..4a8e5493 --- /dev/null +++ b/docs-site/extra/data-sources/index.md @@ -0,0 +1,11 @@ +--- +icon: lucide/database +title: Data Sources +--- + +# Data Sources + +Data sources read information from VMware desktop hypervisors, VMware Fusion Pro and VMware +Workstation Pro, to use elsewhere in your configurations. + +Select a data source from the navigation for the configuration reference. diff --git a/docs-site/extra/intro-lower.md b/docs-site/extra/intro-lower.md new file mode 100644 index 00000000..6d684add --- /dev/null +++ b/docs-site/extra/intro-lower.md @@ -0,0 +1,8 @@ +### Requirements + +The plugin supports versions in accordance with the +[Broadcom Product Lifecycle](https://support.broadcom.com/group/ecx/productlifecycle). + +### Support + +The plugin is [supported](community/support/) by the project maintainers and the plugin community. diff --git a/docs-site/extra/intro-upper.md b/docs-site/extra/intro-upper.md new file mode 100644 index 00000000..58969b3e --- /dev/null +++ b/docs-site/extra/intro-upper.md @@ -0,0 +1,10 @@ +
+ +
+ +[Partner :lucide-handshake:](https://developer.hashicorp.com/packer/integrations/vmware/vsphere){: .md-button target="_blank" rel="noopener" } +[HCP Ready :lucide-rocket:](https://developer.hashicorp.com/hcp/docs/packer){: .md-button target="_blank" rel="noopener" } + +
+ +
diff --git a/docs-site/extra/post-processors/index.md b/docs-site/extra/post-processors/index.md new file mode 100644 index 00000000..f575eb3e --- /dev/null +++ b/docs-site/extra/post-processors/index.md @@ -0,0 +1,11 @@ +--- +icon: lucide/cog +title: Post-Processors +--- + +# Post-Processors + +Post-processors run after a build completes to publish artifacts to VMware desktop hypervisors, +VMware Fusion Pro and VMware Workstation Pro.. + +Select a post-processor from the navigation for the configuration reference. diff --git a/docs-site/javascripts/version-slot.js b/docs-site/javascripts/version-slot.js new file mode 100644 index 00000000..88c4ae61 --- /dev/null +++ b/docs-site/javascripts/version-slot.js @@ -0,0 +1,108 @@ +(function () { + var scheduled = false; + + function scheduleInit() { + if (scheduled) { + return; + } + scheduled = true; + requestAnimationFrame(function () { + scheduled = false; + init(); + }); + } + + function updateOutdatedBannerVersion() { + var el = document.querySelector(".md-version-banner__version"); + if (!el) { + return; + } + + var match = location.pathname.match(/\/(\d+\.\d+(?:\.\d+)?)(?:\/|$)/); + if (match) { + el.textContent = match[1]; + return; + } + + if (/\/development(?:\/|$)/.test(location.pathname)) { + el.textContent = "development"; + } + } + + function relocateVersion() { + var slot = document.getElementById("md-version-slot"); + if (!slot) { + return; + } + + var mount = document.querySelector(".md-header__topic--version-mount"); + if (mount) { + var mountVersion = mount.querySelector(".md-version"); + if (mountVersion && !slot.contains(mountVersion)) { + slot.appendChild(mountVersion); + } + } + } + + function bindVersionLinks() { + document.querySelectorAll(".md-version__link").forEach(function (link) { + if (link.dataset.versionFullNav === "true") { + return; + } + link.dataset.versionFullNav = "true"; + link.addEventListener( + "click", + function (event) { + if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) { + return; + } + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + window.location.assign(link.href); + }, + true + ); + }); + } + + function init() { + updateOutdatedBannerVersion(); + relocateVersion(); + bindVersionLinks(); + } + + function observeMount() { + var mount = document.querySelector(".md-header__topic--version-mount"); + if (!mount) { + return; + } + new MutationObserver(scheduleInit).observe(mount, { + childList: true, + }); + } + + function observeSlot() { + var slot = document.getElementById("md-version-slot"); + if (!slot) { + return; + } + new MutationObserver(scheduleInit).observe(slot, { + childList: true, + }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", function () { + init(); + observeMount(); + observeSlot(); + }); + } else { + init(); + observeMount(); + observeSlot(); + } + + window.addEventListener("pageshow", init); +})(); diff --git a/docs-site/overrides/main.html b/docs-site/overrides/main.html new file mode 100644 index 00000000..9d9f2efc --- /dev/null +++ b/docs-site/overrides/main.html @@ -0,0 +1,36 @@ +{#- + Show a version warning on non-default mike versions (see Zensical versioning docs). + Version selector lives in the content meta bar (see version-slot.js). +-#} +{% extends "base.html" %} + +{% block container %} +
+ {% if "navigation.path" in features %} + {% include "partials/path.html" %} + {% endif %} + {% if config.extra.version %} +
+
+
+ {% endif %} +
+ {% block content %} + {% include "partials/content.html" %} + {% endblock %} +
+
+{% endblock %} + +{% block outdated %} +
+ +

+ You are viewing documentation for version + . + View latest version. +

+
+{% endblock %} diff --git a/docs-site/overrides/partials/header.html b/docs-site/overrides/partials/header.html new file mode 100644 index 00000000..0eaf0bbf --- /dev/null +++ b/docs-site/overrides/partials/header.html @@ -0,0 +1,72 @@ +{#- + Hide the site title in the header; the landing page H1 carries the name, and + other pages show only the current page title. +-#} +{% set class = "md-header" %} +{% if "navigation.tabs.sticky" in features %} + {% set class = class ~ " md-header--shadow md-header--lifted" %} +{% elif "navigation.tabs" not in features %} + {% set class = class ~ " md-header--shadow" %} +{% endif %} +
+ + {% if "navigation.tabs.sticky" in features %} + {% if "navigation.tabs" in features %} + {% include "partials/tabs.html" %} + {% endif %} + {% endif %} +
diff --git a/docs-site/overrides/partials/nav-item.html b/docs-site/overrides/partials/nav-item.html new file mode 100644 index 00000000..b2f2d34b --- /dev/null +++ b/docs-site/overrides/partials/nav-item.html @@ -0,0 +1,174 @@ +{#- + Theme override: Zensical does not set is_index on index.md nav items, so treat the + first child page in each section as the section index (index.md is always listed first). +-#} +{% macro render_status(nav_item, type) %} + {% set class = "md-status md-status--" ~ type %} + {% if config.extra.status and config.extra.status[type] %} + + + {% else %} + + {% endif %} +{% endmacro %} +{% macro render_title(nav_item, ref) %} + {% set ref = ref or nav_item %} + {{ ref.title or nav_item.title }} +{% endmacro %} +{% macro render_content(nav_item, ref) %} + {% set ref = ref or nav_item %} + {% if nav_item.meta and nav_item.meta.icon %} + {% include ".icons/" ~ nav_item.meta.icon ~ ".svg" %} + {% endif %} + + {{ render_title(nav_item, ref) }} + {% if nav_item.meta and nav_item.meta.subtitle %} +
+ {{ nav_item.meta.subtitle }} + {% endif %} +
+ {% if nav_item.meta and nav_item.encrypted %} + {{ render_status(nav_item, "encrypted") }} + {% endif %} + {% if nav_item.meta and nav_item.meta.status %} + {{ render_status(nav_item, nav_item.meta.status) }} + {% endif %} +{% endmacro %} +{% macro render_pruned(nav_item, ref) %} + {% set ref = ref or nav_item %} + {% set first = nav_item.children | first %} + {% if first and first.children %} + {{ render_pruned(first, ref) }} + {% else %} + + {{ render_content(ref) }} + {% if nav_item.children | length > 0 %} + + {% endif %} + + {% endif %} +{% endmacro %} +{% macro render(nav_item, path, level, parent) %} + {% set class = "md-nav__item" %} + {% if nav_item.active %} + {% set class = class ~ " md-nav__item--active" %} + {% endif %} + {% if nav_item.pages %} + {% if page in nav_item.pages %} + {% set nav_item = page %} + {% endif %} + {% endif %} + {% if nav_item.children %} + {% set _ = namespace(index = none) %} + {% if "navigation.indexes" in features %} + {% for item in nav_item.children %} + {% if item.is_index and _.index is none %} + {% set _.index = item %} + {% endif %} + {% endfor %} + {% endif %} + {% if _.index is none and nav_item.children | length > 0 %} + {% set first = nav_item.children | first %} + {% if not first.children %} + {% set _.index = first %} + {% endif %} + {% endif %} + {% set index = _.index %} + {% if "navigation.tabs" in features %} + {% if level == 1 and nav_item.active %} + {% set class = class ~ " md-nav__item--section" %} + {% set is_section = true %} + {% endif %} + {% if "navigation.sections" in features %} + {% if level == 2 and parent.active %} + {% set class = class ~ " md-nav__item--section" %} + {% set is_section = true %} + {% endif %} + {% endif %} + {% elif "navigation.sections" in features %} + {% if level == 1 %} + {% set class = class ~ " md-nav__item--section" %} + {% set is_section = true %} + {% endif %} + {% endif %} + {% if "navigation.prune" in features %} + {% if not is_section and not nav_item.active %} + {% set class = class ~ " md-nav__item--pruned" %} + {% set is_pruned = true %} + {% endif %} + {% endif %} +
  • + {% if not is_pruned %} + {% set checked = "checked" if nav_item.active %} + {% if "navigation.expand" in features and not checked %} + {% set indeterminate = "md-toggle--indeterminate" %} + {% endif %} + + {% if not index %} + {% set tabindex = "0" if not is_section %} + + {% else %} + {% set class = "md-nav__link--active" if index.active %} + + {% endif %} + + {% else %} + {{ render_pruned(nav_item, index) }} + {% endif %} +
  • + {% elif nav_item.active %} +
  • + {% set toc = page.toc %} + {% if config.theme.variant == "classic" %} + + {% endif %} + {% set first = toc | first %} + {% if first and first.level == 1 %} + {% set toc = first.children %} + {% endif %} + {% if toc %} + + {% endif %} + + {{ render_content(nav_item) }} + + {% if toc %} + {% include "partials/toc.html" %} + {% endif %} +
  • + {% else %} +
  • + + {{ render_content(nav_item) }} + +
  • + {% endif %} +{% endmacro %} diff --git a/docs-site/overrides/partials/nav.html b/docs-site/overrides/partials/nav.html new file mode 100644 index 00000000..47197810 --- /dev/null +++ b/docs-site/overrides/partials/nav.html @@ -0,0 +1,32 @@ +{#- + Hide the site title next to the logo in the mobile drawer on the landing page. +-#} +{% import "partials/nav-item.html" as item with context %} +{% set class = "md-nav md-nav--primary" %} +{% if "navigation.tabs" in features %} + {% set class = class ~ " md-nav--lifted" %} +{% endif %} +{% if "toc.integrate" in features %} + {% set class = class ~ " md-nav--integrated" %} +{% endif %} + diff --git a/docs-site/requirements.txt b/docs-site/requirements.txt new file mode 100644 index 00000000..b0bd8673 --- /dev/null +++ b/docs-site/requirements.txt @@ -0,0 +1,2 @@ +zensical>=0.0.46 +mike @ git+https://github.com/squidfunk/mike.git@2.2.0+zensical-0.1.0 diff --git a/docs-site/scripts/README.md b/docs-site/scripts/README.md new file mode 100644 index 00000000..f05ffe3b --- /dev/null +++ b/docs-site/scripts/README.md @@ -0,0 +1,74 @@ +# Documentation Scripts for Zensical and GitHub Pages + +Shell tooling that stages `.web-docs` for [Zensical](https://zensical.org) and publishes versioned +documentation with [`mike`](https://github.com/squidfunk/mike) to GitHub Pages. + +## Entry Points + +| Script | Purpose | +|-------------------|----------------------------------------------------------------------------------------------| +| `prepare-docs.sh` | Stage `.web-docs` and `extra/` into `.build/docs`, rewrite links, emit `zensical.build.toml` | +| `mike-deploy.sh` | Deploy one release version to `gh-pages` | +| `mike-backfill.sh` | Deploy multiple historical versions (interactive) | +| `mike-preview.sh` | Local multi-version preview (no push) | + +Each entry-point script supports `--help` for usage, options, environment variables, and examples: + +```bash +./docs-site/scripts/prepare-docs.sh --help +./docs-site/scripts/mike-deploy.sh --help +./docs-site/scripts/mike-backfill.sh --help +./docs-site/scripts/mike-preview.sh --help +``` + +`lib/` holds sourced helpers. `test/` holds unit tests — run `test/test-all.sh` or `make docs-test`. + +## What `prepare-docs.sh` does + +1. Transform each component README through `lib/stage-markdown.sh`. +2. Stage the home page and inject `extra/intro-upper.md`, `extra/intro-lower.md`, and a data sources section when that release includes data sources. +3. Copy `extra/` community pages and section index files. +4. Copy site assets (`stylesheets/extra.css`, `javascripts/*.js`) into the staging tree. +5. Generate navigation and write `zensical.build.toml`. + +Versioned deploys use `INCLUDE_EXTRA=true` so every release shares the same site shell. + +Component reference content still comes from that tag’s `.web-docs`. + +## Common Commands + +Use the `./Makefile` targets. + +```bash +make docs-prepare # stage only +make docs-test # all script tests +make docs-build # generate + stage + zensical build +make docs-serve # generate + stage + local serve + +make docs-serve-version VERSION=2.1.0 # preview a single release +make docs-serve-mike # local mike preview (tags + development) +make docs-backfill VERSIONS="2.0.0 2.1.0 2.2.0" # push versions to gh-pages +``` + +Stable release tags trigger `deploy-docs` in `.github/workflows/release.yml` after GoReleaser succeeds. + +## Environment variables + +| Variable | Default | Notes | +|-----------------------|--------------------|---------------------------------------------------| +| `INCLUDE_EXTRA` | `true` | Community pages, home injections, section indexes | +| `WEB_DOCS_DIR` | `/.web-docs` | Source component READMEs | +| `MIKE_PREVIEW_BRANCH` | `docs-preview` | Local preview git branch | +| `MIKE_PREVIEW_PORT` | `8001` | Local preview URL port | +| `MIKE_PREVIEW_SKIP_DEVELOPMENT` | `false` | Omit working-tree version from preview deploys | +| `MIKE_BRANCH` | `gh-pages` | Remote pages branch | +| `MIKE_COMMIT_MESSAGE` | unset | Optional mike git commit message (`{version}`, `{branch}`) | +| `MIKE_COMMIT_VERSION` | set by deploy scripts | Value substituted for `{version}` in commit messages | + +## Tests + +```bash +./docs-site/scripts/test/test-all.sh +``` + +Most `lib` scripts have a matching `test/test-.sh`. diff --git a/docs-site/scripts/lib/convert-admonitions.sh b/docs-site/scripts/lib/convert-admonitions.sh new file mode 100755 index 00000000..02bb6888 --- /dev/null +++ b/docs-site/scripts/lib/convert-admonitions.sh @@ -0,0 +1,228 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Convert Material-style admonition markers to Zensical admonitions or blockquotes. + +set -euo pipefail + +convert_admonitions() { + local content="$1" + printf '%s' "$content" | perl -0777 -pe ' + use strict; + use warnings; + + sub admonition_type { + my ($label) = @_; + $label = lc $label; + $label =~ s/:$//; + $label =~ s/\s+$//; + return "warning" if $label =~ /^(important|warning)$/; + return "tip" if $label eq "tip"; + return "danger" if $label eq "danger"; + return "info" if $label eq "info"; + return "note"; + } + + sub display_label { + my ($label) = @_; + $label =~ s/:$//; + $label =~ s/\s+$//; + return "Important" if lc($label) eq "important"; + return "Warning" if lc($label) eq "warning"; + return "Tip" if lc($label) eq "tip"; + return "Note" if lc($label) =~ /^notes?$/; + return "Note" if $label eq "NOTE"; + return $label; + } + + sub normalize_admonition_line { + my ($line) = @_; + if ($line =~ /^(\s*)`((?:~>|-+>)\s*\*\*.+)$/ && $line !~ /`\s*$/) { + return $1 . $2; + } + return $line; + } + + sub is_admonition_line { + my ($line) = @_; + $line = normalize_admonition_line($line); + return 0 if $line =~ /^\s*`-+>\s*\*\*/; + return 0 if $line =~ /^\s*~>\s*\d/; + return 1 if $line =~ /^(\s*)(?:~>|-+>)\s*\*\*([^*]+?)\*\*:?\s*(.*)$/s; + return 1 if $line =~ /^(\s*)\*\*(NOTE|Note|Notes|Important|Tip|Warning|Danger|Info)\*\*:?\s*(.*)$/s; + return 0; + } + + sub parse_admonition_line { + my ($line) = @_; + $line = normalize_admonition_line($line); + if ($line =~ /^(\s*)(?:~>|-+>)\s*\*\*([^*]+?)\*\*:?\s*(.*)$/s) { + return ($1, $2, $3); + } + if ($line =~ /^(\s*)\*\*(NOTE|Note|Notes|Important|Tip|Warning|Danger|Info)\*\*:?\s*(.*)$/s) { + return ($1, $2, $3); + } + return; + } + + sub recent_list_item_context { + my ($out) = @_; + for (my $i = $#$out; $i >= 0; $i--) { + my $line = $out->[$i]; + next if $line =~ /^\s*$/; + next if $line =~ /^[ \t]*[ \t]*\n//mg; + ' +} + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + if [[ $# -ne 1 ]]; then + echo "usage: $0 " >&2 + exit 1 + fi + strip_codegen_comments "$(cat "$1")" +fi diff --git a/docs-site/scripts/lib/sync-web-docs.sh b/docs-site/scripts/lib/sync-web-docs.sh new file mode 100644 index 00000000..7f4b6f1b --- /dev/null +++ b/docs-site/scripts/lib/sync-web-docs.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Replace .web-docs with the tree from a release tag. + +set -euo pipefail + +LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=paths.sh +source "${LIB_DIR}/paths.sh" + +sync_web_docs_from_tag() { + local tag="$1" + + if ! git -C "$REPO_ROOT" rev-parse "$tag" >/dev/null 2>&1; then + echo "error: tag ${tag} not found" >&2 + return 1 + fi + + rm -rf "${WEB_DOCS_DIR}" + git -C "$REPO_ROOT" checkout "$tag" -- .web-docs +} diff --git a/docs-site/scripts/mike-backfill.sh b/docs-site/scripts/mike-backfill.sh new file mode 100755 index 00000000..de33371b --- /dev/null +++ b/docs-site/scripts/mike-backfill.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Backfill multiple documentation versions to GitHub Pages with mike. + +# Usage: +# mike-backfill.sh [VERSION ...] +# +# Publishes documentation for one or more release tags to gh-pages. Each +# version is deployed via mike-deploy.sh. The highest version receives the +# latest alias. +# +# When no VERSION arguments are given, prompts for a space-separated list +# and asks for confirmation before pushing. +# +# Environment: +# MIKE_BACKFILL_VERSIONS Space-separated versions (skips the version prompt) +# MIKE_BRANCH Remote git branch for mike (default: gh-pages) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="${SCRIPT_DIR}/lib" + +# shellcheck source=lib/paths.sh +source "${LIB_DIR}/paths.sh" + +usage() { + cat <&2 + usage + ;; + *) + VERSIONS+=("${1#v}") + shift + ;; + esac +done + +if ((${#VERSIONS[@]} == 0)) && [[ -n "${MIKE_BACKFILL_VERSIONS:-}" ]]; then + # shellcheck disable=SC2206 + VERSIONS=($MIKE_BACKFILL_VERSIONS) +fi + +if ((${#VERSIONS[@]} == 0)); then + echo "Recent release tags:" + git -C "$REPO_ROOT" tag -l 'v*' --sort=v:refname | tail -5 | sed 's/^/ /' || true + echo "" + printf 'Versions to publish (space-separated, highest last for latest alias): ' + read -r line + # shellcheck disable=SC2206 + VERSIONS=($line) +fi + +if ((${#VERSIONS[@]} == 0)); then + echo "error: no versions provided" >&2 + exit 1 +fi + +declare -a sorted_versions=() +while IFS= read -r version; do + [[ -n "$version" ]] && sorted_versions+=("${version#v}") +done < <(printf '%s\n' "${VERSIONS[@]}" | sort -V) + +latest="${sorted_versions[${#sorted_versions[@]} - 1]}" + +echo "" +echo "Will deploy to gh-pages:" +for version in "${sorted_versions[@]}"; do + if [[ "$version" == "$latest" ]]; then + echo " - ${version} (with latest alias)" + else + echo " - ${version}" + fi +done +echo "" +printf 'Continue? [y/N] ' +read -r confirm +if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +restore_web_docs() { + if git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$REPO_ROOT" checkout HEAD -- .web-docs 2>/dev/null || true + fi +} + +trap restore_web_docs EXIT + +for version in "${sorted_versions[@]}"; do + tag="v${version}" + if ! git -C "$REPO_ROOT" rev-parse "$tag" >/dev/null 2>&1; then + echo "error: tag ${tag} not found" >&2 + exit 1 + fi + + echo "" + echo "Deploying ${version} from ${tag}..." + if [[ "$version" == "$latest" ]]; then + "${SCRIPT_DIR}/mike-deploy.sh" "$version" --update-latest + else + "${SCRIPT_DIR}/mike-deploy.sh" "$version" + fi +done + +trap - EXIT +restore_web_docs + +echo "" +echo "Deployed documentation for the following versions: ${sorted_versions[*]}" diff --git a/docs-site/scripts/mike-deploy.sh b/docs-site/scripts/mike-deploy.sh new file mode 100755 index 00000000..0cbef354 --- /dev/null +++ b/docs-site/scripts/mike-deploy.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Deploy a documentation version to GitHub Pages with mike. + +# Usage: +# mike-deploy.sh [--update-latest] +# +# Stages documentation from the matching git tag (v), builds with +# Zensical, and pushes the result to the gh-pages branch. +# +# Environment: +# MIKE_BRANCH Remote git branch for mike (default: gh-pages) +# MIKE_COMMIT_MESSAGE Optional git commit message for mike deploy/set-default +# MIKE_COMMIT_VERSION Expands {version} in MIKE_COMMIT_MESSAGE +# WEB_DOCS_DIR Source component READMEs (default: /.web-docs) +# INCLUDE_EXTRA Always true for this script (community shell, home injections) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="${SCRIPT_DIR}/lib" + +# shellcheck source=lib/paths.sh +source "${LIB_DIR}/paths.sh" +# shellcheck source=lib/mike-env.sh +source "${LIB_DIR}/mike-env.sh" +# shellcheck source=lib/sync-web-docs.sh +source "${LIB_DIR}/sync-web-docs.sh" + +usage() { + cat < [--update-latest] + + Release version without the v prefix (e.g. 2.2.0) + --update-latest Also set the latest alias and default version + +Stages .web-docs from tag v, applies the current docs-site shell +from extra/, builds, and pushes to gh-pages with mike. + +Environment: + MIKE_BRANCH Remote branch for mike (default: gh-pages) + MIKE_COMMIT_MESSAGE Optional git commit message (supports {version}, {branch}) + MIKE_COMMIT_VERSION Set automatically to the deployed version + +Examples: + $0 2.2.0 + $0 2.2.0 --update-latest +EOF + exit 1 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -h | --help) usage ;; + --update-latest) + UPDATE_LATEST=true + shift + ;; + -*) + echo "error: unknown option: $1" >&2 + usage + ;; + *) + if [[ -n "${VERSION:-}" ]]; then + echo "error: unexpected argument: $1" >&2 + usage + fi + VERSION="${1#v}" + shift + ;; + esac +done + +UPDATE_LATEST="${UPDATE_LATEST:-false}" +[[ -n "${VERSION:-}" ]] || usage + +TAG="v${VERSION}" +WEB_DOCS_SYNCED=false + +restore_web_docs() { + if [[ "$WEB_DOCS_SYNCED" == "true" ]] && git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$REPO_ROOT" checkout HEAD -- .web-docs 2>/dev/null || true + fi +} + +if git -C "$REPO_ROOT" rev-parse "$TAG" >/dev/null 2>&1; then + echo "Staging documentation for ${VERSION} from ${TAG}..." + sync_web_docs_from_tag "$TAG" + WEB_DOCS_SYNCED=true +else + echo "warning: ${TAG} not found; using current .web-docs" >&2 +fi + +trap restore_web_docs EXIT + +rm -rf "${DOCS_SITE_DIR}/site" +INCLUDE_EXTRA=true "${SCRIPT_DIR}/prepare-docs.sh" + +trap - EXIT +restore_web_docs + +resolve_mike +export MIKE_BRANCH="${MIKE_BRANCH:-gh-pages}" +MIKE_COMMIT_VERSION="$VERSION" + +if [[ "$UPDATE_LATEST" == "true" ]]; then + mike_cmd deploy --push --update-aliases "$VERSION" latest + mike_cmd set-default --push latest +else + mike_cmd deploy --push "$VERSION" +fi diff --git a/docs-site/scripts/mike-preview.sh b/docs-site/scripts/mike-preview.sh new file mode 100755 index 00000000..399a9866 --- /dev/null +++ b/docs-site/scripts/mike-preview.sh @@ -0,0 +1,264 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Build and serve a local multi-version preview with mike (no push to remote). + +# Usage: +# mike-preview.sh [--fresh] [--deploy-only] [--serve-only] [VERSION ...] +# +# Deploys tagged releases (newest gets the "latest" alias) plus the current +# working tree as "development", then runs mike serve. +# +# Environment: +# MIKE_PREVIEW_BRANCH Local git branch (default: docs-preview) +# MIKE_PREVIEW_PORT Serve port (default: 8001) +# MIKE_PREVIEW_VERSIONS Space-separated release tags (default: last 3 git tags) +# MIKE_PREVIEW_CURRENT Version id for working tree (default: development) +# MIKE_PREVIEW_CURRENT_TITLE Title shown for the working-tree version +# MIKE_PREVIEW_SKIP_DEVELOPMENT Skip deploying the working tree (default: false) +# MIKE_COMMIT_MESSAGE Optional git commit message for mike commits +# MIKE_COMMIT_VERSION Expands {version} in MIKE_COMMIT_MESSAGE + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="${SCRIPT_DIR}/lib" + +# shellcheck source=lib/paths.sh +source "${LIB_DIR}/paths.sh" +# shellcheck source=lib/mike-env.sh +source "${LIB_DIR}/mike-env.sh" +# shellcheck source=lib/sync-web-docs.sh +source "${LIB_DIR}/sync-web-docs.sh" + +BRANCH="${MIKE_PREVIEW_BRANCH:-docs-preview}" +PORT="${MIKE_PREVIEW_PORT:-8001}" +CURRENT="${MIKE_PREVIEW_CURRENT:-development}" +CURRENT_TITLE="${MIKE_PREVIEW_CURRENT_TITLE:-}" +SKIP_DEVELOPMENT="${MIKE_PREVIEW_SKIP_DEVELOPMENT:-false}" + +FRESH=false +DEPLOY_ONLY=false +SERVE_ONLY=false +declare -a VERSIONS=() + +usage() { + cat <&2 + usage + ;; + *) + VERSIONS+=("${1#v}") + shift + ;; + esac +done + +if ((${#VERSIONS[@]} == 0)); then + if [[ -n "${MIKE_PREVIEW_VERSIONS:-}" ]]; then + # shellcheck disable=SC2206 + VERSIONS=($MIKE_PREVIEW_VERSIONS) + else + while IFS= read -r version; do + [[ -n "$version" ]] && VERSIONS+=("$version") + done < <(default_versions) + fi +fi + +resolve_mike +resolve_zensical +export PATH="${DOCS_SITE_DIR}/.venv/bin:${PATH}" +export MIKE_BRANCH="$BRANCH" + +restore_web_docs() { + if git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$REPO_ROOT" checkout HEAD -- .web-docs 2>/dev/null || true + fi +} + +latest_version() { + local version + for version in "${VERSIONS[@]}"; do + version="${version#v}" + if git -C "$REPO_ROOT" rev-parse "v${version}" >/dev/null 2>&1; then + printf '%s\n' "$version" + fi + done | sort -V | tail -1 +} + +development_title() { + if [[ -n "$CURRENT_TITLE" ]]; then + printf '%s' "$CURRENT_TITLE" + return + fi + local branch + branch="$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null || true)" + if [[ -n "$branch" ]]; then + printf '%s' "$branch" + else + printf 'branch' + fi +} + +ensure_preview_branch_ready() { + if ! git -C "$REPO_ROOT" rev-parse --verify "$BRANCH" >/dev/null 2>&1; then + echo "error: preview branch '${BRANCH}' does not exist." >&2 + echo "Deploy first, e.g.:" >&2 + echo " make docs-serve-mike VERSIONS=\"2.0.0 2.1.0 2.1.1 2.1.2 2.2.0\"" >&2 + if [[ "$BRANCH" != "docs-preview" ]]; then + echo "Or use the default local preview branch:" >&2 + echo " unset MIKE_PREVIEW_BRANCH" >&2 + echo " make docs-serve-mike-only" >&2 + fi + exit 1 + fi + + if ! git -C "$REPO_ROOT" cat-file -e "${BRANCH}:versions.json" 2>/dev/null; then + echo "error: branch '${BRANCH}' has no mike versions (missing versions.json)." >&2 + echo "Deploy to this branch before serving, e.g.:" >&2 + echo " MIKE_PREVIEW_BRANCH=${BRANCH} make docs-serve-mike VERSIONS=\"2.0.0 2.1.0 2.1.1 2.1.2 2.2.0\"" >&2 + exit 1 + fi + + echo "Serving from local branch '${BRANCH}'." + mike_cmd list + echo "" +} + +deploy_tagged_version() { + local version="$1" + local with_latest="$2" + local tag="v${version}" + + if ! git -C "$REPO_ROOT" rev-parse "$tag" >/dev/null 2>&1; then + echo "warning: skipping ${version} (tag ${tag} not found)" >&2 + return 0 + fi + + echo "Deploying ${version} from ${tag}..." + sync_web_docs_from_tag "$tag" + rm -rf "${DOCS_SITE_DIR}/site" + INCLUDE_EXTRA=true "${SCRIPT_DIR}/prepare-docs.sh" + MIKE_COMMIT_VERSION="$version" + if [[ "$with_latest" == "true" ]]; then + mike_cmd deploy -t "v${version}" --update-aliases "$version" latest + else + mike_cmd deploy -t "v${version}" "$version" + fi +} + +deploy_tagged_versions() { + local latest version + local -a sorted_versions=() + latest="$(latest_version)" + if [[ -z "$latest" ]]; then + echo "warning: no tagged versions to deploy" >&2 + return 0 + fi + + while IFS= read -r version; do + [[ -n "$version" ]] && sorted_versions+=("$version") + done < <(printf '%s\n' "${VERSIONS[@]}" | sed 's/^v//' | sort -V) + + for version in "${sorted_versions[@]}"; do + if [[ "$version" == "$latest" ]]; then + deploy_tagged_version "$version" true + else + deploy_tagged_version "$version" false + fi + done + + mike_cmd set-default latest +} + +deploy_development() { + echo "Deploying current working tree as ${CURRENT}..." + restore_web_docs + rm -rf "${DOCS_SITE_DIR}/site" + INCLUDE_EXTRA=true "${SCRIPT_DIR}/prepare-docs.sh" + MIKE_COMMIT_VERSION="$CURRENT" + mike_cmd delete next 2>/dev/null || true + mike_cmd deploy -t "$(development_title)" "$CURRENT" +} + +if [[ "$SERVE_ONLY" != "true" ]]; then + if [[ "$FRESH" == "true" ]]; then + echo "Resetting local preview branch ${BRANCH}..." + git -C "$REPO_ROOT" branch -D "$BRANCH" 2>/dev/null || true + fi + + trap restore_web_docs EXIT + + deploy_tagged_versions + if [[ "$SKIP_DEVELOPMENT" == "true" ]]; then + mike_cmd delete "$CURRENT" 2>/dev/null || true + else + deploy_development + fi + + trap - EXIT + + echo "" + mike_cmd list + echo "" +fi + +if [[ "$DEPLOY_ONLY" == "true" ]]; then + echo "Preview branch ${BRANCH} is ready. Run: make docs-serve-mike-only" + exit 0 +fi + +if [[ "$SERVE_ONLY" == "true" ]]; then + ensure_preview_branch_ready +fi + +echo "Serving versioned docs at http://localhost:${PORT}/" +echo "Press Ctrl+C to stop." +mike_cmd serve -a "localhost:${PORT}" diff --git a/docs-site/scripts/prepare-docs.sh b/docs-site/scripts/prepare-docs.sh new file mode 100755 index 00000000..cdd33e20 --- /dev/null +++ b/docs-site/scripts/prepare-docs.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Stage .web-docs and extra content for Zensical, rewrite links, and emit nav config. + +# Usage: +# prepare-docs.sh [--help] +# +# Writes staged markdown to docs-site/.build/docs and generates +# docs-site/zensical.build.toml for Zensical. +# +# Environment: +# INCLUDE_EXTRA Include community pages, home injections, section indexes (default: true) +# WEB_DOCS_DIR Source component READMEs (default: /.web-docs) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="${SCRIPT_DIR}/lib" + +# shellcheck source=lib/paths.sh +source "${LIB_DIR}/paths.sh" +# shellcheck source=lib/stage-markdown.sh +source "${LIB_DIR}/stage-markdown.sh" +# shellcheck source=lib/generate-nav.sh +source "${LIB_DIR}/generate-nav.sh" +# shellcheck source=lib/inject-home-sections.sh +source "${LIB_DIR}/inject-home-sections.sh" +# shellcheck source=lib/inject-home-data-sources.sh +source "${LIB_DIR}/inject-home-data-sources.sh" + +usage() { + cat </.web-docs) + +Examples: + make docs-prepare + INCLUDE_EXTRA=true $0 + WEB_DOCS_DIR=/path/to/.web-docs $0 +EOF + exit 1 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -h | --help) usage ;; + *) + echo "error: unknown argument: $1" >&2 + usage + ;; + esac +done + +main() { + if [[ ! -d "$WEB_DOCS_DIR" ]]; then + echo "error: web docs directory not found: ${WEB_DOCS_DIR}" >&2 + exit 1 + fi + + has_component_docs() { + local component_dir="$1" + local slug_dir + [[ -d "${WEB_DOCS_DIR}/components/${component_dir}" ]] || return 1 + for slug_dir in "${WEB_DOCS_DIR}/components/${component_dir}"/*; do + [[ -d "$slug_dir" ]] || continue + if [[ -f "${slug_dir}/README.md" ]]; then + return 0 + fi + done + return 1 + } + + rm -rf "${DOCS_SITE_DIR}/.build" + mkdir -p "${STAGING_DIR}/assets" + + if [[ -f "${DOCS_SITE_DIR}/assets/header-logo.png" ]]; then + cp "${DOCS_SITE_DIR}/assets/header-logo.png" "${STAGING_DIR}/assets/header-logo.png" + fi + + if [[ -f "${DOCS_SITE_DIR}/stylesheets/extra.css" ]]; then + mkdir -p "${STAGING_DIR}/stylesheets" + cp "${DOCS_SITE_DIR}/stylesheets/extra.css" "${STAGING_DIR}/stylesheets/extra.css" + fi + + if [[ -d "${DOCS_SITE_DIR}/javascripts" ]]; then + mkdir -p "${STAGING_DIR}/javascripts" + cp "${DOCS_SITE_DIR}/javascripts/"*.js "${STAGING_DIR}/javascripts/" 2>/dev/null || true + fi + + if [[ -f "${REPO_ROOT}/LICENSE" ]]; then + cp "${REPO_ROOT}/LICENSE" "${STAGING_DIR}/LICENSE" + fi + + if [[ -f "${WEB_DOCS_DIR}/README.md" ]]; then + stage_file "${WEB_DOCS_DIR}/README.md" "${STAGING_DIR}/index.md" "${HOME_TITLE}" + if [[ "$INCLUDE_EXTRA" == "true" ]]; then + inject_intro_upper "${STAGING_DIR}/index.md" + inject_home_data_sources "${STAGING_DIR}/index.md" + inject_intro_lower "${STAGING_DIR}/index.md" + fi + fi + + local dir type slug_dir slug + for dir in builder post-processor data-source; do + case "$dir" in + builder) type="builders" ;; + post-processor) type="post-processors" ;; + data-source) type="data-sources" ;; + esac + if [[ -d "${WEB_DOCS_DIR}/components/${dir}" ]]; then + for slug_dir in "${WEB_DOCS_DIR}/components/${dir}"/*; do + [[ -d "$slug_dir" ]] || continue + slug="$(basename "$slug_dir")" + if [[ -f "${slug_dir}/README.md" ]]; then + stage_file "${slug_dir}/README.md" "${STAGING_DIR}/${type}/${slug}.md" "$(component_display_name "$slug")" + fi + done + fi + done + + if [[ "$INCLUDE_EXTRA" == "true" ]]; then + for section in builders post-processors; do + local index_page="${DOCS_SITE_DIR}/extra/${section}/index.md" + if [[ -f "$index_page" ]]; then + mkdir -p "${STAGING_DIR}/${section}" + cp "$index_page" "${STAGING_DIR}/${section}/index.md" + fi + done + if has_component_docs data-source; then + local data_sources_index="${DOCS_SITE_DIR}/extra/data-sources/index.md" + if [[ -f "$data_sources_index" ]]; then + mkdir -p "${STAGING_DIR}/data-sources" + cp "$data_sources_index" "${STAGING_DIR}/data-sources/index.md" + fi + fi + fi + + if [[ "$INCLUDE_EXTRA" == "true" ]] && [[ -d "${DOCS_SITE_DIR}/extra/community" ]]; then + mkdir -p "${STAGING_DIR}/community" + for page in "${DOCS_SITE_DIR}/extra/community"/*.md; do + [[ -f "$page" ]] || continue + cp "$page" "${STAGING_DIR}/community/$(basename "$page")" + done + fi + + mkdir -p "$(dirname "$BUILD_CONFIG")" + local nav_file="${DOCS_SITE_DIR}/.build/nav.toml" + generate_nav >"$nav_file" + awk -v nav_file="$nav_file" ' + /^\[project\.theme\]/ { + while ((getline line < nav_file) > 0) { + print line + } + close(nav_file) + print "" + } + { print } + ' "${DOCS_SITE_DIR}/zensical.toml" >"$BUILD_CONFIG" + rm -f "$nav_file" + + echo "Documentation staged in ${STAGING_DIR}." + echo "Navigation configuration generated in ${BUILD_CONFIG}." +} + +main "$@" diff --git a/docs-site/scripts/test/assertions.sh b/docs-site/scripts/test/assertions.sh new file mode 100755 index 00000000..cfa2a84f --- /dev/null +++ b/docs-site/scripts/test/assertions.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Shared assertion helpers for script tests for staged markdown. + +set -euo pipefail + +assert_contains() { + local haystack="$1" + local needle="$2" + local message="$3" + if [[ "$haystack" != *"$needle"* ]]; then + echo "FAIL: ${message}" >&2 + echo " expected to contain: ${needle}" >&2 + echo " got: ${haystack}" >&2 + exit 1 + fi +} + +assert_not_contains() { + local haystack="$1" + local needle="$2" + local message="$3" + if [[ "$haystack" == *"$needle"* ]]; then + echo "FAIL: ${message}" >&2 + echo " did not expect: ${needle}" >&2 + exit 1 + fi +} diff --git a/docs-site/scripts/test/test-all.sh b/docs-site/scripts/test/test-all.sh new file mode 100755 index 00000000..523e2ab0 --- /dev/null +++ b/docs-site/scripts/test/test-all.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Run all script unit tests for staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +run_test() { + local script="$1" + echo "==> ${script##*/}" + bash "$script" +} + +run_test "${TEST_DIR}/test-rewrite-integration-links.sh" +run_test "${TEST_DIR}/test-fix-internal-links.sh" +run_test "${TEST_DIR}/test-convert-admonitions.sh" +run_test "${TEST_DIR}/test-convert-github-alerts.sh" +run_test "${TEST_DIR}/test-format-example-labels.sh" +run_test "${TEST_DIR}/test-group-example-tabs.sh" +run_test "${TEST_DIR}/test-strip-codegen-comments.sh" +run_test "${TEST_DIR}/test-deepen-asterisk-sublists.sh" +run_test "${TEST_DIR}/test-repair-code-fences.sh" +run_test "${TEST_DIR}/test-normalize-list-spacing.sh" +run_test "${TEST_DIR}/test-inject-home-data-sources.sh" +run_test "${TEST_DIR}/test-generate-nav.sh" +run_test "${TEST_DIR}/test-stage-markdown.sh" + +echo "All docs-site script tests passed." diff --git a/docs-site/scripts/test/test-convert-admonitions.sh b/docs-site/scripts/test/test-convert-admonitions.sh new file mode 100755 index 00000000..6331f346 --- /dev/null +++ b/docs-site/scripts/test/test-convert-admonitions.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for Zensical admonition conversion in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/convert-admonitions.sh +source "${LIB_DIR}/convert-admonitions.sh" +# shellcheck source=../lib/strip-codegen-comments.sh +source "${LIB_DIR}/strip-codegen-comments.sh" + +test_block_note() { + local input=$'-> **Note:** First line.\nSecond line.\n\nNext paragraph.' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" "!!! note" "block note header" + assert_contains "$output" " First line." "block note body" + assert_not_contains "$output" "-> **Note:**" "legacy syntax removed" +} + +test_list_nested_note() { + local input=$'- insecure_connection (bool)\n Defaults to false.\n \n -> **Note:** Helpful detail.\n More detail.\n\n- next' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! note' "list nested admonition indented" + assert_contains "$output" $' Helpful detail.' "indented admonition body" + assert_contains "$output" "More detail." "admonition continuation" + assert_not_contains "$output" "
    **Note:** Only the primary disk size.\n Additional disks are not supported.\n\n- next' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! note' "disk_size indented admonition" + assert_contains "$output" "Only the primary disk size." "disk_size note body" +} + +test_tilde_note() { + local input=$' ~> **Note:** The full path must be provided.\n For example, rp-packer.\n\n- next' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! note' "tilde note indented admonition" + assert_contains "$output" "The full path must be provided." "tilde note body" +} + +test_tilde_important_in_list() { + local input=$' ~> **Important:** When using tag blocks the category must exist.' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! warning' "important in list" + assert_contains "$output" "When using tag blocks" "important body" +} + +test_tilde_important_top_level() { + local input=$'~> **Important:** When using tag blocks the category must exist.' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" "!!! warning" "important maps to warning at top level" +} + +test_tilde_notes_with_list() { + local input=$'~> **Notes:**\n - Option A\n - Option B\n\n### Next' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" "!!! note" "top-level notes block" + assert_contains "$output" " - Option A" "sub-list in admonition body" +} + +test_note_after_code_in_list() { + local input=$'- item\n \n ```hcl\n foo = 1\n ```\n \n ~> **Note:** Configuration keys conflict.\n are ignored.\n\n- next' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! note' "note after code block in list" + assert_contains "$output" "Configuration keys conflict." "note body preserved" +} + +test_bold_note_without_arrow() { + local input=$' **NOTE**: Guests using Windows with scp issues.\n Use SFTP instead.\n\n- next' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! note' "bold NOTE indented admonition" + assert_contains "$output" "Guests using Windows" "bold NOTE body" +} + +test_tip_syntax() { + local input=$' --> **Tip:** Use `none` to disable the manifest.' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! tip' "tip indented admonition" +} + +test_skip_inline_code_example() { + local input=$' `-> **Note:** Refer to the docs`' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" '`-> **Note:**' "inline code example preserved" +} + +test_skip_hcl_version_operator() { + local input=$' version = "~> 1"' + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" '"~> 1"' "hcl version operator preserved" + assert_not_contains "$output" "!!!" "no admonition in hcl" +} + +test_firmware_note_after_continuation() { + local input + input="$(cat <<'EOF' +- `firmware` (string) - The firmware for the virtual machine. + + The available options for this setting are: 'bios', 'efi', and + 'efi-secure'. + + -> **Note:** Use `efi-secure` for UEFI Secure Boot. + +- next +EOF +)" + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' !!! note' "firmware note converted" + assert_contains "$output" "Use \`efi-secure\` for UEFI Secure Boot." "firmware note body" + assert_not_contains "$output" $'machine.\n \n The available' "blank line removed after bullet" + assert_not_contains "$output" "-> **Note:**" "legacy note syntax removed" +} + +test_vapp_note_before_continuation() { + local input + input="$(cat <<'EOF' +- `properties` (map[string]string) - Supply configuration parameters. + from an imported OVF or OVA file. + + -> **Note:** The only supported usage path for vApp properties. + These generally come from an existing template. + + You cannot set values for vApp properties on scratch-built VMs. + +- next +EOF +)" + local output + output="$(convert_admonitions "$input")" + assert_contains "$output" $' These generally come from an existing template.\n You cannot set values' "continuation stays in list after note" + assert_not_contains "$output" $'template.\n\n You cannot' "no blank after list admonition" +} + +test_http_ip_notes_after_list_item() { + local input + input="$(cat <<'EOF' +- `http_ip` (string) - The IP address to use for the HTTP server to serve the `http_directory`. + + + +~> **Notes:** + - The options `http_bind_address` and `http_interface` are mutually exclusive. + - Both `http_bind_address` and `http_interface` have higher priority than `http_ip`. + +### Floppy Configuration +EOF +)" + local output + output="$(convert_admonitions "$(strip_codegen_comments "$input")")" + assert_contains "$output" $'- `http_ip` (string) - The IP address' "http_ip list item preserved" + assert_contains "$output" $' !!! note' "notes nested under list item" + assert_contains "$output" $' - The options `http_bind_address`' "note body indented" + assert_not_contains "$output" $'http_directory`.\n!!! note' "no top-level admonition header" + assert_contains "$output" $'directory`.\n\n !!! note' "blank before nested note after bullet" +} + +main() { + test_block_note + test_list_nested_note + test_disk_size_inline_note + test_tilde_note + test_tilde_important_in_list + test_tilde_important_top_level + test_tilde_notes_with_list + test_note_after_code_in_list + test_bold_note_without_arrow + test_tip_syntax + test_skip_inline_code_example + test_skip_hcl_version_operator + test_firmware_note_after_continuation + test_vapp_note_before_continuation + test_http_ip_notes_after_list_item + echo "All admonition conversion tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-convert-github-alerts.sh b/docs-site/scripts/test/test-convert-github-alerts.sh new file mode 100755 index 00000000..ebbbfe81 --- /dev/null +++ b/docs-site/scripts/test/test-convert-github-alerts.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for GitHub alert to Zensical admonition conversion in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/convert-github-alerts.sh +source "${LIB_DIR}/convert-github-alerts.sh" + +test_loose_tip_body() { + local input=$'> [!TIP] +If you need help or have questions about using the plugin, please refer to the +[documentation](https://example.com) or open a discussion. + +## Issues' + local output + output="$(convert_github_alerts "$input")" + assert_contains "$output" "!!! tip" "tip admonition header" + assert_contains "$output" "If you need help" "tip body preserved" + assert_not_contains "$output" "[!TIP]" "github alert marker removed" +} + +test_quoted_warning_body() { + local input=$'> [!WARNING] +> Issues that do not follow the guidelines may be closed. + +## Pull Requests' + local output + output="$(convert_github_alerts "$input")" + assert_contains "$output" "!!! warning" "warning admonition header" + assert_contains "$output" "Issues that do not follow" "warning body preserved" +} + +test_important_and_tip_sequence() { + local input=$'> [!IMPORTANT] +> - Ensure that you are using a recent version of the plugin. + +> [!TIP] +> - Learn about formatting code on GitHub. + +## Next' + local output + output="$(convert_github_alerts "$input")" + assert_contains "$output" "!!! warning" "important maps to warning" + assert_contains "$output" "!!! tip" "second alert converted" + assert_contains "$output" "Learn about formatting" "tip list preserved" +} + +main() { + test_loose_tip_body + test_quoted_warning_body + test_important_and_tip_sequence + echo "All GitHub alert conversion tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-deepen-asterisk-sublists.sh b/docs-site/scripts/test/test-deepen-asterisk-sublists.sh new file mode 100755 index 00000000..f856dfe7 --- /dev/null +++ b/docs-site/scripts/test/test-deepen-asterisk-sublists.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for indenting asterisk sublists in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/deepen-asterisk-sublists.sh +source "${LIB_DIR}/deepen-asterisk-sublists.sh" + +test_deepen_asterisk_sublists() { + local input + input="$(cat <<'EOF' +- `options` ([]string) - Available options include: + * `mac` - MAC address. + * `uuid` - UUID. +EOF +)" + local output + output="$(deepen_asterisk_sublists "$input")" + assert_contains "$output" " * \`mac\`" "asterisk sublists indented" + assert_not_contains "$output" $'\n * `mac`' "no two-space asterisk sublists" +} + +main() { + test_deepen_asterisk_sublists + echo "All deepen-asterisk-sublists tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-fix-internal-links.sh b/docs-site/scripts/test/test-fix-internal-links.sh new file mode 100755 index 00000000..a277f003 --- /dev/null +++ b/docs-site/scripts/test/test-fix-internal-links.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for internal link and anchor fixes in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/fix-internal-links.sh +source "${LIB_DIR}/fix-internal-links.sh" + +test_same_page_link() { + local input='[Hardware Configuration](builders/iso.md#hardware-configuration)' + local output + output="$(fix_internal_links "$input" "builders/iso.md")" + assert_contains "$output" '[Hardware Configuration](#hardware-configuration)' "same-page .md link" + assert_not_contains "$output" 'builders/iso.md' "no absolute same-page path" +} + +test_sibling_builder_link() { + local input='[VMware VMX](builders/vmx.md)' + local output + output="$(fix_internal_links "$input" "builders/iso.md")" + assert_contains "$output" '[VMware VMX](vmx/)' "sibling builder link" +} + +test_anchor_passthrough() { + local input='See [SSH](#ssh) for authentication options.' + local output + output="$(fix_internal_links "$input" "builders/iso.md")" + assert_contains "$output" '#ssh' "anchor is preserved without remapping" + assert_not_contains "$output" '#location-configuration' "no vsphere-specific anchor remapping" +} + +test_home_page_component_link() { + local input='[VMware ISO](builders/iso.md)' + local output + output="$(fix_internal_links "$input" "index.md")" + assert_contains "$output" '[VMware ISO](builders/iso/)' "home page component link" + assert_not_contains "$output" '../builders/' "home page link does not escape site root" +} + +main() { + test_same_page_link + test_sibling_builder_link + test_anchor_passthrough + test_home_page_component_link + echo "All fix-internal-links tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-format-example-labels.sh b/docs-site/scripts/test/test-format-example-labels.sh new file mode 100755 index 00000000..7b23680f --- /dev/null +++ b/docs-site/scripts/test/test-format-example-labels.sh @@ -0,0 +1,336 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for HCL/JSON example label formatting in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/format-example-labels.sh +source "${LIB_DIR}/format-example-labels.sh" + +test_top_level_bold() { + local input=$'Intro text.\n\nHCL Example:\n\n```hcl\nfoo = 1\n```' + local output + output="$(format_example_labels "$input")" + assert_contains "$output" "**HCL Example:**" "top-level label bold" + assert_not_contains "$output" $' **HCL Example:**' "top-level label not indented" +} + +test_plural_labels() { + local input=$'HCL Examples:\n\nJSON Examples\n' + local output + output="$(format_example_labels "$input")" + assert_contains "$output" "**HCL Examples:**" "plural hcl label" + assert_contains "$output" "**JSON Examples:**" "plural json label without colon normalized" +} + +test_nested_after_bullet() { + local input=$'- `tag` (string) - Filter tags.\n \n HCL Example:\n \n ```hcl\n tag {}\n ```' + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $' **HCL Example:**' "nested label indented under bullet" + assert_contains "$output" $' ```hcl' "nested code fence indented under bullet" +} + +test_nested_after_bullet_continuation() { + local input=$' You cannot set values.\n \n JSON Example:\n \n ```json\n {}\n ```' + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $' **JSON Example:**' "nested after continuation text" +} + +test_top_level_example_after_list_item() { + local input + input="$(cat <<'EOF' +- Examples are available in the [examples](https://github.com/example) directory. + +HCL Example: + +```hcl +source "vsphere-supervisor" "example" {} +``` + +JSON Example: + +```json +{} +``` +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $'directory.\n\n**HCL Example:**' "example label not nested under list item" + assert_not_contains "$output" $' **HCL Example:**' "example label not indented under list" +} + +test_skip_variant_labels() { + local input=$'HCL Example with image import:\nJSON Example with image import:' + local output + output="$(format_example_labels "$input")" + assert_not_contains "$output" "**HCL Example with image import:**" "variant label unchanged" +} + +test_normalize_aliases() { + local input + input="$(cat <<'EOF' +In JSON: + +Usage example (JSON): + + Usage example (HCL): + +In HCL: + +In HCL2: +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" "**JSON Example:**" "In JSON alias" + assert_contains "$output" "**HCL Example:**" "In HCL alias" + assert_not_contains "$output" "In HCL2:" "In HCL2 alias replaced" + assert_not_contains "$output" "In JSON:" "legacy In JSON removed" + assert_not_contains "$output" "Usage example (JSON):" "legacy usage json removed" + assert_not_contains "$output" "Usage example (HCL):" "legacy usage hcl removed" +} + +test_aliases_in_same_list_item() { + local input + input="$(cat <<'EOF' +- `cd_files` ([]string) - Place files on a CD. + + Usage example (JSON): + + ```json + "cd_label": "cidata" + ``` + + Usage example (HCL): + + ```hcl + cd_label = "cidata" + ``` + +- next +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" " **JSON Example:**" "json alias in list" + assert_contains "$output" " **HCL Example:**" "hcl alias in list" + assert_not_contains "$output" "Usage example" "all usage aliases replaced" +} + +test_skip_code_fence() { + local input=$'```text\nHCL Example:\n```' + local output + output="$(format_example_labels "$input")" + assert_contains "$output" 'HCL Example:' "label inside fence unchanged" + assert_not_contains "$output" "**HCL Example:**" "label inside fence not bolded" +} + +test_label_after_continuation_text() { + local input + input="$(cat <<'EOF' +- `cd_files` ([]string) - Place files on a CD. + File globbing is allowed. + + Usage example (JSON): + + ```json + "cd_label": "cidata" + ``` +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $'globbing is allowed.\n\n **JSON Example:**' "blank line before nested label" + assert_not_contains "$output" $'allowed.\n **JSON Example:**' "label not inline with text" +} + +test_vapp_properties_examples() { + local input + input="$(cat <<'EOF' +- `properties` (map[string]string) - Supply configuration parameters. + + Omitting the `vapp` block entirely disables vApp support. Including at least one property enables vApp + options for scratch-built virtual machines and creates any listed keys that do not already exist. + + HCL Example: + + ```hcl + vapp { properties = {} } + ``` + + JSON Example: + + ```json + "vapp": {} + ``` + +- next +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" "configuration parameters." "bullet continuation preserved" + assert_contains "$output" "Omitting the \`vapp\` block" "vapp note stays in list item" + assert_contains "$output" $'exist.\n\n **HCL Example:**' "hcl example on its own line" + assert_contains "$output" " **JSON Example:**" "json example nested in list" +} + +test_vapp_note_before_examples() { + local input + input="$(cat <<'EOF' +- `properties` (map[string]string) - Supply configuration parameters. + from an imported OVF or OVA file. + + !!! note + The only supported usage path for vApp properties. + These generally come from an existing template. + + You cannot set values for vApp properties on scratch-built VMs. + that lack a vApp configuration, or on property keys that do not exist. + + HCL Example: + + ```hcl + vapp { properties = {} } + ``` + + JSON Example: + + ```json + "vapp": {} + ``` + +- next +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $' These generally come from an existing template.\n You cannot set values' "continuation stays in list after note" + assert_contains "$output" $'exist.\n\n **HCL Example:**' "blank before nested hcl example" + assert_contains "$output" " **JSON Example:**" "json example nested in list" +} + +test_preserve_blank_before_nested_admonition_after_continuation() { + local input + input="$(cat <<'EOF' +- `firmware` (string) - The firmware for the virtual machine. + The available options are: `bios` and `efi`. + !!! note + Use `efi-secure` for UEFI Secure Boot. +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $'`efi`.\n\n !!! note' "blank before note after continuation" +} + +test_blank_before_nested_admonition_after_bullet() { + local input + input="$(cat <<'EOF' +- `http_ip` (string) - The IP address to use for the HTTP server. + !!! note + - Option A +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $'HTTP server.\n\n !!! note' "blank before note after bullet" +} + +test_examples_after_block_admonition() { + local input + input="$(cat <<'EOF' +Both formats can be used together. + +!!! warning + + When using `tag` blocks with `category` and `name`, the tag `category` must already exist + in vSphere and be associable with virtual machines. The plugin will create tags within existing + categories if they do not exist and the account context used to run the build has the appropriate + privileges. + +HCL Example: + +```hcl +tag {} +``` + +JSON Example: + +```json +{} +``` +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $'privileges.\n\n**HCL Example:**\n\n```hcl' "examples after admonition stay top-level" + assert_not_contains "$output" $' **HCL Example:**' "examples not nested under admonition body" +} + +test_nested_fence_preserves_inner_indentation() { + local input + input="$(cat <<'EOF' +- `properties` (map[string]string) - Supply configuration parameters. + + HCL Example: + + ```hcl + vapp { + properties = { + hostname = var.hostname + } + } + ``` + + JSON Example: + + ```json + "vapp": { + "properties": { + "hostname": "example" + } + } + ``` +EOF +)" + local output + output="$(format_example_labels "$input")" + assert_contains "$output" $' vapp {\n properties = {' "hcl inner indent preserved in list" + assert_contains "$output" $' hostname = var.hostname' "hcl deep indent preserved in list" + assert_contains "$output" $' "vapp": {\n "properties": {' "json inner indent preserved in list" +} + +main() { + test_top_level_bold + test_plural_labels + test_nested_after_bullet + test_nested_after_bullet_continuation + test_top_level_example_after_list_item + test_skip_variant_labels + test_normalize_aliases + test_aliases_in_same_list_item + test_label_after_continuation_text + test_vapp_properties_examples + test_vapp_note_before_examples + test_preserve_blank_before_nested_admonition_after_continuation + test_blank_before_nested_admonition_after_bullet + test_examples_after_block_admonition + test_nested_fence_preserves_inner_indentation + test_skip_code_fence + echo "All example label formatting tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-generate-nav.sh b/docs-site/scripts/test/test-generate-nav.sh new file mode 100755 index 00000000..8e7c6e03 --- /dev/null +++ b/docs-site/scripts/test/test-generate-nav.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for Zensical navigation generation in staged markdown. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="${SCRIPT_DIR}/../lib" +# shellcheck source=assertions.sh +source "${SCRIPT_DIR}/assertions.sh" +# shellcheck source=../lib/generate-nav.sh +source "${LIB_DIR}/generate-nav.sh" + +tmpdir="$(mktemp -d)" +trap 'rm -rf "$tmpdir"' EXIT + +WEB_DOCS_DIR="${tmpdir}/.web-docs" +mkdir -p "${WEB_DOCS_DIR}/components/builder/iso" +mkdir -p "${WEB_DOCS_DIR}/components/builder/vmx" +printf '%s\n' '# Builder' >"${WEB_DOCS_DIR}/components/builder/iso/README.md" +printf '%s\n' '# Builder' >"${WEB_DOCS_DIR}/components/builder/vmx/README.md" + +INCLUDE_EXTRA=true generate_nav >"${tmpdir}/with-extra.toml" +INCLUDE_EXTRA=false generate_nav >"${tmpdir}/without-extra.toml" + +with_extra="$(cat "${tmpdir}/with-extra.toml")" +without_extra="$(cat "${tmpdir}/without-extra.toml")" + +assert_contains "$with_extra" '"builders/index.md"' \ + "includes builders index when INCLUDE_EXTRA=true" +assert_not_contains "$without_extra" '"builders/index.md"' \ + "omits builders index when INCLUDE_EXTRA=false" +assert_not_contains "$without_extra" "Community" \ + "omits community nav when INCLUDE_EXTRA=false" +assert_contains "$with_extra" '{ "vmware-iso" = "builders/iso.md" }' \ + "uses display name for iso builder" +assert_contains "$with_extra" '{ "vmware-vmx" = "builders/vmx.md" }' \ + "uses display name for vmx builder" + +echo "All generate-nav tests passed." diff --git a/docs-site/scripts/test/test-group-example-tabs.sh b/docs-site/scripts/test/test-group-example-tabs.sh new file mode 100755 index 00000000..4da2a133 --- /dev/null +++ b/docs-site/scripts/test/test-group-example-tabs.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for grouping HCL/JSON examples into content tabs in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/group-example-tabs.sh +source "${LIB_DIR}/group-example-tabs.sh" +# shellcheck source=../lib/format-example-labels.sh +source "${LIB_DIR}/format-example-labels.sh" + +test_top_level_pair_hcl_first() { + local input=$'**HCL Example:**\n\n```hcl\nfoo = 1\n```\n\n**JSON Example:**\n\n```json\n{}\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" '=== "HCL"' "hcl tab present" + assert_contains "$output" '=== "JSON"' "json tab present" + assert_not_contains "$output" "**HCL Example:**" "label removed" + assert_contains "$output" $'=== "HCL"\n\n ```hcl' "hcl fence indented under hcl tab" +} + +test_top_level_pair_json_first() { + local input=$'**JSON Example:**\n\n```json\n{}\n```\n\n**HCL Example:**\n\n```hcl\nfoo = 1\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $'=== "HCL"\n\n ```hcl' "hcl tab first even when json label first" + assert_contains "$output" $'=== "JSON"\n\n ```json' "json tab second" +} + +test_nested_list_pair() { + local input=$' **HCL Example:**\n ```hcl\n foo = 1\n ```\n\n **JSON Example:**\n ```json\n {}\n ```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $' === "HCL"' "nested hcl tab indented" + assert_contains "$output" $' === "JSON"' "nested json tab indented" + assert_contains "$output" $' ```hcl' "nested hcl fence indented under tab" + assert_contains "$output" $' ```json' "nested json fence indented under tab" +} + +test_nested_solo_hcl_keeps_fence_indent() { + local input=$' **HCL Example:**\n ```hcl\n floppy_content = {\n "meta-data" = "x"\n }\n ```\n\n- next' + local output + output="$(group_example_tabs "$input")" + assert_not_contains "$output" "**HCL Example:**" "solo label removed" + assert_contains "$output" $' ```hcl\n floppy_content' "fence stays at list indent" + assert_not_contains "$output" $' ```hcl' "fence not double-indented" +} + +test_single_hcl_unchanged() { + local input=$'**HCL Example:**\n\n```hcl\nfoo = 1\n```\n\nMore text.' + local output + output="$(group_example_tabs "$input")" + assert_not_contains "$output" "**HCL Example:**" "single label removed" + assert_contains "$output" '```hcl' "single fence kept" + assert_not_contains "$output" '=== "HCL"' "no tabs for single example" +} + +test_skip_inside_fence() { + local input=$'```text\n**HCL Example:**\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" "**HCL Example:**" "label inside fence unchanged" + assert_not_contains "$output" '=== "HCL"' "no tabs inside fence" +} + +test_plural_labels() { + local input=$'**HCL Examples:**\n\n```hcl\nfoo = 1\n```\n\n**JSON Examples:**\n\n```json\n{}\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" '=== "HCL"' "plural labels grouped" + assert_contains "$output" '=== "JSON"' "plural labels grouped" +} + +test_nested_preserves_inner_indentation() { + local input=$' **HCL Example:**\n ```hcl\n vapp {\n properties = {\n hostname = var.hostname\n }\n }\n ```\n\n **JSON Example:**\n ```json\n "vapp": {\n "properties": {\n "hostname": "example"\n }\n }\n ```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $' properties = {' "hcl nested block indent preserved" + assert_contains "$output" $' hostname = var.hostname' "hcl deep indent preserved" + assert_contains "$output" $' "properties": {' "json nested block indent preserved" +} + +test_top_level_nested_hcl_indent() { + local input=$'**HCL Example:**\n\n```hcl\nsource "vsphere-iso" "example" {\n customize {\n linux_options {\n host_name = "foo"\n }\n }\n}\n```\n\n**JSON Example:**\n\n```json\n{}\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $' host_name = "foo"' "nested hcl indent preserved in top-level tab" + assert_contains "$output" $' linux_options {' "hcl block indent preserved in top-level tab" +} + +test_preserves_inner_indentation() { + local input=$'**HCL Example:**\n\n```hcl\nfoo {\n bar = 1\n}\n```\n\n**JSON Example:**\n\n```json\n{\n "baz": 2\n}\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $' bar = 1' "hcl inner indent preserved" + assert_contains "$output" $' "baz": 2' "json inner indent preserved" +} + +test_multi_fence_section_separate_blocks() { + local input=$'**HCL Examples:**\n\n```hcl\ncustomize {\n windows_sysprep_text = file("a.xml")\n}\n```\n\n```hcl\ncustomize {\n windows_sysprep_text = templatefile("a.xml", {})\n}\n```\n\n**JSON Examples:**\n\n```json\n{"customize": {}}\n```\n\n```json\n{"customize": {"var1": "example"}}\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $' customize {\n windows_sysprep_text = file("a.xml")' "first hcl block" + assert_contains "$output" $' }\n ```\n\n ```hcl' "separate hcl fences in tab" + assert_contains "$output" $'templatefile("a.xml", {})' "second hcl block" + assert_not_contains "$output" $' ```' "no over-indented fence markers in content" +} + +test_iso_style_section_grouping() { + local input=$'**JSON Example:**\n\n```json\n{}\n```\n\n```json\n{"a":1}\n```\n\n**HCL Example:**\n\n```hcl\nfoo = 1\n```\n\n```hcl\nbar = 2\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" '=== "HCL"' "hcl tab in section group" + assert_contains "$output" '=== "JSON"' "json tab in section group" + assert_not_contains "$output" "**JSON Example:**" "json label removed" + assert_not_contains "$output" "**HCL Example:**" "hcl label removed" + assert_contains "$output" $'bar = 2' "second hcl block preserved" +} + +test_mismatched_indent_pair() { + local input=$' **HCL Example:**\n ```hcl\nfoo = 1\n```\n\n**JSON Example:**\n```json\n{}\n```' + local output + output="$(group_example_tabs "$input")" + assert_contains "$output" $' === "HCL"' "nested hcl tab" + assert_contains "$output" $' === "JSON"' "json tab paired across indents" +} + +test_supervisor_examples_with_image_import_variant() { + local input + input="$(cat <<'EOF' +- Examples are available in the [examples](https://github.com/example) directory. + +HCL Example: + +```hcl +source "vsphere-supervisor" "example" { + image_name = "ubuntu" +} +``` + +HCL Example with image import: + +```hcl +source "vsphere-supervisor" "example" { + import_source_url = "https://example.com/example.ovf" +} +``` + +JSON Example: + +```json +{ + "builders": [{ "type": "vsphere-supervisor", "image_name": "ubuntu" }] +} +``` + +JSON Example with image import: + +```json +{ + "builders": [{ "type": "vsphere-supervisor", "import_source_url": "https://example.com/example.ovf" }] +} +``` +EOF +)" + local output + output="$(format_example_labels "$input")" + output="$(group_example_tabs "$output")" + assert_contains "$output" $'=== "HCL"\n\n ```hcl\n source "vsphere-supervisor"' "basic hcl tab" + assert_contains "$output" $'=== "JSON"\n\n ```json' "basic json tab" + assert_contains "$output" "**Example with image import:**" "variant heading before second tab group" + assert_not_contains "$output" "HCL Example with image import:" "variant hcl label consumed" +} + +test_cd_files_prose_not_swallowed() { + local input + input="$(cat <<'EOF' +- `cd_files` ([]string) - Place files on a CD. + + **JSON Example:** + ```json + "cd_files": ["a"] + ``` + + **HCL Example:** + ```hcl + cd_files = ["a"] + ``` + + The above will create a CD with two files. + + Since globbing is also supported, + + ```hcl + cd_files = ["./dir/*"] + ``` + +- next +EOF +)" + local output + output="$(format_example_labels "$input")" + output="$(group_example_tabs "$output")" + assert_contains "$output" "The above will create a CD with two files." "prose after examples preserved" + assert_contains "$output" 'cd_files = ["./dir/*"]' "globbing example preserved" +} + +main() { + test_top_level_pair_hcl_first + test_top_level_pair_json_first + test_nested_list_pair + test_top_level_nested_hcl_indent + test_preserves_inner_indentation + test_nested_preserves_inner_indentation + test_nested_solo_hcl_keeps_fence_indent + test_single_hcl_unchanged + test_skip_inside_fence + test_plural_labels + test_multi_fence_section_separate_blocks + test_iso_style_section_grouping + test_mismatched_indent_pair + test_supervisor_examples_with_image_import_variant + test_cd_files_prose_not_swallowed + echo "All group-example-tabs tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-inject-home-data-sources.sh b/docs-site/scripts/test/test-inject-home-data-sources.sh new file mode 100755 index 00000000..e2180b97 --- /dev/null +++ b/docs-site/scripts/test/test-inject-home-data-sources.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for data sources section injection in staged markdown. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="${SCRIPT_DIR}/../lib" +# shellcheck source=assertions.sh +source "${SCRIPT_DIR}/assertions.sh" +# shellcheck source=../lib/inject-home-data-sources.sh +source "${LIB_DIR}/inject-home-data-sources.sh" + +tmpdir="$(mktemp -d)" +trap 'rm -rf "$tmpdir"' EXIT + +WEB_DOCS_DIR="${tmpdir}/.web-docs" +STAGING_DIR="${tmpdir}/staging" +mkdir -p "${WEB_DOCS_DIR}/components/data-source/virtualmachine" +mkdir -p "$STAGING_DIR" + +cat >"${WEB_DOCS_DIR}/components/data-source/virtualmachine/README.md" <<'EOF' +Type: `vsphere-virtualmachine` + +This data source retrieves information about existing virtual machines from vSphere. + +## Configuration Reference +EOF + +cat >"${STAGING_DIR}/index.md" <<'EOF' +### Components + +The plugin includes builders and post-processors for creating virtual machine images. + +#### Builders + +- [vsphere-iso](builders/vsphere-iso/) - Builds from ISO. + +#### Post-Processors + +- [vsphere](../post-processors/vsphere/) - Uploads artifacts. + +### Requirements + +Plugin requirements go here. +EOF + +inject_home_data_sources "${STAGING_DIR}/index.md" +content="$(cat "${STAGING_DIR}/index.md")" + +assert_contains "$content" "builders, post-processors, and data sources" \ + "updates components intro when data sources exist" +assert_contains "$content" "#### Data Sources" \ + "adds Data Sources heading" +assert_contains "$content" "[virtualmachine](data-sources/virtualmachine/)" \ + "lists data source with site-relative link" +assert_contains "$content" "retrieves information about existing virtual machines" \ + "includes data source summary" +assert_contains "$content" $'#### Post-Processors\n\n- [vsphere]' \ + "preserves post-processors section" +assert_contains "$content" $'#### Data Sources\n\n- [virtualmachine]' \ + "places data sources after post-processors" + +WEB_DOCS_DIR="${tmpdir}/.web-docs-empty" +mkdir -p "${WEB_DOCS_DIR}/components/builder/vsphere-iso" +cat >"${WEB_DOCS_DIR}/components/builder/vsphere-iso/README.md" <<'EOF' +Builder docs. +EOF + +cat >"${STAGING_DIR}/index-no-ds.md" <<'EOF' +### Components + +The plugin includes builders and post-processors for creating virtual machine images. + +#### Post-Processors + +- [vsphere](../post-processors/vsphere/) - Uploads artifacts. + +### Requirements +EOF + +inject_home_data_sources "${STAGING_DIR}/index-no-ds.md" +content="$(cat "${STAGING_DIR}/index-no-ds.md")" + +assert_not_contains "$content" "#### Data Sources" \ + "skips injection when no data sources exist" +assert_contains "$content" "builders and post-processors" \ + "leaves intro unchanged when no data sources exist" + +echo "All inject-home-data-sources tests passed." diff --git a/docs-site/scripts/test/test-normalize-list-spacing.sh b/docs-site/scripts/test/test-normalize-list-spacing.sh new file mode 100755 index 00000000..08a18a34 --- /dev/null +++ b/docs-site/scripts/test/test-normalize-list-spacing.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for collapsing and preserving blank lines between list item in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/strip-codegen-comments.sh +source "${LIB_DIR}/strip-codegen-comments.sh" +# shellcheck source=../lib/normalize-list-spacing.sh +source "${LIB_DIR}/normalize-list-spacing.sh" + +test_strip_then_normalize_list_spacing() { + local input + input="$(cat <<'EOF' +- `cd_label` (string) - CD Label + + + + + +- `cdrom_type` (string) - Defaults to `ide`. +EOF +)" + local output + output="$(strip_codegen_comments "$input")" + output="$(normalize_list_spacing "$output")" + assert_not_contains "$output" "Code generated from" "codegen comments removed" + assert_contains "$output" $'- `cd_label` (string) - CD Label\n- `cdrom_type`' "list items adjacent" +} + +test_preserve_blank_after_code_block() { + local input + input="$(cat <<'EOF' +- `cd_files` (list) - Long item with code: + + ```hcl + x = 1 + ``` + +- `cd_label` (string) - CD Label +EOF +)" + local output + output="$(normalize_list_spacing "$input")" + assert_contains "$output" $'```\n\n- `cd_label`' "blank preserved after code block" +} + +test_collapse_blank_between_simple_items() { + local input + input="$(cat <<'EOF' +- `cd_label` (string) - CD Label + +- `cdrom_type` (string) - Defaults to `ide`. +EOF +)" + local output + output="$(normalize_list_spacing "$input")" + assert_contains "$output" $'CD Label\n- `cdrom_type`' "blank removed between simple items" + assert_not_contains "$output" $'CD Label\n\n- `cdrom_type`' "no double newline between items" +} + +test_collapse_blank_after_nested_sublist() { + local input + input="$(cat <<'EOF' + * `uuid` - UUID. + + For example, adding the following outputs the + MAC addresses for each Ethernet device: +EOF +)" + local output + output="$(normalize_list_spacing "$input")" + assert_contains "$output" $'UUID.\n For example' "continuation follows nested sublist" + assert_not_contains "$output" $'UUID.\n\n For example' "no blank after nested sublist" +} + +test_fix_sibling_list_breaks_after_tabs() { + local input=$' === "JSON"\n\n ```json\n {}\n ```\n- `network_interface` (NetworkInterfaces) - Next field.' + local output + output="$(normalize_list_spacing "$input")" + assert_contains "$output" $' ```\n\n- `network_interface`' "blank line before sibling list item" +} + +main() { + test_strip_then_normalize_list_spacing + test_preserve_blank_after_code_block + test_collapse_blank_between_simple_items + test_collapse_blank_after_nested_sublist + test_fix_sibling_list_breaks_after_tabs + echo "All normalize-list-spacing tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-repair-code-fences.sh b/docs-site/scripts/test/test-repair-code-fences.sh new file mode 100755 index 00000000..91b8b7fe --- /dev/null +++ b/docs-site/scripts/test/test-repair-code-fences.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for closing unclosed fenced code blocks in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/repair-code-fences.sh +source "${LIB_DIR}/repair-code-fences.sh" + +test_repair_unclosed_fences() { + local input=$' ```json\n {\n "customize": {\n "windows_sysprep_text": "example"\n }\n }\n\n- `network_interface` (NetworkInterfaces) - Next field.' + local output + output="$(repair_unclosed_fences "$input")" + assert_contains "$output" $'```\n\n- `network_interface`' "closing fence inserted before list item" +} + +test_preserve_yaml_playbook_fence() { + local input + input="$(cat <<'EOF' +```yaml +--- +# cleanup-playbook.yml +- name: Clean up source virtual machine + hosts: default + tasks: + - name: Truncate machine id + file: + state: absent +``` +EOF +)" + local output + output="$(repair_unclosed_fences "$input")" + assert_contains "$output" $'```yaml\n---\n# cleanup-playbook.yml\n- name: Clean up' "yaml playbook stays inside fence" + assert_not_contains "$output" $'---\n```\n\n#' "fence not closed at yaml document marker" +} + +main() { + test_repair_unclosed_fences + test_preserve_yaml_playbook_fence + echo "All repair-code-fences tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-rewrite-integration-links.sh b/docs-site/scripts/test/test-rewrite-integration-links.sh new file mode 100755 index 00000000..10f57fb6 --- /dev/null +++ b/docs-site/scripts/test/test-rewrite-integration-links.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for integration link rewriting in staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/rewrite-integration-links.sh +source "${LIB_DIR}/rewrite-integration-links.sh" + +test_vmware_vsphere_builder_link() { + local input='[vsphere-iso](/packer/integrations/vmware/vsphere/latest/components/builder/vsphere-iso)' + local output + output="$(rewrite_integration_links "$input")" + assert_contains "$output" 'builders/vsphere-iso.md' "vmware vsphere builder link" + assert_not_contains "$output" '/packer/integrations/vmware' "no raw integration path" +} + +test_hashicorp_vsphere_post_processor_link() { + local input='[vSphere](/packer/integrations/hashicorp/vsphere/latest/components/post-processor/vsphere)' + local output + output="$(rewrite_integration_links "$input")" + assert_contains "$output" 'post-processors/vsphere.md' "hashicorp vsphere post-processor link" +} + +test_hashicorp_vmware_builder_link_with_anchor() { + local input='[vApp](/packer/integrations/hashicorp/vmware/latest/components/builder/vsphere-clone#vapp-options-configuration)' + local output + output="$(rewrite_integration_links "$input")" + assert_contains "$output" 'builders/vsphere-clone.md#vapp-options-configuration' "builder link with anchor" +} + +test_cross_plugin_link_to_hashicorp() { + local input='[`ssh_interface`](/packer/integrations/hashicorp/amazon/latest/components/builder/ebs#ssh_interface)' + local output + output="$(rewrite_integration_links "$input")" + assert_contains "$output" 'https://developer.hashicorp.com/packer/integrations/hashicorp/amazon/latest/components/builder/ebs#ssh_interface' "cross-plugin external link" +} + +test_packer_docs_link() { + local input='[packer init](/packer/docs/commands/init)' + local output + output="$(rewrite_integration_links "$input")" + assert_contains "$output" 'https://developer.hashicorp.com/packer/docs/commands/init' "packer docs external link" +} + +main() { + test_vmware_vsphere_builder_link + test_hashicorp_vsphere_post_processor_link + test_hashicorp_vmware_builder_link_with_anchor + test_cross_plugin_link_to_hashicorp + test_packer_docs_link + echo "All rewrite-integration-links tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-stage-markdown.sh b/docs-site/scripts/test/test-stage-markdown.sh new file mode 100755 index 00000000..827391d5 --- /dev/null +++ b/docs-site/scripts/test/test-stage-markdown.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Integration tests for the staged markdown transform pipeline. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/stage-markdown.sh +source "${LIB_DIR}/stage-markdown.sh" + +test_export_options_examples() { + local input + input="$(cat <<'EOF' +- `options` ([]string) - Advanced image export options. Available options include: + * `mac` - MAC address is exported for each Ethernet device. + * `uuid` - UUID is exported for the virtual machine. + * `nodevicesubtypes` - Resource subtypes for CD/DVD drives, floppy + drives, and SCSI controllers are not exported. + + For example, adding the following export configuration option outputs the + MAC addresses for each Ethernet device in the OVF descriptor: + + HCL Example: + + ```hcl + export { options = ["mac"] } + ``` + + JSON: Example: + + ```json + "export": { "options": ["mac"] } + ``` + +- `output_format` (string) - The output format. +EOF +)" + local output + output="$(transform_markdown "$input")" + assert_contains "$output" " * \`mac\`" "nested option list" + assert_contains "$output" $'exported.\n For example' "example text in list item" + assert_contains "$output" '=== "HCL"' "hcl example tab" + assert_contains "$output" '=== "JSON"' "json example tab" + assert_not_contains "$output" "JSON: Example:" "legacy json label removed" +} + +main() { + test_export_options_examples + echo "All stage-markdown integration tests passed." +} + +main "$@" diff --git a/docs-site/scripts/test/test-strip-codegen-comments.sh b/docs-site/scripts/test/test-strip-codegen-comments.sh new file mode 100755 index 00000000..df330b7a --- /dev/null +++ b/docs-site/scripts/test/test-strip-codegen-comments.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# © Broadcom. All Rights Reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# SPDX-License-Identifier: MPL-2.0 + +# Tests for removing generated-code HTML comments from staged markdown. + +set -euo pipefail + +TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="$(cd "${TEST_DIR}/.." && pwd)" +LIB_DIR="${SCRIPTS_DIR}/lib" + +# shellcheck source=assertions.sh +source "${TEST_DIR}/assertions.sh" +# shellcheck source=../lib/strip-codegen-comments.sh +source "${LIB_DIR}/strip-codegen-comments.sh" + +test_strip_codegen_comments() { + local input + input="$(cat <<'EOF' +- `cd_label` (string) - CD Label + + + + + +- `cdrom_type` (string) - Defaults to `ide`. +EOF +)" + local output + output="$(strip_codegen_comments "$input")" + assert_not_contains "$output" "Code generated from" "codegen comments removed" + assert_contains "$output" '`cd_label`' "first list item preserved" + assert_contains "$output" '`cdrom_type`' "second list item preserved" +} + +main() { + test_strip_codegen_comments + echo "All strip-codegen-comments tests passed." +} + +main "$@" diff --git a/docs-site/stylesheets/extra.css b/docs-site/stylesheets/extra.css new file mode 100644 index 00000000..2ff8c61b --- /dev/null +++ b/docs-site/stylesheets/extra.css @@ -0,0 +1,364 @@ +:root { + --bc-crimson: #a6192e; + --bc-pulse-red: #cc092f; + --bc-navy-blue: #002244; + --bc-navy-blue-dark: #001528; + --bc-chrome: #21223e; + --bc-gradient-purple: #6c4b94; + --bc-gradient-end: #1b1d36; + --bc-sapphire: #005caa; + --bc-cyan: #41b6e6; + --bc-aqua: #00c8c2; + --bc-cool-gray-11: #53565a; + --bc-cool-gray-1: #d9d9d6; + --md-text-font: "Inter", Arial, sans-serif; +} + +/* Light scheme */ +[data-md-color-scheme="default"] { + --md-primary-fg-color: var(--bc-navy-blue); + --md-primary-fg-color--light: var(--bc-sapphire); + --md-primary-fg-color--dark: var(--bc-navy-blue-dark); + --md-accent-fg-color: var(--bc-crimson); + --md-accent-fg-color--transparent: #a6192e1a; + --md-typeset-a-color: var(--bc-sapphire); + --md-typeset-mark-color: #a6192e26; +} + +[data-md-color-scheme="default"] .md-typeset a:hover, +[data-md-color-scheme="default"] .md-typeset a:focus-visible { + color: var(--bc-navy-blue); +} + +/* Dark scheme */ +[data-md-color-scheme="slate"] { + --md-primary-fg-color: var(--bc-navy-blue); + --md-primary-fg-color--light: var(--bc-sapphire); + --md-primary-fg-color--dark: var(--bc-navy-blue-dark); + --md-accent-fg-color: var(--bc-cyan); + --md-accent-fg-color--transparent: #41b6e61a; + --md-typeset-a-color: var(--bc-cyan); + --md-typeset-mark-color: #41b6e626; + /* MaterialX slate: 14% lightness vs Zensical's 5% near-black */ + --md-default-bg-color: hsla(var(--md-hue), 15%, 14%, 1); + --md-default-bg-color--light: hsla(var(--md-hue), 15%, 14%, 0.54); + --md-default-bg-color--lighter: hsla(var(--md-hue), 15%, 14%, 0.26); + --md-default-bg-color--lightest: hsla(var(--md-hue), 15%, 14%, 0.07); + --md-code-bg-color: hsla(var(--md-hue), 15%, 18%, 1); + --md-code-bg-color--light: hsla(var(--md-hue), 15%, 18%, 0.9); + --md-code-bg-color--lighter: hsla(var(--md-hue), 15%, 18%, 0.54); +} + +[data-md-color-scheme="slate"] .md-typeset a:hover, +[data-md-color-scheme="slate"] .md-typeset a:focus-visible { + color: var(--bc-aqua); +} + +/* Header and footer chrome */ +.md-header { + background-color: var(--bc-chrome); + color: #fff; +} + +.md-header .md-header__button, +.md-header .md-header__title, +.md-header .md-header__topic, +.md-header .md-header__ellipsis { + color: #fff; +} + +.md-header .md-icon svg { + fill: currentColor; +} + +.md-header::after, +.md-footer::before { + content: ""; + display: block; + width: 100%; + height: 7px; + background: linear-gradient( + to right, + var(--bc-pulse-red) 4%, + var(--bc-gradient-purple) 50%, + var(--bc-gradient-end) 96% + ); + pointer-events: none; +} + +/* Footer: match header */ +.md-footer, +.md-footer-meta { + background-color: var(--bc-chrome); + color: #fff; + border-top: none; +} + +.md-footer__link, +.md-footer__direction, +.md-footer__title, +.md-copyright, +.md-copyright__highlight { + color: #fff; +} + +.md-footer-meta.md-typeset a { + color: #fff; +} + +.md-footer-meta.md-typeset a:hover, +.md-footer-meta.md-typeset a:focus-visible { + color: var(--bc-cyan); +} + +.md-footer .md-social__link { + color: #fff; +} + +.md-footer .md-icon svg { + fill: currentColor; +} + +/* Mike outdated-version banner (non-latest docs) */ +.md-banner--warning { + background-color: var(--bc-crimson); + color: #fff; +} + +.md-banner--warning .md-banner__inner { + margin: 0.35rem auto; + padding: 0 0.8rem; +} + +.md-banner--warning::after { + content: ""; + display: block; + width: 100%; + height: 2px; + background: linear-gradient( + to right, + var(--bc-pulse-red) 4%, + var(--bc-gradient-purple) 50%, + var(--bc-gradient-end) 96% + ); + pointer-events: none; +} + +.md-version-banner { + display: flex; + align-items: center; + gap: 0.5rem; + margin: 0; +} + +.md-version-banner__icon { + flex-shrink: 0; + color: #fff; + line-height: 1; +} + +.md-version-banner__icon svg { + width: 0.95rem; + height: 0.95rem; + fill: none; + stroke: currentColor; +} + +.md-version-banner__text { + margin: 0; + font-size: 0.7rem; + line-height: 1.25; +} + +.md-version-banner__text a { + color: inherit; + text-decoration: underline; + text-underline-offset: 0.12em; +} + +.md-version-banner__text a:hover, +.md-version-banner__text a:focus-visible { + color: var(--bc-cool-gray-1); +} + +/* Version selector: content meta bar (relocated from header by version-slot.js) */ +.md-header__topic--version-mount { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.md-header__topic--version-mount .md-version { + display: none; +} + +.md-version-meta { + display: flex; + justify-content: flex-end; + align-items: center; + margin: 0.35rem 1.2rem 0; + min-height: 1.5rem; + padding-bottom: 0.35rem; + background-color: var(--md-default-bg-color); + border-bottom: 1px solid var(--md-default-fg-color--lightest); +} + +.md-version-meta__slot { + display: flex; + justify-content: flex-end; +} + +.md-version-meta .md-version { + position: relative; + flex-shrink: 0; + font-family: var(--md-code-font, ui-monospace, monospace); + font-size: 0.65rem; + height: auto; + z-index: 5; +} + +.md-version-meta .md-version__current { + margin: 0; + padding: 0.2rem 0.55rem; + top: 0; + border: 1px solid var(--md-default-fg-color--lighter); + border-radius: 0.2rem; + color: var(--md-default-fg-color); + background-color: var(--md-default-bg-color); + font-family: inherit; + font-size: inherit; + text-align: right; + letter-spacing: -0.01em; +} + +.md-version-meta .md-version__current:after { + margin-left: 0.35rem; +} + +.md-version-meta .md-version__alias { + margin-left: 0.25rem; + font-size: 0.92em; + opacity: 0.75; +} + +.md-version-meta .md-version__list { + top: calc(100% + 0.15rem); + bottom: auto; + left: auto; + right: 0; + margin: 0; + min-width: 9rem; + padding: 0.15rem 0; + font-family: inherit; + font-size: inherit; + text-align: right; + z-index: 6; + border: 1px solid var(--md-default-fg-color--lighter); + box-shadow: var(--md-shadow-z2); + background-color: var(--md-default-bg-color); +} + +.md-version-meta .md-version:focus-within .md-version__list, +.md-version-meta .md-version:hover .md-version__list { + border-color: var(--md-default-fg-color--light); +} + +.md-version-meta .md-version__link { + text-align: right; + padding: 0 0.65rem 0 1rem; + font-family: inherit; + font-size: inherit; + letter-spacing: -0.01em; +} + +/* Active nav and tabs: Crimson accent (light) / Cyan accent (dark) */ +[data-md-color-scheme="default"] .md-nav__link--active, +[data-md-color-scheme="default"] .md-nav__item--active > .md-nav__link { + color: var(--bc-crimson); +} + +[data-md-color-scheme="slate"] .md-nav__link--active, +[data-md-color-scheme="slate"] .md-nav__item--active > .md-nav__link { + color: var(--bc-cyan); +} + +/* Keyboard focus */ +a:focus-visible, +.md-nav__link:focus-visible, +.md-header__button:focus-visible { + outline: 2px solid var(--md-accent-fg-color); + outline-offset: 2px; +} + +/* External nav links in sidebar */ +.md-nav--primary .md-nav__link[href^="http"]:not(.md-nav__link--active) { + color: var(--md-typeset-a-color); +} + +/* Small external-link icon after label (Releases, Discussions, GitHub links, etc.) */ +.md-nav--primary .md-nav__link[href^="http"] .md-ellipsis::after { + content: ""; + display: inline-block; + width: 0.85em; + height: 0.85em; + margin-left: 0.25em; + vertical-align: -0.1em; + background-color: currentColor; + opacity: 0.75; + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E"); + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E"); + -webkit-mask-repeat: no-repeat; + -webkit-mask-position: center; + -webkit-mask-size: contain; +} + +/* + * Section children (e.g. vSphere ISO under Builders): indent so text lines up + * with the section title, not the section icon. Matches theme nav icon size/gap. + */ +:root { + --md-nav-nested-text-offset: 1.3em; +} + +[dir="ltr"] .md-nav--primary > .md-nav__list > .md-nav__item--nested > .md-nav > .md-nav__list { + margin-left: calc(0.6rem + var(--md-nav-nested-text-offset)); +} + +[dir="rtl"] .md-nav--primary > .md-nav__list > .md-nav__item--nested > .md-nav > .md-nav__list { + margin-right: calc(0.6rem + var(--md-nav-nested-text-offset)); +} + +/* Home page integration buttons */ +.integration-meta { + margin: 1rem 0 1.25rem; +} + +.integration-badges { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +/* Buttons: match header chrome */ +.md-typeset .md-button { + background-color: var(--bc-chrome); + border-color: var(--bc-chrome); + color: #fff; +} + +.md-typeset .md-button:hover, +.md-typeset .md-button:focus-visible { + background-color: var(--bc-gradient-end); + border-color: var(--bc-gradient-end); + color: #fff; +} diff --git a/docs-site/zensical.toml b/docs-site/zensical.toml new file mode 100644 index 00000000..2473b8c8 --- /dev/null +++ b/docs-site/zensical.toml @@ -0,0 +1,91 @@ +[project] +site_name = "Packer Plugin for VMware Desktop Hypervisors" +site_url = "https://vmware.github.io/packer-plugin-vmware/" +repo_url = "https://github.com/vmware/packer-plugin-vmware" +edit_uri = "edit/main/docs-site/extra/" +docs_dir = ".build/docs" +site_dir = "site" +copyright = "Copyright © Broadcom. All Rights Reserved.
    The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.
    Licensed under the Mozilla Public License, version 2.0." +extra_css = ["stylesheets/extra.css"] +extra_javascript = ["javascripts/version-slot.js"] + +[project.theme] +variant = "modern" +language = "en" +logo = "assets/header-logo.png" +font.text = "Inter" +custom_dir = "overrides" + +[project.theme.icon] +repo = "fontawesome/brands/github" + +features = [ + "navigation.instant", + "navigation.instant.prefetch", + "navigation.tracking", + "navigation.indexes", + "navigation.sections", + "navigation.path", + "navigation.top", + "navigation.footer", + "search.highlight", + "content.code.copy", + "content.tooltips", + "content.tabs.link", +] + +[[project.theme.palette]] +media = "(prefers-color-scheme)" +toggle.icon = "lucide/sun-moon" +toggle.name = "Switch to light mode" + +[[project.theme.palette]] +media = "(prefers-color-scheme: light)" +scheme = "default" +primary = "custom" +accent = "custom" +toggle.icon = "lucide/sun" +toggle.name = "Switch to dark mode" + +[[project.theme.palette]] +media = "(prefers-color-scheme: dark)" +scheme = "slate" +primary = "custom" +accent = "custom" +toggle.icon = "lucide/moon" +toggle.name = "Switch to system preference" + +[project.extra] +generator = false + +[project.extra.version] +provider = "mike" +default = ["latest", "development"] + +[[project.extra.social]] +icon = "fontawesome/brands/github" +link = "https://github.com/vmware/packer-plugin-vmware" + +[project.markdown_extensions.abbr] +[project.markdown_extensions.admonition] +[project.markdown_extensions.attr_list] +[project.markdown_extensions.def_list] +[project.markdown_extensions.footnotes] +[project.markdown_extensions.md_in_html] +[project.markdown_extensions.pymdownx.details] +[project.markdown_extensions.pymdownx.snippets] +base_path = [".build/docs"] +check_paths = true +[project.markdown_extensions.pymdownx.superfences] +[project.markdown_extensions.pymdownx.highlight] +anchor_linenums = true +line_spans = "__span" +pygments_lang_class = true +[project.markdown_extensions.pymdownx.tabbed] +alternate_style = true +combine_header_slug = true +[project.markdown_extensions.pymdownx.emoji] +emoji_generator = "zensical.extensions.emoji.to_svg" +emoji_index = "zensical.extensions.emoji.twemoji" +[project.markdown_extensions.toc] +permalink = true