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 00000000..f8eddb96 Binary files /dev/null and b/docs-site/assets/header-logo.png differ diff --git a/docs-site/extra/builders/index.md b/docs-site/extra/builders/index.md new file mode 100644 index 00000000..dff86239 --- /dev/null +++ b/docs-site/extra/builders/index.md @@ -0,0 +1,11 @@ +--- +icon: lucide/toolbox +title: Builders +--- + +# Builders + +Builders create machines and images on VMware desktop hypervisors, VMware +Fusion Pro and VMware Workstation Pro. + +Select a builder from the navigation for the configuration reference. diff --git a/docs-site/extra/community/code-of-conduct.md b/docs-site/extra/community/code-of-conduct.md new file mode 100644 index 00000000..4e070f92 --- /dev/null +++ b/docs-site/extra/community/code-of-conduct.md @@ -0,0 +1,136 @@ +--- +title: Code of Conduct +--- + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[opensource@broadcom.com][enforcement]. All complaints will be reviewed and +investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][cc], version +[v2.1][cc-v2.1]. + +Community Impact Guidelines were inspired by Mozilla's +[Code of Conduct Enforcement][mozilla-coce] consequence ladder. + +For answers to common questions about this code of conduct, please refer to the +[Frequently Asked Questions][cc-faq]. Translations are available in +[additional languages][cc-translations]. + +[cc]: https://www.contributor-covenant.org +[cc-faq]: https://www.contributor-covenant.org/faq/ +[cc-translations]: https://www.contributor-covenant.org/translations/ +[cc-v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/ +[enforcement]: mailto:opensource@broadcom.com?subject=Open%20Source%20Code%20of%20Conduct +[mozilla-coce]: https://github.com/mozilla/inclusion/ diff --git a/docs-site/extra/community/contributing.md b/docs-site/extra/community/contributing.md new file mode 100644 index 00000000..e4e478da --- /dev/null +++ b/docs-site/extra/community/contributing.md @@ -0,0 +1,144 @@ +--- +title: Contributing +--- + +# Contributing Guidelines + +We greatly value feedback and contributions from our community. + +Please review this document before submitting any issues or pull requests to ensure we have all the +necessary information to effectively collaborate on your contribution. + +--8<-- "community/issues-guidance.md" + +## Pull Requests + +Use GitHub pull requests to propose changes to the codebase using the following guidelines. + +!!! warning + + Pull requests that do not follow the guidelines may be closed by the maintainers + without further review. + +**Before** submitting a pull request, ensure that: + +1. You have [opened a discussion][gh-discussions] to discuss any **significant** work with + the maintainer(s). This ensures that your contribution is aligned with the + project's direction and avoids unnecessary work. +2. You have identified or [open an issue][gh-issues]. This ensures that your contribution + focuses on a specific topic and avoids duplicating effort. +3. You have forked the repository. Refer to the [GitHub documentation][gh-forks] for help. +3. You are working against the latest source on the `main` branch. You may need to + rebase your branch against the latest `main` branch. +4. You have created a topic branch based on `main`. Do not work directly on the `main` branch. +5. You have modified the source based on logical units of work. Focus on the specific change + you are contributing. Pull requests that contain multiple unrelated changes will be + rejected. +4. You have followed the existing style and conventions of the project. +5. You have added tests for your changes. +5. You have generated the updated documentation and associated assets by running `make generate`. +5. You have tested building the plugin by running `make build`. +7. You have tested your changes with a local build of the plugin by running `make dev`. +9. You have verified all new and existing tests are passing by running `make test`. +10. You have used [Conventional Commits][conventional-commits] format for commit messages. +11. You have signed-off and committed your changes [using clear commit messages][git-commit]. + +When opening a pull request, ensure that: + +1. You title your pull request using the [Conventional Commits][conventional-commits] format. +2. You provide a detailed description of the changes in the pull request template. +2. You open any work-in-progress pull requests as a draft. +3. You mark the pull request as ready for review when you are ready for it to be reviewed. +4. You follow the status checks for the pull request to ensure that all checks are passing. +5. You stay involved in the conversation with the maintainers to ensure that your contribution + can be reviewed. + +!!! tip + + If you have any questions about the contribution process, open a [discussion][gh-discussions]. + +**Contributor Flow** + +This is an outline of the contributor workflow: + +Example: + +```shell +git remote add upstream https://github.com//.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