Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ python run.py build
python run.py build-docs
```

> ***Note: When releasing a new version, update ``switcher.json`` in ``docs/source/_static/``
to include the new tag in the version dropdown for documentation.***

Options:
- `--skip-build` (`-s`): Skip building before generating docs

Expand Down Expand Up @@ -159,6 +162,7 @@ We welcome contributions! Please see our [Contributing Guide](https://github.com

We use [Semantic Versioning](https://semver.org/). For available versions, see the [tags on this repository](https://github.com/Autodesk/moldflow-api/tags).


## License

This project is licensed under the Apache License 2.0 - see the [LICENSE](https://github.com/Autodesk/moldflow-api/blob/main/LICENSE) file for details.
Expand Down
38 changes: 38 additions & 0 deletions docs/source/_static/switcher.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"version": "v26.0.5",
"name": "v26.0.5 (latest)",
"url": "../v26.0.5/",
"is_latest": true
},
{
"version": "v26.0.4",
"name": "v26.0.4",
"url": "../v26.0.4/",
"is_latest": false
},
{
"version": "v26.0.3",
"name": "v26.0.3",
"url": "../v26.0.3/",
"is_latest": false
},
{
"version": "v26.0.2",
"name": "v26.0.2",
"url": "../v26.0.2/",
"is_latest": false
},
{
"version": "v26.0.1",
"name": "v26.0.1",
"url": "../v26.0.1/",
"is_latest": false
},
{
"version": "v26.0.0",
"name": "v26.0.0",
"url": "../v26.0.0/",
"is_latest": false
}
]
43 changes: 42 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import os
import sys
from pathlib import Path

sys.path.insert(
0, os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'src', 'moldflow')
Expand All @@ -29,9 +30,15 @@
'sphinx.ext.napoleon', # Supports Google-style/Numpy-style docstrings
'sphinx.ext.viewcode',
'sphinx_autodoc_typehints',
'sphinx_multiversion',
]

templates_path = ['_templates']
Comment thread
sankalps0549 marked this conversation as resolved.
smv_tag_whitelist = r'^v?\d+\.\d+\.\d+$'
Comment thread
sankalps0549 marked this conversation as resolved.
smv_branch_whitelist = r'^$'
smv_remote_whitelist = r'^origin$'
smv_latest_version = 'latest'

exclude_patterns = []

# -- Options for autodoc -----------------------------------------------------
Expand All @@ -53,9 +60,14 @@
html_theme_options = {
"back_to_top_button": False,
"github_url": "https://github.com/Autodesk/moldflow-api",
"external_links": "",
"external_links": [
{"name": "Changelog", "url": "https://github.com/Autodesk/moldflow-api/releases"}
],
"footer_end": "",
"footer_start": "copyright",
"navbar_start": ["navbar-logo", "version-switcher"],
"navbar_end": ["theme-switcher", "navbar-icon-links"],
"switcher": {"json_url": "_static/switcher.json", "version_match": smv_latest_version},
}
html_static_path = ['_static']
html_title = "Moldflow API"
Expand All @@ -72,5 +84,34 @@ def skip_member(app, what, name, obj, skip, options):
return skip


def set_context_switcher(app, pagename, templatename, context, doctree):
"""Set version_match in the switcher to show the correct version label."""

# Try output directory name (e.g., v26.0.2) - most reliable for sphinx-multiversion
version_name = None
if hasattr(app, "builder") and hasattr(app.builder, "outdir"):
outdir_name = Path(app.builder.outdir).name
if outdir_name and outdir_name != "html":
version_name = outdir_name

# Fallback to sphinx-multiversion context
if not version_name:
current = context.get("current_version")
if current:
version_name = getattr(current, "name", None) or (
current.get("name") if isinstance(current, dict) else None
)

# Final fallback
if not version_name:
version_name = smv_latest_version

# Update switcher config for this page
switcher = dict(app.config.html_theme_options.get("switcher", {}))
switcher["version_match"] = version_name
context["theme_switcher"] = switcher


def setup(app):
app.connect("autodoc-skip-member", skip_member)
app.connect("html-page-context", set_context_switcher)
7 changes: 6 additions & 1 deletion docs/source/readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,12 @@ The project includes a ``run.py`` script with several useful commands:
- ``python run.py test`` - Run tests
- ``python run.py lint`` - Run code linting
- ``python run.py format`` - Format code with black
- ``python run.py build-docs`` - Build documentation
- ``python run.py build-docs`` - Build versioned documentation (HTML uses git tags for the
navigation dropdown; run ``git fetch --tags`` locally before building)

.. note::
When releasing a new version, update ``switcher.json`` in ``docs/source/_static/``
to include the new tag in the version dropdown.
Comment thread
sankalps0549 marked this conversation as resolved.

Contributing
============
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pydata-sphinx-theme==0.16.1
pylint==3.3.4
pytest==8.3.4
sphinx==8.1.3
sphinx-multiversion==0.2.4
sphinx-autodoc-typehints==3.0.1
twine==6.1.0
PyGithub==2.7.0
101 changes: 87 additions & 14 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@
import shutil
import glob
from urllib.parse import urlparse

import docopt
from github import Github
import polib
from packaging.version import InvalidVersion, Version
Comment thread
sankalps0549 marked this conversation as resolved.


WINDOWS = platform.system() == 'Windows'
Expand All @@ -86,7 +86,9 @@
LOCALE_DIR = os.path.join(MOLDFLOW_DIR, 'locale')
DOCS_DIR = os.path.join(ROOT_DIR, 'docs')
DOCS_SOURCE_DIR = os.path.join(DOCS_DIR, 'source')
DOCS_STATIC_DIR = os.path.join(DOCS_SOURCE_DIR, '_static')
DOCS_BUILD_DIR = os.path.join(DOCS_DIR, 'build')
DOCS_HTML_DIR = os.path.join(DOCS_BUILD_DIR, 'html')
COVERAGE_HTML_DIR = os.path.join(ROOT_DIR, 'htmlcov')
DIST_DIR = os.path.join(ROOT_DIR, 'dist')

Expand All @@ -100,6 +102,7 @@
VERSION_FILE = os.path.join(ROOT_DIR, VERSION_JSON)
DIST_FILES = os.path.join(ROOT_DIR, 'dist', '*')
PYTHON_FILES = [MOLDFLOW_DIR, DOCS_SOURCE_DIR, TEST_DIR, "run.py"]
SWITCHER_JSON = os.path.join(DOCS_STATIC_DIR, 'switcher.json')


def run_command(args, cwd=os.getcwd(), extra_env=None):
Expand Down Expand Up @@ -325,6 +328,51 @@ def build_mo():
)


def create_latest_alias(build_output: str) -> None:
Comment thread
sankalps0549 marked this conversation as resolved.
"""Create a 'latest' alias pointing to the newest version using symlinks when possible."""
version_dirs = [d for d in os.listdir(build_output) if d.startswith('v')]
if not version_dirs:
return

def version_key(v):
try:
return Version(v.lstrip('v'))
Comment thread
sankalps0549 marked this conversation as resolved.
except InvalidVersion:
return Version("0.0.0")

sorted_versions = sorted(version_dirs, key=version_key, reverse=True)
latest_version = sorted_versions[0]
latest_src = os.path.join(build_output, latest_version)
latest_dest = os.path.join(build_output, 'latest')

# Verify source exists before proceeding
if not os.path.exists(latest_src):
logging.error("Source directory for 'latest' alias does not exist: %s", latest_src)
return

# Clean up any existing 'latest' entry first
if os.path.islink(latest_dest):
os.unlink(latest_dest)
elif os.path.isdir(latest_dest):
shutil.rmtree(latest_dest)
elif os.path.exists(latest_dest):
os.remove(latest_dest)

# Try creating a symbolic link first (most efficient)
logging.info("Creating 'latest' alias for %s", latest_version)
try:
os.symlink(latest_src, latest_dest, target_is_directory=True)
Comment thread
sankalps0549 marked this conversation as resolved.
Comment thread
sankalps0549 marked this conversation as resolved.
logging.info("Created symbolic link: latest -> %s", latest_version)
except (OSError, NotImplementedError) as err:
# Fall back to copying if symlinks aren't supported
logging.warning(
"Could not create symbolic link for 'latest' alias (%s); "
"falling back to copying documentation.",
err,
)
shutil.copytree(latest_src, latest_dest)


def build_docs(target, skip_build):
"""Build Documentation"""

Expand All @@ -338,19 +386,44 @@ def build_docs(target, skip_build):
shutil.rmtree(DOCS_BUILD_DIR)

try:
run_command(
[
sys.executable,
'-m',
'sphinx',
'build',
'-M',
target,
DOCS_SOURCE_DIR,
DOCS_BUILD_DIR,
],
ROOT_DIR,
)
if target == 'html':
build_output = os.path.join(DOCS_BUILD_DIR, 'html')
try:
# fmt: off
run_command(
[
sys.executable, '-m', 'sphinx_multiversion',
DOCS_SOURCE_DIR, build_output
],
ROOT_DIR,
)
except Exception as err:
logging.error(
"Failed to build documentation with sphinx_multiversion.\n"
"This can happen if no Git tags or branches match your version pattern.\n"
"Try running 'git fetch --tags' and ensure version tags exist in the repo.\n"
"Underlying error: %s",
str(err),
)
# Re-raise so the outer handler can log the general failure as well.
raise
# fmt: on
create_latest_alias(build_output)
else:
# For other targets such as latex, pdf, etc.
Comment thread
sankalps0549 marked this conversation as resolved.
run_command(
[
sys.executable,
'-m',
'sphinx',
'build',
'-M',
target,
DOCS_SOURCE_DIR,
DOCS_BUILD_DIR,
],
ROOT_DIR,
)
logging.info('Sphinx documentation built successfully.')
except Exception as err:
logging.error(
Expand Down