diff --git a/.copier-answers.yml b/.copier-answers.yml index 2abd127..fe5c973 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 2.6.0 +_commit: 4.0.1 _src_path: gh:DiamondLightSource/python-copier-template author_email: martin.gaughran@diamond.ac.uk author_name: Martin Gaughran @@ -9,10 +9,11 @@ component_type: library description: A set of tools used for deploying applications to a shared filesystem. distribution_name: dls-deploy-tools docker: true -docs_type: README +docs_type: sphinx git_platform: github.com github_org: DiamondLightSource package_name: deploy_tools pypi: true repo_name: deploy-tools +strict_typing: true type_checker: pyright diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3868e12..d9a5c04 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects. -For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.6.0/how-to.html). +For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/4.0.1/how-to.html). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index aa65892..92087f5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,7 +7,7 @@ assignees: '' --- -Describe the bug, including a clear and concise description of the expected behavior, the actual behavior and the context in which you encountered it (ideally include details of your environment). +Describe the bug, including a clear and concise description of the expected behaviour, the actual behavior and the context in which you encountered it (ideally include details of your environment). ## Steps To Reproduce Steps to reproduce the behavior: diff --git a/.github/actions/install_requirements/action.yml b/.github/actions/install_requirements/action.yml index d33e080..7a7bda0 100644 --- a/.github/actions/install_requirements/action.yml +++ b/.github/actions/install_requirements/action.yml @@ -15,7 +15,8 @@ runs: run: | PYTHON_VERSION="${{ inputs.python-version }}" if [ $PYTHON_VERSION == "dev" ]; then - PYTHON_VERSION=$(sed -n "s/ARG PYTHON_VERSION=//p" Dockerfile) + # python version from Dockerfile, removing potential pinned sha + PYTHON_VERSION=$(sed -Ene "s/ARG PYTHON_VERSION=([0-9\.]+).*/\1/p" Dockerfile) fi echo "PYTHON_VERSION=$PYTHON_VERSION" >> "$GITHUB_ENV" shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 184ba36..d2c2a0d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,8 @@ updates: actions: patterns: - "*" + commit-message: + prefix: "chore" - package-ecosystem: "pip" directory: "/" @@ -22,3 +24,5 @@ updates: dev-dependencies: patterns: - "*" + commit-message: + prefix: "chore" diff --git a/.github/workflows/_check.yml b/.github/workflows/_check.yml deleted file mode 100644 index a6139c1..0000000 --- a/.github/workflows/_check.yml +++ /dev/null @@ -1,27 +0,0 @@ -on: - workflow_call: - outputs: - branch-pr: - description: The PR number if the branch is in one - value: ${{ jobs.pr.outputs.branch-pr }} - -jobs: - pr: - runs-on: "ubuntu-latest" - outputs: - branch-pr: ${{ steps.script.outputs.result }} - steps: - - uses: actions/github-script@v7 - id: script - if: github.event_name == 'push' - with: - script: | - const prs = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - head: context.repo.owner + ':${{ github.ref_name }}' - }) - if (prs.data.length) { - console.log(`::notice ::Skipping CI on branch push as it is already run in PR #${prs.data[0]["number"]}`) - return prs.data[0]["number"] - } diff --git a/.github/workflows/_container.yml b/.github/workflows/_container.yml index da5e493..c6cd469 100644 --- a/.github/workflows/_container.yml +++ b/.github/workflows/_container.yml @@ -1,5 +1,9 @@ on: workflow_call: + inputs: + publish: + type: boolean + description: If true, pushes image to container registry jobs: build: @@ -47,7 +51,7 @@ jobs: type=raw,value=latest - name: Push cached image to container registry - if: github.ref_type == 'tag' + if: inputs.publish && github.ref_type == 'tag' uses: docker/build-push-action@v6 env: DOCKER_BUILD_RECORD_UPLOAD: false diff --git a/.github/workflows/_docs.yml b/.github/workflows/_docs.yml new file mode 100644 index 0000000..1f5491b --- /dev/null +++ b/.github/workflows/_docs.yml @@ -0,0 +1,55 @@ +on: + workflow_call: + + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Avoid git conflicts when tag and branch pushed at same time + if: github.ref_type == 'tag' + run: sleep 60 + + - name: Checkout + uses: actions/checkout@v4 + with: + # Need this to get version number from last tag + fetch-depth: 0 + + - name: Install system packages + run: sudo apt-get install graphviz + + - name: Install python packages + uses: ./.github/actions/install_requirements + + - name: Build docs + run: tox -e docs + + - name: Remove environment.pickle + run: rm build/html/.doctrees/environment.pickle + + - name: Upload built docs artifact + uses: actions/upload-artifact@v4 + with: + name: docs + path: build + + - name: Sanitize ref name for docs version + run: echo "DOCS_VERSION=${GITHUB_REF_NAME//[^A-Za-z0-9._-]/_}" >> $GITHUB_ENV + + - name: Move to versioned directory + run: mv build/html .github/pages/$DOCS_VERSION + + - name: Write switcher.json + run: python .github/pages/make_switcher.py --add $DOCS_VERSION ${{ github.repository }} .github/pages/switcher.json + + - name: Publish Docs to gh-pages + if: github.ref_type == 'tag' || github.ref_name == 'main' + # We pin to the SHA, not the tag, for security reasons. + # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: .github/pages + keep_files: true diff --git a/.github/workflows/_release.yml b/.github/workflows/_release.yml index 81b6264..3f949fc 100644 --- a/.github/workflows/_release.yml +++ b/.github/workflows/_release.yml @@ -23,7 +23,7 @@ jobs: - name: Create GitHub Release # We pin to the SHA, not the tag, for security reasons. # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions - uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9 + uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }} files: "*" diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index b8e4ba6..e6c5060 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -60,7 +60,7 @@ jobs: tox -e tests - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: name: ${{ inputs.python-version }}/${{ inputs.runs-on }} files: cov.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 755e68d..cbc954c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,26 +2,24 @@ name: CI on: push: + branches: + - main + tags: + - '*' pull_request: jobs: - check: - uses: ./.github/workflows/_check.yml lint: - needs: check - if: needs.check.outputs.branch-pr == '' uses: ./.github/workflows/_tox.yml with: tox: pre-commit,type-checking test: - needs: check - if: needs.check.outputs.branch-pr == '' strategy: matrix: runs-on: ["ubuntu-latest"] # can add windows-latest, macos-latest - python-version: ["3.12"] + python-version: ["3.12", "3.13"] include: # Include one that runs in the dev environment - runs-on: "ubuntu-latest" @@ -35,28 +33,32 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} container: - needs: check - if: needs.check.outputs.branch-pr == '' + needs: test + if: always() uses: ./.github/workflows/_container.yml + with: + publish: ${{ needs.test.result == 'success' }} permissions: contents: read packages: write + docs: + uses: ./.github/workflows/_docs.yml + + dist: - needs: check - if: needs.check.outputs.branch-pr == '' uses: ./.github/workflows/_dist.yml pypi: + needs: [dist, test] if: github.ref_type == 'tag' - needs: dist uses: ./.github/workflows/_pypi.yml permissions: id-token: write release: + needs: [dist, test, docs] if: github.ref_type == 'tag' - needs: [dist] uses: ./.github/workflows/_release.yml permissions: contents: write diff --git a/.github/workflows/periodic.yml b/.github/workflows/periodic.yml new file mode 100644 index 0000000..e2a0fd1 --- /dev/null +++ b/.github/workflows/periodic.yml @@ -0,0 +1,13 @@ +name: Periodic + +on: + workflow_dispatch: + schedule: + # Run weekly to check URL links still resolve + - cron: "0 8 * * WED" + +jobs: + linkcheck: + uses: ./.github/workflows/_tox.yml + with: + tox: docs build -- -b linkcheck diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51e5ee2..ef68f8a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: ruff name: lint with ruff language: system - entry: ruff check --force-exclude + entry: ruff check --force-exclude --fix types: [python] require_serial: true diff --git a/.vscode/launch.json b/.vscode/launch.json index 2f5dabb..afbaacd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,10 +14,6 @@ "debug-test" ], "console": "integratedTerminal", - "env": { - // Enable break on exception when debugging tests (see: tests/conftest.py) - "PYTEST_RAISE": "1", - }, }, { "name": "Debug Schema Generation", @@ -27,10 +23,6 @@ "program": "/venv/bin/deploy-tools", "args": "schema ${input:schema-folder}", "console": "integratedTerminal", - "env": { - // Enable break on exception when debugging tests (see: tests/conftest.py) - "PYTEST_RAISE": "1", - }, }, { "name": "Debug Validation", @@ -40,10 +32,6 @@ "program": "/venv/bin/deploy-tools", "args": "validate ${input:allow-all} ${input:from-scratch} ${input:test-build} ${input:deploy-folder} ${input:config-folder}", "console": "integratedTerminal", - "env": { - // Enable break on exception when debugging tests (see: tests/conftest.py) - "PYTEST_RAISE": "1", - }, }, { "name": "Debug Compare", @@ -53,10 +41,6 @@ "program": "/venv/bin/deploy-tools", "args": "compare ${input:compare-mode} ${input:deploy-folder}", "console": "integratedTerminal", - "env": { - // Enable break on exception when debugging tests (see: tests/conftest.py) - "PYTEST_RAISE": "1", - }, }, { "name": "Debug Sync", @@ -66,10 +50,6 @@ "program": "/venv/bin/deploy-tools", "args": "sync ${input:allow-all} ${input:from-scratch} ${input:deploy-folder} ${input:config-folder}", "console": "integratedTerminal", - "env": { - // Enable break on exception when debugging tests (see: tests/conftest.py) - "PYTEST_RAISE": "1", - }, }, ], "inputs": [ diff --git a/README.md b/README.md index b5fca85..3682d54 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Source | :---: | :---: PyPI | `pip install dls-deploy-tools` Docker | `docker run ghcr.io/diamondlightsource/deploy-tools:latest` +Documentation | Releases | The demo_configuration folder in this repository can be passed as the config_folder to @@ -109,3 +110,7 @@ See the Deployment Steps above for an overview of the primary stages of a deploy |Deprecate |Moving a modulefile to the separate Deprecated Folder, to indicate that its use should be discouraged | |Deprecated Folder |The folder used to contain Modulefiles for Modules that have been deprecated. By adding the modulefiles subdirectory to your MODULEPATH environment variable, you then have the ability to use any deprecated Module as normal. | |End User |Refers to anybody who is intending to make use of a deployed Module. This can include the people modifying configuration themselves | + + + +See https://diamondlightsource.github.io/deploy-tools for more detailed documentation. diff --git a/docs/_api.rst b/docs/_api.rst new file mode 100644 index 0000000..f24e808 --- /dev/null +++ b/docs/_api.rst @@ -0,0 +1,16 @@ +:orphan: + +.. + This page is not included in the TOC tree, but must exist so that the + autosummary pages are generated for deploy_tools and all its + subpackages + +API +=== + +.. autosummary:: + :toctree: _api + :template: custom-module-template.rst + :recursive: + + deploy_tools diff --git a/docs/_templates/custom-module-template.rst b/docs/_templates/custom-module-template.rst new file mode 100644 index 0000000..9aeca54 --- /dev/null +++ b/docs/_templates/custom-module-template.rst @@ -0,0 +1,37 @@ +{{ ('``' + fullname + '``') | underline }} + +{%- set filtered_members = [] %} +{%- for item in members %} + {%- if item in functions + classes + exceptions + attributes %} + {% set _ = filtered_members.append(item) %} + {%- endif %} +{%- endfor %} + +.. automodule:: {{ fullname }} + :members: + + {% block modules %} + {% if modules %} + .. rubric:: Submodules + + .. autosummary:: + :toctree: + :template: custom-module-template.rst + :recursive: + {% for item in modules %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block members %} + {% if filtered_members %} + .. rubric:: Members + + .. autosummary:: + :nosignatures: + {% for item in filtered_members %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..35c0d03 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,191 @@ +"""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 +""" + +import sys +from pathlib import Path +from subprocess import check_output + +import requests + +import deploy_tools + +# -- General configuration ------------------------------------------------ + +# General information about the project. +project = "deploy-tools" + +# The full version, including alpha/beta/rc tags. +release = deploy_tools.__version__ + +# The short X.Y version. +if "+" in release: + # Not on a tag, use branch name + root = Path(__file__).absolute().parent.parent + git_branch = check_output("git branch --show-current".split(), cwd=root) + version = git_branch.decode().strip() +else: + version = release + +extensions = [ + # Use this for generating API docs + "sphinx.ext.autodoc", + # and making summary tables at the top of API docs + "sphinx.ext.autosummary", + # This can parse google style docstrings + "sphinx.ext.napoleon", + # For linking to external sphinx documentation + "sphinx.ext.intersphinx", + # Add links to source code in API docs + "sphinx.ext.viewcode", + # Adds the inheritance-diagram generation directive + "sphinx.ext.inheritance_diagram", + # Add a copy button to each code block + "sphinx_copybutton", + # For the card element + "sphinx_design", + # So we can write markdown files + "myst_parser", +] + +# So we can use the ::: syntax +myst_enable_extensions = ["colon_fence"] + +# If true, Sphinx will warn about all references where the target cannot +# be found. +nitpicky = True + +# A list of (type, target) tuples (by default empty) that should be ignored when +# generating warnings in "nitpicky mode". Note that type should include the +# domain name if present. Example entries would be ('py:func', 'int') or +# ('envvar', 'LD_LIBRARY_PATH'). +nitpick_ignore = [ + ("py:class", "NoneType"), + ("py:class", "'str'"), + ("py:class", "'float'"), + ("py:class", "'int'"), + ("py:class", "'bool'"), + ("py:class", "'object'"), + ("py:class", "'id'"), + ("py:class", "typing_extensions.Literal"), +] + +# Both the class’ and the __init__ method’s docstring are concatenated and +# inserted into the main body of the autoclass directive +autoclass_content = "both" + +# Order the members by the order they appear in the source code +autodoc_member_order = "bysource" + +# Don't inherit docstrings from baseclasses +autodoc_inherit_docstrings = False + +# Document only what is in __all__ +autosummary_ignore_module_all = False + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# Output graphviz directive produced images in a scalable format +graphviz_output_format = "svg" + +# The name of a reST role (builtin or Sphinx extension) to use as the default +# role, that is, for text marked up `like this` +default_role = "any" + +# The master toctree document. +master_doc = "index" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# These patterns also affect html_static_path and html_extra_path +exclude_patterns = ["_build"] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# This means you can link things like `str` and `asyncio` to the relevant +# docs in the python documentation. +intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} + +# A dictionary of graphviz graph attributes for inheritance diagrams. +inheritance_graph_attrs = {"rankdir": "TB"} + +# Ignore localhost links for periodic check that links in docs are valid +linkcheck_ignore = [r"http://localhost:\d+/"] + +# Set copy-button to ignore python and bash prompts +# https://sphinx-copybutton.readthedocs.io/en/latest/use.html#using-regexp-prompt-identifiers +copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " +copybutton_prompt_is_regexp = True + +# -- 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 = "pydata_sphinx_theme" +github_repo = "deploy-tools" +github_user = "DiamondLightSource" +switcher_json = f"https://{github_user}.github.io/{github_repo}/switcher.json" +switcher_exists = requests.get(switcher_json).ok +if not switcher_exists: + print( + "*** Can't read version switcher, is GitHub pages enabled? \n" + " Once Docs CI job has successfully run once, set the " + "Github pages source branch to be 'gh-pages' at:\n" + f" https://github.com/{github_user}/{github_repo}/settings/pages", + file=sys.stderr, + ) + +# Theme options for pydata_sphinx_theme +# We don't check switcher because there are 3 possible states for a repo: +# 1. New project, docs are not published so there is no switcher +# 2. Existing project with latest copier template, switcher exists and works +# 3. Existing project with old copier template that makes broken switcher, +# switcher exists but is broken +# Point 3 makes checking switcher difficult, because the updated copier template +# will fix the switcher at the end of the docs workflow, but never gets a chance +# to complete as the docs build warns and fails. +html_theme_options = { + "logo": { + "text": project, + }, + "use_edit_page_button": True, + "github_url": f"https://github.com/{github_user}/{github_repo}", + "icon_links": [ + { + "name": "PyPI", + "url": f"https://pypi.org/project/{project}", + "icon": "fas fa-cube", + } + ], + "switcher": { + "json_url": switcher_json, + "version_match": version, + }, + "check_switcher": False, + "navbar_end": ["theme-switcher", "icon-links", "version-switcher"], + "navigation_with_keys": False, +} + +# A dictionary of values to pass into the template engine’s context for all pages +html_context = { + "github_user": github_user, + "github_repo": github_repo, + "github_version": version, + "doc_path": "docs", +} + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = False + +# Logo +html_logo = "images/dls-logo.svg" +html_favicon = html_logo diff --git a/docs/explanations.md b/docs/explanations.md new file mode 100644 index 0000000..73ab289 --- /dev/null +++ b/docs/explanations.md @@ -0,0 +1,10 @@ +# Explanations + +Explanations of how it works and why it works that way. + +```{toctree} +:maxdepth: 1 +:glob: + +explanations/* +``` diff --git a/docs/explanations/decisions.md b/docs/explanations/decisions.md new file mode 100644 index 0000000..0533b98 --- /dev/null +++ b/docs/explanations/decisions.md @@ -0,0 +1,12 @@ +# Architectural Decision Records + +Architectural decisions are made throughout a project's lifetime. As a way of keeping track of these decisions, we record these decisions in Architecture Decision Records (ADRs) listed below. + +```{toctree} +:glob: true +:maxdepth: 1 + +decisions/* +``` + +For more information on ADRs see this [blog by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). diff --git a/docs/explanations/decisions/0001-record-architecture-decisions.md b/docs/explanations/decisions/0001-record-architecture-decisions.md new file mode 100644 index 0000000..44d234e --- /dev/null +++ b/docs/explanations/decisions/0001-record-architecture-decisions.md @@ -0,0 +1,18 @@ +# 1. Record architecture decisions + +## Status + +Accepted + +## Context + +We need to record the architectural decisions made on this project. + +## Decision + +We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions). + +## Consequences + +See Michael Nygard's article, linked above. To create new ADRs we will copy and +paste from existing ones. diff --git a/docs/explanations/decisions/0002-switched-to-python-copier-template.md b/docs/explanations/decisions/0002-switched-to-python-copier-template.md new file mode 100644 index 0000000..66fe5d8 --- /dev/null +++ b/docs/explanations/decisions/0002-switched-to-python-copier-template.md @@ -0,0 +1,28 @@ +# 2. Adopt python-copier-template for project structure + +## Status + +Accepted + +## Context + +We should use the following [python-copier-template](https://github.com/DiamondLightSource/python-copier-template). +The template will ensure consistency in developer +environments and package management. + +## Decision + +We have switched to using the template. + +## Consequences + +This module will use a fixed set of tools as developed in `python-copier-template` +and can pull from this template to update the packaging to the latest techniques. + +As such, the developer environment may have changed, the following could be +different: + +- linting +- formatting +- pip venv setup +- CI/CD diff --git a/docs/explanations/decisions/COPYME b/docs/explanations/decisions/COPYME new file mode 100644 index 0000000..b466c79 --- /dev/null +++ b/docs/explanations/decisions/COPYME @@ -0,0 +1,19 @@ +# 3. Short descriptive title + +Date: Today's date + +## Status + +Accepted + +## Context + +Background to allow us to make the decision, to show how we arrived at our conclusions. + +## Decision + +What decision we made. + +## Consequences + +What we will do as a result of this decision. diff --git a/docs/genindex.md b/docs/genindex.md new file mode 100644 index 0000000..73f1191 --- /dev/null +++ b/docs/genindex.md @@ -0,0 +1,3 @@ +# Index + + diff --git a/docs/how-to.md b/docs/how-to.md new file mode 100644 index 0000000..6b16141 --- /dev/null +++ b/docs/how-to.md @@ -0,0 +1,10 @@ +# How-to Guides + +Practical step-by-step guides for the more experienced user. + +```{toctree} +:maxdepth: 1 +:glob: + +how-to/* +``` diff --git a/docs/how-to/contribute.md b/docs/how-to/contribute.md new file mode 100644 index 0000000..6e41979 --- /dev/null +++ b/docs/how-to/contribute.md @@ -0,0 +1,2 @@ +```{include} ../../.github/CONTRIBUTING.md +``` diff --git a/docs/how-to/run-container.md b/docs/how-to/run-container.md new file mode 100644 index 0000000..bb1ec23 --- /dev/null +++ b/docs/how-to/run-container.md @@ -0,0 +1,14 @@ +# Run in a container + +Pre-built containers with deploy-tools and its dependencies already +installed are available on [Github Container Registry](https://ghcr.io/DiamondLightSource/deploy-tools). + +## Starting the container + +To pull the container from github container registry and run: + +``` +$ docker run ghcr.io/diamondlightsource/deploy-tools:latest --version +``` + +To get a released version, use a numbered release instead of `latest`. diff --git a/docs/images/dls-logo.svg b/docs/images/dls-logo.svg new file mode 100644 index 0000000..4fcaa86 --- /dev/null +++ b/docs/images/dls-logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..730b3fd --- /dev/null +++ b/docs/index.md @@ -0,0 +1,56 @@ +--- +html_theme.sidebar_secondary.remove: true +--- + +```{include} ../README.md +:end-before: + +::::{grid} 2 +:gutter: 4 + +:::{grid-item-card} {material-regular}`directions_walk;2em` +```{toctree} +:maxdepth: 2 +tutorials +``` ++++ +Tutorials for installation and typical usage. New users start here. +::: + +:::{grid-item-card} {material-regular}`directions;2em` +```{toctree} +:maxdepth: 2 +how-to +``` ++++ +Practical step-by-step guides for the more experienced user. +::: + +:::{grid-item-card} {material-regular}`info;2em` +```{toctree} +:maxdepth: 2 +explanations +``` ++++ +Explanations of how it works and why it works that way. +::: + +:::{grid-item-card} {material-regular}`menu_book;2em` +```{toctree} +:maxdepth: 2 +reference +``` ++++ +Technical reference material including APIs and release notes. +::: + +:::: diff --git a/docs/reference.md b/docs/reference.md new file mode 100644 index 0000000..80c268c --- /dev/null +++ b/docs/reference.md @@ -0,0 +1,12 @@ +# Reference + +Technical reference material including APIs and release notes. + +```{toctree} +:maxdepth: 1 +:glob: + +API <_api/deploy_tools> +genindex +Release Notes +``` diff --git a/docs/tutorials.md b/docs/tutorials.md new file mode 100644 index 0000000..1fe66c5 --- /dev/null +++ b/docs/tutorials.md @@ -0,0 +1,10 @@ +# Tutorials + +Tutorials for installation and typical usage. New users start here. + +```{toctree} +:maxdepth: 1 +:glob: + +tutorials/* +``` diff --git a/docs/tutorials/installation.md b/docs/tutorials/installation.md new file mode 100644 index 0000000..ab7f8a8 --- /dev/null +++ b/docs/tutorials/installation.md @@ -0,0 +1,42 @@ +# Installation + +## Check your version of python + +You will need python 3.11 or later. You can check your version of python by +typing into a terminal: + +``` +$ python3 --version +``` + +## Create a virtual environment + +It is recommended that you install into a “virtual environment” so this +installation will not interfere with any existing Python software: + +``` +$ python3 -m venv /path/to/venv +$ source /path/to/venv/bin/activate +``` + +## Installing the library + +You can now use `pip` to install the library and its dependencies: + +``` +$ python3 -m pip install dls-deploy-tools +``` + +If you require a feature that is not currently released you can also install +from github: + +``` +$ python3 -m pip install git+https://github.com/DiamondLightSource/deploy-tools.git +``` + +The library should now be installed and the commandline interface on your path. +You can check the version that has been installed by typing: + +``` +$ deploy-tools --version +``` diff --git a/pyproject.toml b/pyproject.toml index c550b35..3f49d2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ classifiers = [ "Development Status :: 3 - Alpha", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] description = "A set of tools used for deploying applications to a shared filesystem." dependencies = [ @@ -25,12 +26,17 @@ requires-python = ">=3.12" [project.optional-dependencies] dev = [ "copier", + "myst-parser", "pipdeptree", "pre-commit", + "pydata-sphinx-theme>=0.12", "pyright", "pytest", "pytest-cov", "ruff", + "sphinx-autobuild", + "sphinx-copybutton", + "sphinx-design", "tox-direct", "types-mock", ] @@ -53,16 +59,15 @@ version_file = "src/deploy_tools/_version.py" deploy_tools = ['templates', 'models/schema/*'] [tool.pyright] -reportMissingImports = false # Ignore missing stubs in imported modules -strict = ["src"] exclude = ["src/deploy_tools/_version.py"] deprecateTypingAliases = true -typeCheckingMode = "standard" +typeCheckingMode = "strict" +reportMissingImports = false # Ignore missing stubs in imported modules [tool.pytest.ini_options] # Run pytest with all our checkers, and don't spam us with massive tracebacks on error addopts = """ - --tb=native -vv + --tb=native -vv --doctest-modules --doctest-glob="*.rst" """ # https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings filterwarnings = "error" @@ -83,7 +88,7 @@ legacy_tox_ini = """ [tox] skipsdist=True -[testenv:{pre-commit,type-checking,tests}] +[testenv:{pre-commit,type-checking,tests,docs}] # Don't create a virtualenv for the command, requires tox-direct plugin direct = True passenv = * @@ -91,10 +96,13 @@ allowlist_externals = pytest pre-commit pyright + sphinx-build + sphinx-autobuild commands = pre-commit: pre-commit run --all-files --show-diff-on-failure {posargs} type-checking: pyright src tests {posargs} tests: pytest --cov=deploy_tools --cov-report term --cov-report xml:cov.xml {posargs} + docs: sphinx-{posargs:build -EW --keep-going} -T docs build/html """ [tool.ruff] diff --git a/tests/conftest.py b/tests/conftest.py index 1571803..77e0c8e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,4 @@ -import os from pathlib import Path -from typing import Any import pytest from typer.testing import CliRunner @@ -10,31 +8,13 @@ runner = CliRunner() -def run_cli(*args): +def run_cli(*args: str | Path): result = runner.invoke(app, [str(x) for x in args]) if result.exception: raise result.exception assert result.exit_code == 0, result -# Prevent pytest from catching exceptions when debugging in vscode so that break on -# exception works correctly (see: https://github.com/pytest-dev/pytest/issues/7409) -if os.getenv("PYTEST_RAISE", "0") == "1": - - @pytest.hookimpl(tryfirst=True) - def pytest_exception_interact(call: pytest.CallInfo[Any]): - if call.excinfo is not None: - raise call.excinfo.value - else: - raise RuntimeError( - f"{call} has no exception data, an unknown error has occurred" - ) - - @pytest.hookimpl(tryfirst=True) - def pytest_internalerror(excinfo: pytest.ExceptionInfo[Any]): - raise excinfo.value - - @pytest.fixture def schemas(): return Path(__file__).parent.parent / "src" / "deploy_tools" / "models" / "schemas"