Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EOL sphinx extension #2251

Closed
wants to merge 6 commits into from
Closed

Conversation

oraNod
Copy link
Contributor

@oraNod oraNod commented Dec 3, 2024

Fixes #1490

This adds a versions.yaml file that declares the active and eol versions of core and package docs. A sphinx extension, docs/docsite/rst/_ext/eol_version.py, uses the core and ansible tags from the sphinx conf.py file to set the build type, grabs the branch name from the git repo, then checks if the branch name is in the eol list. If so then the extension injects the eol_banner into pages during the build.

This PR also separates EOL banner from the docs/docsite/.templates/banner.html file into a new file called docs/docsite/.templates/eol_banner.html.

Tested my fork by adding the branch name to the eol list for package docs and deploying to the test site: https://ansible-community.github.io/package-doc-builds/

image

You can verify the changes locally by adding the branch to the eol or active lists for core docs and running nox -s make.

Final note: I think this might be causing an issue with the main banner. I need to check that out but I do see "You are reading an older version of the Ansible documentation." in the devel version of the docs.

@oraNod oraNod added doc builds Relates to building the documentation tooling This PR affects tooling (CI, pr_labeler, noxfile, linters, etc.) but not the docs builds themselves. labels Dec 4, 2024
@samccann
Copy link
Contributor

Thanks for doing this!

So the extra 'older version' banner is coming from [line 57 in banner.html](https://github.com/ansible/ansible-documentation/pull/2251/files#diff-1d79a9e5a54a083e8cd65610b77d6983e4d6b75ebb4c230b7d13633701bbd063L57]. This is the banner that shows up in PR builds etc.

I can't figure out how to test this locally. I tried adding 'devel' to the core eol list, but since I'm grabbing your PR, I'm guessing the 'branch' isn't devel and thus not working?

{# Creates a banner at the top of the page for EOL versions. #}
<div id="banner" class="Admonition caution">
<p>
You are reading an unmaintained version of the Ansible documentation. Unmaintained Ansible versions can contain unfixed security vulnerabilities (CVE). Please upgrade to a maintained version. See <a href="/ansible/latest/">the latest Ansible documentation</a>.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an existing bug, but I just realized that href will redirect eol core docsite to ansible latest. I don't know of an easy way to fix it so maybe I should just open a separate issue for this? It should be /ansible/latest for package releases, and ansible-core/devel for core releases probably.

banner += '<div id="banner_id" class="admonition ';
banner += important ? 'important' : 'caution';
banner += '">';
banner += '<div id="banner_id" class="admonition '; banner += important ? 'important' : 'caution'; banner += '">';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is an accidental change?

Suggested change
banner += '<div id="banner_id" class="admonition '; banner += important ? 'important' : 'caution'; banner += '">';
banner += '<div id="banner_id" class="admonition ';
banner += important ? 'important' : 'caution';
banner += '">';

banner += important ? '<br>' : '';
banner += msg;
banner += important ? '<br>' : '';
banner += '</div>';
document.write(banner);
{% endif %}
</script>
{% endif %}
</script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just keep it indented to hide from the diff

Suggested change
</script>
</script>

</div>
{% else %}
<script>
<script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<script>
<script>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to have this file. Just reference the extension directly. Re-exporting an ambiguous setup func is going to be confusing. Plus, referencing this entire folder as an extension is not what it exists for. It's already supposed to be injected into sys.path.

@@ -25,6 +25,7 @@
# sys.path.append(os.path.abspath('some/directory'))
#
sys.path.insert(0, os.path.join('ansible', 'lib'))
sys.path.insert(0, os.path.abspath('.'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idiomatic way is to inject the _ext dir so you could have multiple separate extensions loadable. Besides, no need to make things outside this directory importable.
https://github.com/ansible/awx-plugins/blob/0d569b5/docs/conf.py#L34C1-L34C48

Suggested change
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('_ext'))

@@ -65,6 +66,7 @@
'notfound.extension',
'sphinx_antsibull_ext', # provides CSS for the plugin/module docs generated by antsibull
'sphinx_copybutton',
'_ext.eol_version',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to be explicit about which extensions are local: https://github.com/ansible/awx-plugins/blob/0d569b5/docs/conf.py#L81C5-L81C26

Suggested change
'_ext.eol_version',
# In-tree extensions:
'eol_version', # conditionally injects a banner if EOL

@@ -0,0 +1,20 @@
package:
active:
- devel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have a single-space indent? Most people argue about zero vs. two-space... I'm in the no-indent camp FWIW.
https://github.com/ansible/awx-plugins/blob/0d569b5/.yamllint#L6C3-L8C28

@@ -0,0 +1,20 @@
package:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is rather misleading. Both are packages. ansible-core is a distribution package that does not bundle heavy stuff.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this structure should be a mapping of version to EOL date instead. The Python side could then compute if something is EOL based on that…

@@ -227,7 +229,6 @@
html_context = {
'display_github': 'True',
'show_sphinx': False,
'is_eol': False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still in the middle of the review, but it appears to me that a lot of what's happening here is over-engineering. I'd rather compute this dynamically and have this context for the main Jinja rendering mechanism to do its thing.



class EOLVersionCheck(Transform):
# pylint: disable=too-few-public-methods
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pro tip: try to use disable-next or put the suppression onto the exact line. bare disable turns the pylint check off until the end of the document, unlike what flake8 would do.

Comment on lines +37 to +38
with open(yaml_path, "r", encoding="utf-8") as f:
versions = yaml.safe_load(f)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a low-level API. Pathlib has a more elegant interface for reading/writing entire files:

Suggested change
with open(yaml_path, "r", encoding="utf-8") as f:
versions = yaml.safe_load(f)
versions = yaml.safe_load(yaml_path.read_text(encoding="utf-8"))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... That is better. Thanks! I'll actually put read_text to use in a separate project.

["git", "rev-parse", "--abbrev-ref", "HEAD"], text=True
).strip()
except subprocess.CalledProcessError as e:
print(f"Git error: {e}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't just print stuff. Use logging. You can get it via from sphinx.util import logging.

Better yet — raise a proper error: https://github.com/sphinx-contrib/sphinxcontrib-towncrier/blob/7f57120/src/sphinxcontrib/towncrier/ext.py#L148C13-L151C14

Suggested change
print(f"Git error: {e}")
raise self.error(f"Failed to abbreviate Git HEAD: {e !s}") from None


import yaml
from docutils import nodes
from jinja2 import Template
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So my problem with this is that you're going out of the way to have disconnected double use of the templating engine, while previously it was happening in one pass. I'm pretty sure you could throw most of this away and just compute a single boolean that would go into the context that the regular templating engine relies on.

The only thing you'd need would be hooking to different Sphinx lifecycle events. Since this is being computed early, I'd go for the earliest one that receives the Sphinx app as an argument and assign the computed value to the env config object that gets into Jinja, through that very same html_context mapping.

Oh, and since the config values may change, it's important to tell Sphinx that it influences caching. Otherwise, it may refuse to rebuild things that depend on that file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"disconnected double use of the templating engine, while previously it was happening in one pass" - now that you say it like that...

self.document.insert(0, banner)


def setup(app) -> Dict[str, Any]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to annotate the arg. Also, what Python version are you targeting? It may be possible to just lowercase dict. Plus, you should avoid using Any, especially since it's dead obvious that it's str | bool.

- stable-2.17
- stable-2.16
eol:
- stable-2.15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this represents non-core dist, the versioning here would probably be different

webknjaz added a commit to webknjaz/ansible--ansible-documentation that referenced this pull request Jan 17, 2025
This is an initial change allowing for restructuring how the config
settings are computed and putting that logic into a dedicated Sphinx
extension. The idea is that this extension may have multiple callbacks
that set configuration for Sphinx based on tags and the environment
state.

As an example, this in-tree extension implements setting the `is_eol`
variable in Jinja2 context very early in Sphinx life cycle.
It does this based on inspecting the state of current Git checkout as
well as reading a config file listing EOL and supported versions of
`ansible-core`.

The configuration format is TOML as it's gained a lot of the ecosystem
adoption over the past years and its parser made its way into the
standard library of Python, while PyYAML remains a third-party
dependency.

Supersedes ansible#2251.
webknjaz added a commit to webknjaz/ansible--ansible-documentation that referenced this pull request Jan 17, 2025
This is an initial change allowing for restructuring how the config
settings are computed and putting that logic into a dedicated Sphinx
extension. The idea is that this extension may have multiple callbacks
that set configuration for Sphinx based on tags and the environment
state.

As an example, this in-tree extension implements setting the `is_eol`
variable in Jinja2 context very early in Sphinx life cycle.
It does this based on inspecting the state of current Git checkout as
well as reading a config file listing EOL and supported versions of
`ansible-core`.

The configuration format is TOML as it's gained a lot of the ecosystem
adoption over the past years and its parser made its way into the
standard library of Python, while PyYAML remains a third-party
dependency.

Supersedes ansible#2251.
@oraNod oraNod closed this Jan 17, 2025
@ansible ansible deleted a comment from webknjaz Jan 17, 2025
@ansible ansible deleted a comment from oraNod Jan 17, 2025
@ansible ansible deleted a comment from webknjaz Jan 17, 2025
@samccann
Copy link
Contributor

Closed in favor of #2360 thanks both of you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc builds Relates to building the documentation tooling This PR affects tooling (CI, pr_labeler, noxfile, linters, etc.) but not the docs builds themselves.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a way to EOL package docs separate from core docs
3 participants