diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c982919..a363d80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,23 @@ jobs: - pre-commit uses: ./.github/workflows/test-action.yml + docs: + name: Docs + needs: + - pre-commit + uses: ./.github/workflows/docs-action.yml + + deploy-docs: + name: Deploy Docs + uses: ./.github/workflows/deploy-docs-action.yml + # Only build doc deployments from the main branch of the org repo and never for PRs. + if: >- + github.event_name != 'pull_request' && + github.ref == 'refs/heads/main' + needs: + - docs + - test + set-pipeline-exit-status: # This step is just so we can make github require this step, to pass checks # on a pull request instead of requiring all @@ -41,29 +58,30 @@ jobs: if: always() needs: - test + - docs steps: - - name: Download Exit Status Files - if: always() - uses: actions/download-artifact@v4 - with: - path: exitstatus - pattern: exitstatus-* - merge-multiple: true + - name: Download Exit Status Files + if: always() + uses: actions/download-artifact@v4 + with: + path: exitstatus + pattern: exitstatus-* + merge-multiple: true - - name: Delete Exit Status Artifacts - if: always() - uses: geekyeggo/delete-artifact@v5 - with: - name: exitstatus-* - useGlob: true - failOnError: false + - name: Delete Exit Status Artifacts + if: always() + uses: geekyeggo/delete-artifact@v5 + with: + name: exitstatus-* + useGlob: true + failOnError: false - - name: Set Pipeline Exit Status - run: | - tree exitstatus - grep -RE 'failure|cancelled' exitstatus/ && exit 1 || exit 0 + - name: Set Pipeline Exit Status + run: | + tree exitstatus + grep -RE 'failure|cancelled' exitstatus/ && exit 1 || exit 0 - - name: Done - if: always() - run: - echo "All workflows finished" + - name: Done + if: always() + run: + echo "All workflows finished" diff --git a/.github/workflows/deploy-docs-action.yml b/.github/workflows/deploy-docs-action.yml new file mode 100644 index 0000000..97f357c --- /dev/null +++ b/.github/workflows/deploy-docs-action.yml @@ -0,0 +1,76 @@ +name: Publish Documentation + +on: + workflow_call: + inputs: + # This is the name of the regular artifact that should + # be transformed into a GitHub Pages deployment. + artifact-name: + type: string + required: false + default: html-docs + +jobs: + + # The released docs are not versioned currently, only the latest ones are deployed. + # + # Versioning support would require either (better): + # * Rebuilding docs for all versions when a new release is made + # * Version selector support in `furo`: https://github.com/pradyunsg/furo/pull/500 + # + # or (more basic): + # * using the `gh-pages` branch and peaceiris/actions-gh-pages + # to be able to deploy to subdirectories. The implementation via + # actions/deploy-pages always replaces the directory root. + + Deploy-Docs-GH-Pages: + name: Publish Docs to GitHub Pages + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + permissions: + pages: write + id-token: write + + runs-on: ubuntu-latest + steps: + - name: Download built docs + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: html-docs + + - name: Upload GitHub Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + name: html-docs-pages + path: html-docs + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + with: + artifact_name: html-docs-pages + + - name: Delete GitHub Pages artifact + if: always() + uses: geekyeggo/delete-artifact@v5 + with: + name: html-docs-pages + failOnError: false + + - name: Set Exit Status + if: always() + run: | + mkdir exitstatus + echo "${{ job.status }}" > exitstatus/${{ github.job }} + + - name: Upload Exit Status + if: always() + uses: actions/upload-artifact@v4 + with: + name: exitstatus-${{ github.job }} + path: exitstatus + if-no-files-found: error diff --git a/.github/workflows/docs-action.yml b/.github/workflows/docs-action.yml new file mode 100644 index 0000000..39ccae0 --- /dev/null +++ b/.github/workflows/docs-action.yml @@ -0,0 +1,52 @@ +name: Build Documentation + +on: + workflow_call: + +jobs: + Docs: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 For Nox + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Nox + run: | + python -m pip install --upgrade pip + pip install nox + + - name: Install Doc Requirements + run: | + nox --force-color -e docs --install-only + + - name: Build Docs + env: + SKIP_REQUIREMENTS_INSTALL: true + run: | + nox --force-color -e docs + + - name: Upload built docs as artifact + uses: actions/upload-artifact@v4 + with: + name: html-docs + path: docs/_build/html + + - name: Set Exit Status + if: always() + run: | + mkdir exitstatus + echo "${{ job.status }}" > exitstatus/${{ github.job }} + + - name: Upload Exit Status + if: always() + uses: actions/upload-artifact@v4 + with: + name: exitstatus-${{ github.job }} + path: exitstatus + if-no-files-found: error diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 73626e4..04d76bf 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -14,4 +14,6 @@ jobs: uses: ./.github/workflows/ci.yml permissions: contents: write + pages: write + id-token: write pull-requests: read diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..aec3363 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +The changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +This project uses [Semantic Versioning](https://semver.org/) - MAJOR.MINOR.PATCH + +# Changelog diff --git a/changelog/.template.jinja b/changelog/.template.jinja new file mode 100644 index 0000000..0cf429a --- /dev/null +++ b/changelog/.template.jinja @@ -0,0 +1,15 @@ +{% if sections[""] %} +{% for category, val in definitions.items() if category in sections[""] %} + +### {{ definitions[category]['name'] }} + +{% for text, values in sections[""][category].items() %} +- {{ text }} {{ values|join(', ') }} +{% endfor %} + +{% endfor %} +{% else %} +No significant changes. + + +{% endif %} diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_ext/saltdomain.py b/docs/_ext/saltdomain.py new file mode 100644 index 0000000..7a85489 --- /dev/null +++ b/docs/_ext/saltdomain.py @@ -0,0 +1,18 @@ +""" +Copied/distilled from Salt doc/_ext/saltdomain.py in order to be able +to use Salt's custom doc refs. +""" + + +def setup(app): + app.add_crossref_type( + directivename="conf_master", + rolename="conf_master", + indextemplate="pair: %s; conf/master", + ) + app.add_crossref_type( + directivename="conf_minion", + rolename="conf_minion", + indextemplate="pair: %s; conf/minion", + ) + return {"parallel_read_safe": True, "parallel_write_safe": True} diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100755 index 0000000..4d407ea --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,141 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +# -- Path setup -------------------------------------------------------------- +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import datetime +import os +import sys +from pathlib import Path + +try: + docs_basepath = os.path.abspath(os.path.dirname(__file__)) +except NameError: + # sphinx-intl and six execute some code which will raise this NameError + # assume we're in the doc/ directory + docs_basepath = os.path.abspath(os.path.dirname(".")) + +PROJECT_ROOT_DIR = Path(docs_basepath).parent + +addtl_paths = ("_ext",) # custom Sphinx extensions + +for addtl_path in addtl_paths: + sys.path.insert(0, os.path.abspath(os.path.join(docs_basepath, addtl_path))) + +# -- Project information ----------------------------------------------------- +this_year = datetime.datetime.today().year +copyright = f"2023 - {this_year}, Salt Extensions organization" + +# Variables to pass into the docs from sitevars.rst for rst substitution +with open("sitevars.rst") as site_vars_file: + site_vars = site_vars_file.read().splitlines() + +rst_prolog = """ +{} +""".format( + "\n".join(site_vars[:]) +) + +# -- General configuration --------------------------------------------------- + +linkcheck_ignore = [r"http://localhost:\d+"] + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.intersphinx", + "sphinxcontrib.spelling", + "sphinx_copybutton", + "sphinxcontrib.towncrier.ext", + "myst_parser", + "sphinx_inline_tabs", +] + +myst_enable_extensions = [ + "colon_fence", + "deflist", + "tasklist", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + ".vscode", + ".venv", + ".git", + ".gitlab-ci", + ".gitignore", + "sitevars.rst", +] + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "furo" +html_title = "Salt Extensions" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = "" + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. Favicons can be up to at least 228x228. PNG +# format is supported as well, not just .ico' +html_favicon = "" + +# ----- Intersphinx Config ----------------------------------------------------------------------------------------> +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "pytest": ("https://docs.pytest.org/en/stable", None), + "salt": ("https://docs.saltproject.io/en/latest", None), + "saltfactories": ("https://pytest-salt-factories.readthedocs.io/en/latest", None), +} +# <---- Intersphinx Config ----------------------------------------------------------------------------------------- + +# Towncrier draft config +towncrier_draft_autoversion_mode = "draft" +towncrier_draft_include_empty = True +towncrier_draft_working_directory = str(PROJECT_ROOT_DIR) + + +def setup(app): + app.add_crossref_type( + directivename="fixture", + rolename="fixture", + indextemplate="pair: %s; fixture", + ) + app.add_crossref_type( + directivename="question", + rolename="question", + indextemplate="single: %s; ref/questions", + ) + return {"parallel_read_safe": True, "parallel_write_safe": True} + + # Allow linking to pytest's confvals. + app.add_object_type( + "confval", + "pytest-confval", + objname="configuration value", + indextemplate="pair: %s; configuration value", + ) diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..4d73a5f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,44 @@ +Create and Maintain Salt Extensions +=================================== + +This is the documentation for `salt-extension-copier `_, a Copier +template for creating and maintaining :ref:`Salt extensions `. + +It is intended to be a one-stop shop for all topics related to the +lifecycle of Salt extensions, from creating (or extracting) an extension, +submitting it to the ``salt-extensions`` organization, +building the docs and releases and publishing it on PyPI. + +.. toctree:: + :maxdepth: 2 + :caption: Quickstart + :hidden: + + topics/installation + topics/creation + +.. toctree:: + :maxdepth: 2 + :caption: Guides + :hidden: + + topics/building + topics/testing + topics/publishing + topics/organization + topics/extraction + +.. toctree:: + :maxdepth: 2 + :caption: Reference + :hidden: + + ref/questions + ref/concepts + ref/changelog + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/ref/changelog.md b/docs/ref/changelog.md new file mode 100644 index 0000000..8152e33 --- /dev/null +++ b/docs/ref/changelog.md @@ -0,0 +1,12 @@ +# Changelog + +The changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +This project uses [Semantic Versioning](https://semver.org/) - MAJOR.MINOR.PATCH + +```{towncrier-draft-entries} +``` + +```{include} ../../CHANGELOG.md +:start-after: '# Changelog' +``` diff --git a/docs/ref/concepts.md b/docs/ref/concepts.md new file mode 100644 index 0000000..9715f05 --- /dev/null +++ b/docs/ref/concepts.md @@ -0,0 +1,48 @@ +# Concepts + +The following describes important concepts around Salt extensions on a high level. + +(saltext-ref)= +## Salt extension + +Salt extensions are `pip`-installable Python packages that hook into Salt's module +loading system and can thus provide all kinds of modules, much like custom +extension modules in `salt://_modules`, `salt://_states` etc. + +In contrast to custom modules, they can be developed inside a dedicated +testing framework similar to the one found in Salt core and have versioned +releases. On a downside, they need to be installed on each node separately. + +(great-migration-ref)= +## Great module migration +TODO + +(gh-org-ref)= +## GitHub organization + +While anyone can create and release a Salt extension, the [salt-extensions GitHub organization][saltext-org] +was created as a community-run umbrella to provide a more centralized and +frictionless experience for both developers and users. + +It is intended to both host existing sets of modules extracted from Salt core +from the `3008` release onwards as well as completely new extensions. + +Do you care about a set of modules that will be removed from Salt core? +Do you want to publish some awesome new Salt functionality? +Getting involved is easy! Just head over to the `salt-extensions` channel +on the [community Discord][discord-invite] and introduce yourself. + +## Copier template + +The `salt-extension-copier` template reduces overhead in creating and maintaining +Salt extensions a lot. Based on a set of inputs, it creates an individualized +boilerplate for anyone to develop shiny new Salt functionality. +When new template versions are released, an existing project's boilerplate +can be updated in an automated way. + +Very important: You don't need to publish inside the organization to use it! +It can be used for individual projects as well. + + +[saltext-org]: https://github.com/salt-extensions +[discord-invite]: https://discord.gg/bPah23K7mG diff --git a/docs/ref/questions.md b/docs/ref/questions.md new file mode 100644 index 0000000..1d9320b --- /dev/null +++ b/docs/ref/questions.md @@ -0,0 +1,235 @@ +# Template questions + +During project creation or when an update introduces a new variable, you will be asked some questions. + +:::{question} project_name +::: +## `project_name` + +The name of the project, especially on PyPI. + +The final name depends on {question}`no_saltext_namespace`. + +Example: `vault` + +:::{important} +Do not include the `saltext.` namespace here. +::: + +:::{question} author +::: +## `author` + +The name of the author for PyPI package metadata and license. + +Example: `Foo Bar` + +:::{question} author_email +::: +## `author_email` + +A contact email address for PyPI package metadata. + +Example: `foo@b.ar` + +:::{question} integration_name +::: +## `integration_name` + +The name of the integrated service. Used in autogenerated docs/README. + +Example: `HashiCorp Vault` + +:::{question} summary +::: +## `summary` + +A short description of the project. Included in PyPI metadata, docs and README. + +Example: `Salt extension for interacting with HashiCorp Vault` + +:::{question} url +::: +## `url` + +The main URL of the project for PyPI metadata. Usually, this is the URL of the hosted git repository, but can be different if there is a dedicated website. + +Example: `https://github.com/salt-extensions/saltext-vault` + +:::{question} source_url +::: +## `source_url` + +The URL of the hosted git repository for PyPI metadata. + +Example: `https://github.com/salt-extensions/saltext-vault` + +:::{question} tracker_url +::: +## `tracker_url` + +The URL of the issue tracker for PyPI metadata. + +Example: `https://github.com/salt-extensions/saltext-vault/issues` + +:::{question} package_name +::: +## `package_name` + +The name of the Python package, used for importing it. The final name depends on {question}`no_saltext_namespace`. + +Example: `vault` + +:::{important} +Do not include the `saltext.` namespace here. +::: + +:::{question} license +::: +## `license` + +Choose a license. Any other license than Apache 2.0 needs to be managed manually. + +:::{question} license_classifier +::: +## `license_classifier` + +The license classifier for PyPI metadata. + +Example: `License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)` + +:::{note} +Only asked when a {question}`license` other than Apache is selected. +::: + +:::{question} loaders +::: +## `loaders` + +Choose which Salt module types this extension should provide. + +:::{question} salt_version +::: +## `salt_version` + +The minimum Salt version to support. Influences package dependencies and versions that are tested against. + +:::{question} python_requires +::: +## `python_requires` + +The minimum Python version to support. This also influences pre-commit autoformatting hooks. + +:::{question} max_salt_version +::: +## `max_salt_version` + +The maximum Salt version to support. + +:::{hint} +This only has meaning when non-centralized (non-org) {question}`workflows` are used, where it influences the Salt versions tests are run against. +::: + +:::{question} no_saltext_namespace +::: +## `no_saltext_namespace` + +Whether to use the `saltext.` namespace for the Python package. + +:::{question} ssh_fixtures +::: +## `ssh_fixtures` + +Include test fixtures for Salt-SSH tests (`salt_ssh_cli` etc.). + +:::{note} +Not asked when `wrapper` {question}`modules ` are included, where it defaults to true. +::: + +:::{question} test_containers +::: +## `test_containers` + +Add support for running containers in the test suite (for functional and integration tests). + +:::{question} workflows +::: +## `workflows` + +Choose a GitHub action workflow style to use for the project. The following ones are available: + +org +: The `salt-extensions` GitHub organization provides a repository with reusable workflows. + + Workflows will: + + * check `pre-commit` status + * run the test suite on different OS and with different Salt versions + * render and publish documentation + * build and publish releases to PyPI + +enhanced +: Mostly equivalent to the centralized workflows of the organization, but part of the individual extension repository. + +basic +: Only provided for compatibility with the deprecated create-salt-extension tool. Should not be used in new projects. + +:::{note} +Not asked when {question}`source_url` is not on GitHub. +::: + +:::{question} deploy_docs +::: +## `deploy_docs` + +Choose whether and when the rendered documentation is published to GitHub pages. + +never +: Don't publish documentation to GitHub pages at all. + +release +: Publish documentation only when a release is tagged. + This ensures documentation is in sync with the released functionality. + +rolling +: Publish documentation after `push` and `tag` events to the default branch. + This ensures upcoming unreleased changes are displayed in the changelog. + +:::{important} +Publishing to your GitHub pages site requires the GitHub Actions workflow to be allowed to do so. In your repository `Settings`, ensure +`Pages` > `Build and deployment source` is set to `GitHub Actions`. +::: + +:::{note} +Not asked when {qubestion}`source_url` is not on GitHub or the `basic` {question}`workflows` have been selected. +::: + +:::{note} +The current workflows do not support versioned documentation. +::: + +:::{question} docs_url +::: +## `docs_url` + +The URL of the hosted rendered documentation. + +When publishing docs to GitHub pages via {question}`deploy_docs`, it's usually the corresponding pages URL. + +Example: `https://salt-extensions.github.io/saltext-vault/` + +:::{question} coc_contact +::: +## `coc_contact` + +A contact email address for Code of Conduct complaints. + +Example: `foo@b.ar` + +:::{question} copyright_begin +::: +## `copyright_begin` + +The beginning year of the copyright range. + +Example: `2024` diff --git a/docs/sitevars.rst b/docs/sitevars.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/topics/building.md b/docs/topics/building.md new file mode 100644 index 0000000..bcf7906 --- /dev/null +++ b/docs/topics/building.md @@ -0,0 +1,17 @@ +# Building your Saltext + +## Prerequisites + +In order to build your Salt extension, ensure your current Python has the `build` package installed: + +```bash +python -m pip install build +``` + +## Building + +Building your Salt extension is easy: + +```bash +python -m build --outdir dist/ +``` diff --git a/docs/topics/creation.md b/docs/topics/creation.md new file mode 100644 index 0000000..dd7dc63 --- /dev/null +++ b/docs/topics/creation.md @@ -0,0 +1,27 @@ +# Creation + +With Copier, creating a Salt extension project structure is easy: + +```bash +copier copy --trust https://github.com/salt-extensions/salt-extension-copier my-awesome-new-saltext +``` + +You are then asked a set of questions that will shape the final project structure. They will be remembered for future updates in `.copier-answers.yml`. + +:::{important} + +Copier needs to be invoked with the `--trust` flag in order to enable +custom Jinja extensions required for template rendering (and, when updating, migrations). +Effectively, this runs unsandboxed commands defined in the template on your host, +so ensure you trust it! + +* [Jinja extensions][jinja-exts] +* [tasks and migrations][tasks-migrations] +::: + +## Important considerations +TODO +### Organization or individual + +[jinja-exts]: https://github.com/salt-extensions/salt-extension-copier/blob/main/jinja_extensions/saltext.py +[tasks-migrations]: https://github.com/salt-extensions/salt-extension-copier/blob/main/copier.yml diff --git a/docs/topics/extraction.md b/docs/topics/extraction.md new file mode 100644 index 0000000..e9f18ad --- /dev/null +++ b/docs/topics/extraction.md @@ -0,0 +1,2 @@ +# Extraction of Salt core modules +TODO - ref https://github.com/salt-extensions/extension_migration diff --git a/docs/topics/installation.md b/docs/topics/installation.md new file mode 100644 index 0000000..316fb4b --- /dev/null +++ b/docs/topics/installation.md @@ -0,0 +1,33 @@ +# Installation + +You only need a functional [Copier][copier-docs] installation to render the template. + +## Copier + +:::{tab} pipx + +It is generally recommended to install it globally via [pipx][pipx-docs]: + +```bash +pipx install 'copier>=9.1' && \ + pipx inject copier copier-templates-extensions +``` +::: + +:::{tab} pip + +If you want to use `pip` instead, run the following, preferably inside a virtual environment: + +```bash +python -m pip install copier copier-templates-extensions +``` +::: + +:::{important} +Since this template provides some Jinja extensions, you need to ensure [copier-templates-extensions][copier-templates-extensions] is present in the `copier` virtual environment. The example installation commands above account for this. +::: + +[copier-docs]: https://copier.readthedocs.io/en/stable/ +[copier-multiselect-pr]: https://github.com/copier-org/copier/pull/1386 +[copier-templates-extensions]: https://github.com/copier-org/copier-templates-extensions +[pipx-docs]: https://pipx.pypa.io/stable/ diff --git a/docs/topics/organization.md b/docs/topics/organization.md new file mode 100644 index 0000000..bf6eafc --- /dev/null +++ b/docs/topics/organization.md @@ -0,0 +1,2 @@ +# The GitHub organization +TODO describe the org and how to get involved/publish an extension there diff --git a/docs/topics/publishing.md b/docs/topics/publishing.md new file mode 100644 index 0000000..38a9d91 --- /dev/null +++ b/docs/topics/publishing.md @@ -0,0 +1,3 @@ +# Publishing your Saltext + +TODO describe release workflow diff --git a/docs/topics/testing.md b/docs/topics/testing.md new file mode 100644 index 0000000..e08240a --- /dev/null +++ b/docs/topics/testing.md @@ -0,0 +1,3 @@ +# Testing your Saltext + +TODO describe basics of how to test with `pytest-salt-factories` diff --git a/noxfile.py b/noxfile.py index 1682014..2629b8b 100755 --- a/noxfile.py +++ b/noxfile.py @@ -1,5 +1,6 @@ # pylint: disable=missing-module-docstring,import-error,protected-access,missing-function-docstring import datetime +import json import os import pathlib import shutil @@ -44,6 +45,19 @@ DEV_REQUIREMENTS = ("pylint",) +DOCS_REQUIREMENTS = ( + "sphinx", + "sphinxcontrib-spelling", + "sphinx-copybutton", + "myst_parser", + "furo", + "sphinx-inline-tabs", + "towncrier==22.12.0", + "sphinxcontrib-towncrier", +) + +DOCSAUTO_REQUIREMENTS = ("sphinx-autobuild",) + TESTS_REQUIREMENTS = ( "copier>=9.1", "copier-templates-extensions", @@ -258,3 +272,100 @@ def lint_tests_pre_commit(session): else: paths = ["tests/"] _lint_pre_commit(session, ".pylintrc", flags, paths) + + +@nox.session +def docs(session): + """ + Build Docs + """ + _install_requirements( + session, + *DOCS_REQUIREMENTS, + ) + os.chdir("docs/") + session.run("make", "clean", external=True) + session.run("make", "linkcheck", "SPHINXOPTS=-W", external=True) + session.run("make", "html", "SPHINXOPTS=-W", external=True) + os.chdir(str(REPO_ROOT)) + + +@nox.session(name="docs-html") +@nox.parametrize("clean", [False, True]) +@nox.parametrize("include_api_docs", [False, True]) +def docs_html(session, clean, include_api_docs): + """ + Build Sphinx HTML Documentation + + TODO: Add option for `make linkcheck` and `make coverage` + calls via Sphinx. Ran into problems with two when + using Furo theme and latest Sphinx. + """ + _install_requirements( + session, + *DOCS_REQUIREMENTS, + ) + build_dir = pathlib.Path("docs", "_build", "html") + sphinxopts = "-Wn" + if clean: + sphinxopts += "E" + args = [sphinxopts, "--keep-going", "docs", str(build_dir)] + session.run("sphinx-build", *args, external=True) + + +@nox.session(name="docs-dev") +@nox.parametrize("clean", [False, True]) +def docs_dev(session, clean) -> None: + """ + Build and serve the Sphinx HTML documentation, with live reloading on file changes, via sphinx-autobuild. + + Note: Only use this in INTERACTIVE DEVELOPMENT MODE. This SHOULD NOT be called + in CI/CD pipelines, as it will hang. + """ + _install_requirements( + session, + *DOCS_REQUIREMENTS, + *DOCSAUTO_REQUIREMENTS, + ) + + # Launching LIVE reloading Sphinx session + build_dir = pathlib.Path("docs", "_build", "html") + args = ["--watch", ".", "--open-browser", "docs", str(build_dir)] + if clean and build_dir.exists(): + shutil.rmtree(build_dir) + + session.run("sphinx-autobuild", *args) + + +@nox.session(name="docs-crosslink-info") +def docs_crosslink_info(session): + """ + Report intersphinx cross links information + """ + _install_requirements( + session, + install_extras=["docs"], + ) + os.chdir("docs/") + intersphinx_mapping = json.loads( + session.run( + "python", + "-c", + "import json; import conf; print(json.dumps(conf.intersphinx_mapping))", + silent=True, + log=False, + ) + ) + intersphinx_mapping_list = ", ".join(list(intersphinx_mapping)) + try: + mapping_entry = intersphinx_mapping[session.posargs[0]] + except IndexError: + session.error( + f"You need to pass at least one argument whose value must be one of: {intersphinx_mapping_list}" + ) + except KeyError: + session.error(f"Only acceptable values for first argument are: {intersphinx_mapping_list}") + session.run( + "python", "-m", "sphinx.ext.intersphinx", mapping_entry[0].rstrip("/") + "/objects.inv" + ) + os.chdir(str(REPO_ROOT)) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3918e42 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,50 @@ +[tool.build_sphinx] +source_dir = "docs" +build_dir = "build/sphinx" + +[tool.black] +line-length = 100 + +[tool.isort] +force_single_line = true +profile = "black" +line_length = 100 + +[tool.towncrier] +filename = "CHANGELOG.md" +template = "changelog/.template.jinja" +directory = "changelog/" +start_string = "# Changelog\n" +underlines = ["", "", ""] +title_format = "## {version} ({project_date})" +issue_format = "[#{issue}](https://github.com/salt-extensions/salt-extension/copier/issues/{issue})" + +[[tool.towncrier.type]] +directory = "removed" +name = "Removed" +showcontent = true + +[[tool.towncrier.type]] +directory = "deprecated" +name = "Deprecated" +showcontent = true + +[[tool.towncrier.type]] +directory = "changed" +name = "Changed" +showcontent = true + +[[tool.towncrier.type]] +directory = "fixed" +name = "Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "added" +name = "Added" +showcontent = true + +[[tool.towncrier.type]] +directory = "security" +name = "Security" +showcontent = true