From bf3b5f47acf5ff205ea209743eff3c4e2a3b82ed Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 24 Sep 2024 04:03:36 -0500 Subject: [PATCH 1/4] Add setup and setup-remotes Nox commands --- noxfile.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/noxfile.py b/noxfile.py index a5823c2f..d83dede9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -21,11 +21,12 @@ nox.options.sessions = ["build"] nox.options.default_venv_backend = "none" - -CI = "CI" in os.environ +ORG_NAME = "spyder-ide" +REPO_NAME = "spyder-docs" +REPO_URL_HTTPS = "https://github.com/{user}/{repo}.git" +REPO_URL_SSH = "git@github.com:{user}/{repo}.git" CANARY_COMMAND = ("sphinx-build", "--version") - BUILD_INVOCATION = ("python", "-m", "sphinx") SOURCE_DIR = Path("doc").resolve() BUILD_DIR = Path("doc/_build").resolve() @@ -51,6 +52,8 @@ CONF_PY = SOURCE_DIR / "conf.py" SCRIPT_DIR = Path("scripts").resolve() +CI = "CI" in os.environ + # ---- Helpers ---- # @@ -89,6 +92,8 @@ def extract_option_values(options, option_names, *, split_csv=False): """Extract particular option values from a sequence of options.""" option_values = [] remaining_options = [] + if isinstance(option_names, str): + option_names = [option_names] save_next_option = False for option in options: @@ -209,14 +214,15 @@ def run(session): def _clean(session): """Remove the build directory.""" print(f"Removing build directory {BUILD_DIR.as_posix()!r}") - ignore = session.posargs and session.posargs[0] in {"-i", "--ignore"} + ignore_flag = "--ignore" + should_ignore = ignore_flag in session.posargs try: - shutil.rmtree(BUILD_DIR, ignore_errors=ignore) + shutil.rmtree(BUILD_DIR, ignore_errors=should_ignore) except FileNotFoundError: pass except Exception: - print("\nError removing files; pass '-i'/'--ignore' flag to ignore\n") + print(f"\nError removing files; pass {ignore_flag!r} flag to ignore\n") raise @@ -226,6 +232,72 @@ def clean(session): _clean(session) +# --- Set up --- # + +def _setup_remotes(session): + """Set up the origin and upstream remote repositories.""" + remote_cmd = ["git", "remote"] + posargs = list(session.posargs[1:]) + https = "--https" in posargs + ssh = "--ssh" in posargs + username_args = extract_option_values(posargs, "--username")[0] + if https == ssh: + session.error("Exactly one of '--https' or '--ssh' must be passed") + + # Get current origin details + origin_url_cmd = (*remote_cmd, "get-url", "origin") + origin_url = session.run( + *origin_url_cmd, external=True, silent=True, log=False).strip() + if "https://" not in origin_url: + origin_url = origin_url.split(":")[-1] + origin_user, origin_repo = origin_url.split("/")[-2:] + if origin_repo.endswith(".git"): + origin_repo = origin_repo[:-4] + + # Check username + if username_args: + origin_user = username_args[0].strip().lstrip("@") + elif origin_user.lower() == ORG_NAME.lower(): + code_host = REPO_URL_HTTPS.split(":")[1].lstrip("/").split("/")[0] + session.warn( + "Origin remote currently set to upstream; should be your fork.\n" + f"To fix, fork it and pass --username " + ) + + # Set up remotes + existing_remotes = session.run( + *remote_cmd, external=True, silent=True, log=False).strip().split("\n") + for remote, user_name, repo_name in ( + ("origin", origin_user, origin_repo), + ("upstream", ORG_NAME, REPO_NAME), + ): + action = "set-url" if remote in existing_remotes else "add" + fetch_url = REPO_URL_HTTPS.format(user=user_name, repo=repo_name) + session.run(*remote_cmd, action, remote, fetch_url, external=True) + + ssh_url = REPO_URL_SSH.format(user=user_name, repo=repo_name) + push_url = ssh_url if ssh else fetch_url + session.run( + *remote_cmd, "set-url", "--push", remote, push_url, external=True) + + session.run("git", "fetch", "--all", external=True) + + +@nox.session(name="setup-remotes") +def setup_remotes(session): + """Set up the Git remotes; pass --https or --ssh to specify URL type.""" + session.notify("_execute", posargs=([_setup_remotes], *session.posargs)) + + +@nox.session() +def setup(session): + """Set up the project; pass --https or --ssh to specify Git URL type.""" + session.notify( + "_execute", + posargs=([_setup_remotes, _install_hooks, _clean], *session.posargs), + ) + + # ---- Build ---- # def _build(session): @@ -312,6 +384,7 @@ def serve(_session): def _prepare_multiversion(_session=None): """Execute the pre-deployment steps for multi-version support.""" # pylint: disable=import-outside-toplevel + # pylint: disable=import-error sys.path.append(str(SCRIPT_DIR)) import generateredirects From 947896f46d36bcbfef050a2fc7816bf3827ccb7a Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 24 Sep 2024 04:24:49 -0500 Subject: [PATCH 2/4] Add Nox setup command to Contributing Guide and restructure accordingly --- CONTRIBUTING.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ae1325ca..fab9a858 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,7 +25,8 @@ Let us know if you have any further questions, and we look forward to your contr - [Setting Up a Development Environment Manually](#setting-up-a-development-environment-manually) - [Create and activate a fresh environment](#create-and-activate-a-fresh-environment) - [Install dependencies](#install-dependencies) -- [Installing the Pre-Commit Hooks](#installing-the-pre-commit-hooks) + - [Add the upstream remote](#add-the-upstream-remote) +- [Installing and Using the Pre-Commit Hooks](#installing-and-using-the-pre-commit-hooks) - [Building the Docs](#building-the-docs) - [Build with Nox](#build-with-nox) - [Build manually](#build-manually) @@ -66,12 +67,6 @@ After cloning the repository, navigate to its new directory using the `cd` comma cd spyder-docs ``` -Finally, set the upstream remote to the official Spyder-Docs repo with: - -```shell -git remote add upstream https://github.com/spyder-ide/spyder-docs.git -``` - ## Setting Up a Development Environment with Nox (Recommended) @@ -97,12 +92,21 @@ or, if not using Conda, python -m pip install nox ``` -To check that Nox is installed and browse a list of commands (called "sessions") we provide through Nox and what they do, you can run +To check that Nox is installed and browse a list of commands (called "sessions") we provide through Nox and what they do, run ```shell nox --list ``` +Then, run the ``setup`` session, which performs the project's one-time setup steps; pass either ``--https`` or ``--ssh`` to specify how you'd like to push changes to GitHub. +If not sure, pass ``--https`` for now: + +```shell +nox -s setup -- --https +``` + +You can always switch it later with ``nox -s setup-remotes -- --ssh``. + ## Setting Up a Development Environment Manually @@ -171,20 +175,30 @@ python -m pip install -r requirements.txt ``` +### Add the upstream remote + +Make sure to set the upstream Git remote to the official Spyder-Docs repo with: + +```shell +git remote add upstream https://github.com/spyder-ide/spyder-docs.git +``` + + -## Installing the Pre-Commit Hooks +## Installing and Using the Pre-Commit Hooks This repository uses [Pre-Commit](https://pre-commit.com/) to install, configure and update a suite of pre-commit hooks that check for common problems and issues, and fix many of them automatically. You'll need to install the pre-commit hooks before committing any changes, as they both auto-generate/update specific files and run a comprehensive series of checks to help you find likely errors and enforce the project's code quality guidelines and style guide. They are also run on all pull requests, and will need to pass before your changes can be merged. -If you've [using Nox](#setting-up-a-development-environment-with-nox-recommended), it installs Pre-Commit in its own environment, and we provide our own simplified command to install the hooks: +If you've [using Nox](#setting-up-a-development-environment-with-nox-recommended), it installs Pre-Commit and its hooks for you when running ``nox -s setup`` (as above). +You can also install them with ```shell nox -s install-hooks ``` -If instead you've followed the [manual install approach](#install-dependencies), pre-commit will be installed directly in your local environment. +If you've followed the [manual install approach](#install-dependencies), Pre-Commit will be installed directly in your local environment. To install the hooks, run the following from the root of this repo: ```shell From 9dca84ef1042461c3c4d870da23ddf691ab4533c Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 24 Sep 2024 04:45:40 -0500 Subject: [PATCH 3/4] Add docs aliases to Nox build commands for consistency/reusability --- noxfile.py | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/noxfile.py b/noxfile.py index d83dede9..bfeb5863 100644 --- a/noxfile.py +++ b/noxfile.py @@ -302,9 +302,7 @@ def setup(session): def _build(session): """Execute the docs build.""" - sphinx_invocation = construct_sphinx_invocation( - posargs=session.posargs[1:]) - session.run(*sphinx_invocation) + _docs(session) @nox.session @@ -314,6 +312,32 @@ def build(session): def _autobuild(session): + """Use Sphinx-Autobuild to rebuild the project and open in browser.""" + _autodocs(session) + + +@nox.session +def autobuild(session): + """Rebuild the project continuously as source files are changed.""" + session.notify("_execute", posargs=([_autobuild], *session.posargs)) + + +# --- Docs --- # + +def _docs(session): + """Execute the docs build.""" + sphinx_invocation = construct_sphinx_invocation( + posargs=session.posargs[1:]) + session.run(*sphinx_invocation) + + +@nox.session +def docs(session): + """Build the documentation.""" + session.notify("_execute", posargs=([_docs], *session.posargs)) + + +def _autodocs(session): """Use Sphinx-Autobuild to rebuild the project and open in browser.""" session.install("sphinx-autobuild") @@ -333,9 +357,9 @@ def _autobuild(session): @nox.session -def autobuild(session): - """Rebuild the project continuously as source files are changed.""" - session.notify("_execute", posargs=([_autobuild], *session.posargs)) +def autodocs(session): + """Rebuild the docs continuously as source files are changed.""" + session.notify("_execute", posargs=([_autodocs], *session.posargs)) def _build_languages(session): @@ -370,17 +394,28 @@ def build_multilanguage(session): # ---- Deploy ---- # -def _serve(_session=None): +def _serve(session=None): + """Open the docs in a web browser.""" + _serve_docs(session) + + +def _serve_docs(_session=None): """Open the docs in a web browser.""" webbrowser.open(HTML_INDEX_PATH.as_uri()) @nox.session def serve(_session): - """Display the project.""" + """Display the built project.""" _serve() +@nox.session(name="serve-docs") +def serve_docs(_session): + """Display the rendered documentation.""" + _serve_docs() + + def _prepare_multiversion(_session=None): """Execute the pre-deployment steps for multi-version support.""" # pylint: disable=import-outside-toplevel From 23eba15dee3c7992a310d1ab1a8b8c0a46733c63 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 24 Sep 2024 04:51:05 -0500 Subject: [PATCH 4/4] Make nox -s run emit a helpful error if no command invocation is passed --- noxfile.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index bfeb5863..cd3bc7f7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -201,8 +201,11 @@ def build_help(session): def _run(session): - """Run an arbitrary command in the project's venv.""" - session.run(*session.posargs[1:]) + """Run an arbitrary command invocation in the project's venv.""" + posargs = session.posargs[1:] + if not posargs: + session.error("Must pass a command invocation to run") + session.run(*posargs) @nox.session()