-
-
Notifications
You must be signed in to change notification settings - Fork 74
[fix] Allow easily building docs with local changes again #251 #257
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
Changes from all commits
ab4b835
d98c821
4b9525a
2e8ae91
1749f96
bc1aa8f
662b074
09a9879
8a41477
3bea3c2
c8ec441
885390d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
|
|
||
| import argparse | ||
| import os | ||
| import shutil | ||
| import subprocess | ||
| from copy import deepcopy | ||
|
|
||
|
|
@@ -265,12 +266,57 @@ def git_is_on_branch(repo_path): | |
| return result.stdout.strip() != "HEAD" | ||
|
|
||
|
|
||
| def clone_or_update_repo(name, branch, dir_name, owner="openwisp", dest=None): | ||
| def clone_or_update_repo( | ||
| name, branch, dir_name, owner="openwisp", dest=None, version_name=None | ||
| ): | ||
| """ | ||
| Clone or update a repository based on the module name and branch provided. | ||
| If the repository already exists, update it. Otherwise, clone the repository. | ||
| """ | ||
| repository = f"{owner}/{name}" | ||
| # Support for building with local changes | ||
| if name == "openwisp-docs": | ||
| # Ensure staging-dir is a real, empty directory | ||
| if os.path.islink("staging-dir") or os.path.isfile("staging-dir"): | ||
| os.unlink("staging-dir") | ||
| elif os.path.isdir("staging-dir"): | ||
| shutil.rmtree("staging-dir") | ||
| # If dev version, copy all doc files to the staging dir | ||
| if name == "openwisp-docs" and version_name == "dev": | ||
| os.makedirs("staging-dir", exist_ok=True) | ||
| base_dir = "docs" if os.path.isdir("docs") else "." | ||
| exclude_items = ("staging-dir", "modules", "version_switcher") | ||
| for item in os.listdir(base_dir): | ||
| is_dir = os.path.isdir(os.path.join(base_dir, item)) | ||
| if any( | ||
| ( | ||
| # not a dir nor a rst file, nor the spelling wordlist | ||
| all( | ||
| ( | ||
| not is_dir, | ||
| not item.endswith(".rst"), | ||
| item != "spelling_wordlist.txt", | ||
| ) | ||
| ), | ||
| # it's a dir but not designed to contain rst files | ||
| is_dir and item.startswith((".", "_")), | ||
| # in the exclude list | ||
| item in exclude_items, | ||
| ) | ||
| ): | ||
| continue | ||
|
Comment on lines
+287
to
+307
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Include conf.py and non‑RST assets in dev staging-dir. Proposed fix- exclude_items = ("staging-dir", "modules", "version_switcher")
+ exclude_items = ("staging-dir", "modules", "version_switcher", "_build", "__pycache__")
+ allowed_underscore_dirs = {"_static", "_templates"}
for item in os.listdir(base_dir):
is_dir = os.path.isdir(os.path.join(base_dir, item))
- if any(
- (
- # not a dir nor a rst file, nor the spelling wordlist
- all(
- (
- not is_dir,
- not item.endswith(".rst"),
- item != "spelling_wordlist.txt",
- )
- ),
- # it's a dir but not designed to contain rst files
- is_dir and item.startswith((".", "_")),
- # in the exclude list
- item in exclude_items,
- )
- ):
- continue
+ if item in exclude_items:
+ continue
+ if is_dir:
+ if item.startswith("."):
+ continue
+ if item.startswith("_") and item not in allowed_underscore_dirs:
+ continue
+ elif item.startswith("."):
+ continue🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @coderabbitai I'm testing locally and it's working perfectly. Are you sure sphinx doesn't already look also in the root dir for config files and templates? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 1384 🌐 Web query: 💡 Result: In Sphinx, To make Sphinx/autodoc “search” (import) modules from a parent directory, prepend that directory to Python’s import path ( # docs/source/conf.py
import sys
from pathlib import Path
# add repo root (parent of docs/source) to import path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))Common “src layout” example (repo has import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))This is the standard Sphinx-recommended approach when modules aren’t otherwise importable (e.g., not installed in the build env). (sphinx-doc.org) Citations:
🏁 Script executed: Length of output: 4454
Looking at the Makefile, I can see that Sphinx is invoked with ALLSPHINXOPTS = -c . -d $(BUILDDIR)/doctrees ... $(SRCDIR)So the current approach is correct:
The filtering logic is working exactly as intended. My original review comment was mistaken—there's no need to include Thank you for testing locally and pushing back on this! 🙏 |
||
| # skip virtual environments (detect with pyvenv.cfg) | ||
| if os.path.isdir(os.path.join(base_dir, item)) and os.path.exists( | ||
| os.path.join(base_dir, item, "pyvenv.cfg") | ||
| ): | ||
| continue | ||
| src_path = os.path.abspath(os.path.join(base_dir, item)) | ||
| dest_path = os.path.join("staging-dir", item) | ||
| if os.path.islink(dest_path): | ||
| os.unlink(dest_path) | ||
| if not os.path.exists(dest_path): | ||
| os.symlink(src_path, dest_path) | ||
| return | ||
| if os.environ.get("SSH"): | ||
| # SSH cloning is a convenient option for local development, as it | ||
| # allows you to commit changes directly to the repository, but it | ||
|
|
@@ -286,15 +332,28 @@ def clone_or_update_repo(name, branch, dir_name, owner="openwisp", dest=None): | |
| if os.path.exists(clone_path): | ||
| print(f"Repository '{name}' already exists. Updating...") | ||
| subprocess.run( | ||
| # Clears ambiguity when git tags and branches have identical names | ||
| ["git", "fetch", "origin", f"refs/heads/{branch}:refs/heads/{branch}"], | ||
| # Update remote-tracking ref (avoid fetching into checked-out branch) | ||
| [ | ||
| "git", | ||
| "fetch", | ||
| "origin", | ||
| branch, | ||
| ], | ||
| cwd=clone_path, | ||
| check=True, | ||
| ) | ||
| # "-c advice.detachedHead=false" is used to suppress the warning | ||
| # about being in a detached HEAD state when checking out tags. | ||
| subprocess.run( | ||
| ["git", "-c", "advice.detachedHead=false", "checkout", f"refs/heads/{branch}"], | ||
| [ | ||
| "git", | ||
| "-c", | ||
| "advice.detachedHead=false", | ||
| "checkout", | ||
| "-B", | ||
| branch, | ||
| "FETCH_HEAD", | ||
| ], | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| cwd=clone_path, | ||
| check=True, | ||
| ) | ||
|
|
@@ -304,7 +363,9 @@ def clone_or_update_repo(name, branch, dir_name, owner="openwisp", dest=None): | |
| # During local development, we attempt to pull updates, but only if the | ||
| # current HEAD is on a branch (i.e., not detached, such as when on a tag). | ||
| if not os.environ.get("PRODUCTION", False) and git_is_on_branch(clone_path): | ||
| subprocess.run(["git", "pull"], cwd=clone_path, check=True) | ||
| subprocess.run( | ||
| ["git", "pull", "origin", branch], cwd=clone_path, check=True | ||
| ) | ||
| else: | ||
| print(f"Cloning repository '{name}'...") | ||
| subprocess.run( | ||
|
|
@@ -394,6 +455,7 @@ def main(): | |
| name="openwisp-docs", | ||
| branch=docs_branch, | ||
| dir_name="openwisp-docs", | ||
| version_name=version_name, | ||
| ) | ||
| for module in modules: | ||
| clone_or_update_repo( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gate the local-dev staging path to non‑PRODUCTION builds.
Right now the dev symlink path triggers whenever
version_name == "dev", even ifPRODUCTION=1, which violates the stated constraint and can make production builds use the working tree instead of a clean clone.Proposed fix
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai PRODUCTION=1 means this is running the github actions CI, which is always a fresh clone. Moreover, we want the CI build to run this code so that if it's broken the CI build will fail and the problem won't be hidden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nemesifier, thank you for the clarification! You're absolutely right—in CI withPRODUCTION=1, the repository is a fresh clone, so the staging-dir symlinks would point to the same committed files that a normal clone would check out. There are no uncommitted local changes in that context, and running this code path in CI validates that the feature doesn't break. My concern doesn't apply here. I appreciate the explanation!🐰✨