From 87f04263efa135d66b4d7a6bbe737d4f58f7320d Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 22 Dec 2021 16:45:18 -0600 Subject: [PATCH 1/3] update contributing docs with release checklist --- CONTRIBUTING.md | 31 ++++++++++++++++++++----------- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 22c0ccfb..43706e5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,17 +70,26 @@ doit release This will recreate the `test` environment with the specified lockfile, and repeat all the steps. -### Releasing - -- ensure `VERSION` has been increased appropriately -- ensure `HISTORY.ipynb` is up-to-date -- (usually) grab the `dist` assets from CI, unpack into `./dist` -- make a GitHub release, adding the `.tar.gz`, `.whl`, and `SHA256SUMS` -- upload to PyPI - +## Releasing + +- [ ] merge all outstanding PRs +- [ ] start a release issue with a checklist (maybe like this one) +- [ ] ensure `VERSION` has been increased appropriately +- [ ] ensure the `HISTORY.ipynb` is up-to-date +- [ ] validate on binder +- [ ] validate on ReadTheDocs +- [ ] wait for a successful build of `master` +- [ ] download the `dist` archive and unpack somewhere (maybe a fresh `dist`) +- [ ] create a new release through the GitHub UI + - [ ] paste in the relevant `HISTORY` entries + - [ ] upload the artifacts +- [ ] actually upload to pypi.org ```bash doit publish ``` - -- do a post-mortem issue -- bump `VERSION` to a working status +- [ ] postmortem + - [ ] handle `conda-forge` feedstock tasks + - [ ] validate on binder via simplest-possible gists + - [ ] activate the version on ReadTheDocs + - [ ] bump `VERSION` to next development version + - [ ] update release procedures diff --git a/package.json b/package.json index 607ccbed..685aaa45 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "singleQuote": true }, "devDependencies": { - "prettier": "^2.1.2" + "prettier": "^2.5.1" }, "scripts": { "prettier": "prettier --write --list-different \"./*.{json,md,yml}\" \"{.github,.binder,docs}/**/*.{json,md,yml,css}\"" diff --git a/yarn.lock b/yarn.lock index 46b9ed83..f5d535fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -prettier@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.0.tgz#8a03c7777883b29b37fb2c4348c66a78e980418b" - integrity sha512-yYerpkvseM4iKD/BXLYUkQV5aKt4tQPqaGW6EsZjzyu0r7sVZZNPJW4Y8MyKmicp6t42XUPcBVA+H6sB3gqndw== +prettier@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== From 0713ac8245be06b116fad667665144b28fb05b40 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 22 Dec 2021 17:12:35 -0600 Subject: [PATCH 2/3] update dev examples --- CONTRIBUTING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43706e5d..6f2a40b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,12 +2,12 @@ ## Get `CONDA_EXE` -- Get [miniforge](https://github.com/conda-forge/miniforge) +- Get [Mambaforge](https://github.com/conda-forge/miniforge) ```bash -conda install -c conda-forge doit - optional meta-dependencies -conda install -c conda-forge conda-lock mamba +mamba install -c conda-forge doit +# optional meta-dependency +mamba install -c conda-forge conda-lock ``` ## Get the code @@ -51,19 +51,19 @@ developing, hoping for a better cache hit rate. On the same _operating system_, however, any of the pre-solved lockfiles can be used, by specifying the `RJFL_LOCKFILE` environment variable. -For example, if `linux-64` running `python3.6` with `jupyterlab 1` failed: +For example, if `linux-64` running `python3.7` with `jupyterlab 1` failed: ```bash !/usr/bin/env bash set -eux -RFJL_LOCKDIR=test/linux-64/py3.6/lab1 doit release +RFJL_LOCKDIR=test/linux-64/py3.7/lab1 doit release ``` Or, in a `bat` script: ```bat @echo on -set RFJL_LOCKDIR=test/win-64/py3.9/lab1 +set RFJL_LOCKDIR=test/win-64/py3.7/lab1 doit release ``` From e7838ed50ae5926b89a964e723a4bbd7f8146a09 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 22 Dec 2021 18:20:48 -0600 Subject: [PATCH 3/3] tune up docs further --- CONTRIBUTING.md | 2 +- _scripts/project.py | 4 +++- docs/CI.ipynb | 10 +++++----- docs/INSTALL.ipynb | 15 +++++++++------ docs/KEYWORDS.ipynb | 4 ++-- docs/LIMITS.ipynb | 4 ++-- docs/MAGIC.ipynb | 4 ++-- docs/WHY.ipynb | 20 +++++++++++--------- docs/conf.py | 22 +++++++++++++++++++--- docs/index.ipynb | 4 ++-- dodo.py | 42 +++++++++++++++++++++++++++++++++++++----- 11 files changed, 93 insertions(+), 38 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f2a40b9..e5563274 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Get `CONDA_EXE` -- Get [Mambaforge](https://github.com/conda-forge/miniforge) +- Get [Mambaforge](https://conda-forge.org/miniforge/) ```bash mamba install -c conda-forge doit diff --git a/_scripts/project.py b/_scripts/project.py index 2132493d..37e0644c 100644 --- a/_scripts/project.py +++ b/_scripts/project.py @@ -104,7 +104,7 @@ DOCS = ROOT / "docs" RTD_ENV = DOCS / "rtd.yml" DOCS_CONF_PY = DOCS / "conf.py" -DOCS_BUILDINFO = BUILD / "docs" / "html" / ".buildinfo" +DOCS_BUILDINFO = BUILD / "docs/html/.buildinfo" # demo LABEXTXT = BINDER / "labex.txt" @@ -239,6 +239,8 @@ def _is_excluded(flow, pf, py, lab): DOCS_CONF_PY, ] if ".ipynb_checkpoints" not in str(p) + and "_robot_magic_" not in str(p) + and not p.name.endswith(".html") ] PACKAGE_JSON = ROOT / "package.json" diff --git a/docs/CI.ipynb b/docs/CI.ipynb index d26b999c..a384ea46 100644 --- a/docs/CI.ipynb +++ b/docs/CI.ipynb @@ -99,7 +99,7 @@ ">\n", "> A small collection of [scripts][], not shipped as part of the distribution, provide some custom behaviors around particularly complex tasks.\n", "> \n", - "> - sometimes `doit` is to heavy of a hammer for delicate work\n", + "> - sometimes `doit` is too heavy of a hammer for delicate work\n", "\n", "[scripts]: https://github.com/robots-from-jupyter/robotframework-jupyterlibrary/tree/master/scripts\n", "[doit]: https://github.com/robots-from-jupyter/robotframework-jupyterlibrary/blob/master/dodo.py" @@ -124,15 +124,15 @@ "A heavy CI pipeline can become necessary to manage many competing concerns. Each non-trivial, browser-based robot test can easily cost tens of seconds. Some approaches:\n", "- use an up-front dry-run `robot` test\n", " - this can help catch whitespace errors in robot syntax\n", - " - this usually costs $\\frac{\\sim1}{100}$ the time of running the full test \n", - "- run tests in subsets and in parallel with `pabot`\n", + " - this usually costs $\\frac{\\sim1}{100}$ the time of running the full test\n", + "- run tests in subsets, in parallel, and in random order with `pabot`\n", " - requires avoiding shared resources, e.g. network ports, databases, logfiles" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -146,7 +146,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/docs/INSTALL.ipynb b/docs/INSTALL.ipynb index 65dce4bf..f8b8f4ec 100644 --- a/docs/INSTALL.ipynb +++ b/docs/INSTALL.ipynb @@ -38,14 +38,17 @@ "Here's a complete setup:\n", "```shell\n", "conda create --name testing-jupyter --channel conda-forge \n", - " python=3 \\ # 3.6 required, not tested with pypy \n", + " python=3 \\ # 3.7 required, not tested with pypy \n", " jupyterlab \\\n", " robotframework-seleniumlibrary \\\n", - " geckodriver\n", + " geckodriver \\\n", + " firefox\n", "\n", "conda activate testing-jupyter\n", "\n", - "pip install --no-deps \\ # don't want any surprises\n", + "pip install \\\n", + " --no-deps \\ # don't want any surprises\n", + " --ignore-installed \\ # just to be sure\n", " git+http://github.com/robots-from-jupyter/robotframework-jupyterlibrary\n", "```" ] @@ -79,13 +82,13 @@ }, "outputs": [], "source": [ - "!python ../dodo.py list --all --status" + "!python ../dodo.py list --all" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -99,7 +102,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" }, "nteract": { "version": "nteract-on-jupyter@0.35.4" diff --git a/docs/KEYWORDS.ipynb b/docs/KEYWORDS.ipynb index da47fa0b..ce88f62e 100644 --- a/docs/KEYWORDS.ipynb +++ b/docs/KEYWORDS.ipynb @@ -144,7 +144,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -158,7 +158,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/docs/LIMITS.ipynb b/docs/LIMITS.ipynb index 4f2b635e..706391ee 100644 --- a/docs/LIMITS.ipynb +++ b/docs/LIMITS.ipynb @@ -32,7 +32,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -46,7 +46,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/docs/MAGIC.ipynb b/docs/MAGIC.ipynb index a23019ab..af1c01b5 100644 --- a/docs/MAGIC.ipynb +++ b/docs/MAGIC.ipynb @@ -173,7 +173,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -187,7 +187,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/docs/WHY.ipynb b/docs/WHY.ipynb index a9a6fd87..f8fd1978 100644 --- a/docs/WHY.ipynb +++ b/docs/WHY.ipynb @@ -31,21 +31,23 @@ "source": [ "## ...JupyterLibrary?\n", "\n", - "Powered by [Robot Framework](https://robotframework.org), `JupyterLibrary` allows you to:\n", + "Powered by [Robot Framework](https://robotframework.org) and [SeleniumLibrary](https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html), `JupyterLibrary` allows you to:\n", "\n", - "- write tests in concise, plain language\n", + "- write tests in concise, **plain language**\n", " - and extend this language to meet your needs\n", - "- run in multiple browsers (even at the same time)\n", - " - run on multiple operating systems\n", - "- view rich reports of your test results\n", - " - but also compare your reports over time with machine-readable formats\n", - "- generate screenshots to augment your documentation" + "- test multiple Jupyter **clients**\n", + " - and multiple **versions** of them\n", + "- run in multiple, **real browsers** (even at the same time)\n", + " - and on multiple **operating systems**\n", + "- view rich **reports** of your test results\n", + " - but also compare your reports over time with **machine-readable** formats\n", + "- generate **screenshots** to augment your documentation" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -59,7 +61,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/docs/conf.py b/docs/conf.py index 6d47c7f1..aa4f7c92 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,6 +20,8 @@ CONF = {k: _parser[k] for k in _parser.sections()} YEAR = datetime.now().year +KEYWORDS = "*** Keywords ***" +VARIABLES = "*** Variables ***" def setup(app): @@ -38,12 +40,26 @@ def setup(app): for client_dir in CLIENTS: client = Path(client_dir) + print(client.name) with TemporaryDirectory() as td: tdp = Path(td) - agg = "" + agg = "\n".join( + [ + "*** Settings ***", + f"Documentation Keywords for {client.name}", + "", + ] + ) for sub in sorted(client.rglob("*.resource")): - print(f"collecting {sub.relative_to(client)}") - agg += sub.read_text() + sub_text = sub.read_text() + has_vars = VARIABLES in sub_text + has_kw = KEYWORDS in sub_text + print(f"... collecting {sub.relative_to(client)}") + print(f" ... keywords? {has_kw}") + print(f" ... variables? {has_vars}") + + split_on = VARIABLES if has_vars else KEYWORDS + agg += "\n".join([split_on, sub_text.split(split_on)[1], ""]) out_file = Path(tdp / f"{client.name}.resource") out_file.write_text(agg) subprocess.run( diff --git a/docs/index.ipynb b/docs/index.ipynb index 4fa5a204..17a293ec 100644 --- a/docs/index.ipynb +++ b/docs/index.ipynb @@ -31,7 +31,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -45,7 +45,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.12" } }, "nbformat": 4, diff --git a/dodo.py b/dodo.py index 47ebe43e..6f61112b 100644 --- a/dodo.py +++ b/dodo.py @@ -58,6 +58,7 @@ def task_build(): yield dict( name="pypi", + doc="build the pypi sdist/wheel", actions=[[*run_in, *P.PY, "setup.py", "sdist", "bdist_wheel"]], targets=[P.SDIST, P.WHEEL], file_dep=[*P.PY_SRC, *P.ROBOT_SRC, P.VERSION_FILE, *P.SETUP_CRUFT, env_lock], @@ -73,6 +74,7 @@ def _update_hash(): yield dict( name="hash", + doc="generate a hash file of all distributions", file_dep=P.HASH_DEPS, targets=[P.SHA256SUMS], actions=[_update_hash], @@ -108,6 +110,7 @@ def _template(): yield dict( name="recipe", + doc="update the conda recipe", file_dep=file_dep, targets=[P.META_YAML], actions=[_template], @@ -115,6 +118,7 @@ def _template(): yield dict( name="build", + doc="use boa to build the conda package", file_dep=[P.META_YAML], actions=[ [ @@ -179,8 +183,20 @@ def _env_from_lock(): yield dict( name="sphinx", - actions=[[*run_in, "sphinx-build", "-M", "html", "docs", "build/docs"]], - file_dep=[frozen, *P.ALL_DOCS_SRC, *P.SETUP_CRUFT, *P.ROBOT_SRC], + doc="build the docs with sphinx", + actions=[ + [ + *run_in, + "sphinx-build", + "-W", + "-a", + "-b", + "html", + "docs", + "build/docs/html", + ] + ], + file_dep=[frozen, *P.ALL_DOCS_SRC, *P.SETUP_CRUFT, *P.ROBOT_SRC, P.DODO], targets=[P.DOCS_BUILDINFO], ) @@ -217,6 +233,7 @@ def _make_env(env): yield dict( name=env, + doc=f"create the local {env} environment", file_dep=[lockfile], actions=actions, targets=[explicit_list], @@ -242,6 +259,7 @@ def task_lint(): yield dict( name="black", + doc="ensure python code is well-formatted", actions=[clean, [*pym, "black", "--quiet", *P.ALL_PY], touch], file_dep=[*P.ALL_PY, env_lock], targets=[P.OK.black], @@ -251,6 +269,7 @@ def task_lint(): yield dict( name="pyflakes", + doc="ensure python code is well-behaved", actions=[clean, [*pym, "pyflakes", *P.ALL_PY], touch], file_dep=[*P.ALL_PY, env_lock, P.OK.black], targets=[P.OK.pyflakes], @@ -260,6 +279,7 @@ def task_lint(): yield dict( name="robotidy", + doc="ensure robot code is well-formatted", actions=[clean, [*run_in, *P.ROBOTIDY_ARGS, P.SRC, P.ATEST], touch], file_dep=[*P.ALL_ROBOT, env_lock], targets=[P.OK.robotidy], @@ -269,6 +289,7 @@ def task_lint(): yield dict( name="robocop", + doc="ensure robot code is well-behaved", actions=[clean, [*run_in, *P.ROBOCOP_ARGS, P.SRC, P.ATEST], touch], file_dep=[*P.ALL_ROBOT, env_lock, P.OK.robotidy], targets=[P.OK.robocop], @@ -278,6 +299,7 @@ def task_lint(): yield dict( name="prettier", + doc="ensure markdown, YAML, JSON, etc. are well-formatted", actions=[clean, [*run_in, "yarn", "--silent", "prettier"], touch], file_dep=[*P.ALL_PRETTIER, P.YARN_INTEGRITY], targets=[P.OK.prettier], @@ -293,6 +315,7 @@ def task_js(): yield dict( name="yarn", + doc="install nodejs dev dependencies", uptodate=[ config_changed({k: P.PACKAGE[k] for k in ["devDependencies", "prettier"]}) ], @@ -357,6 +380,7 @@ def _lab(): yield dict( name="serve", + doc="runs lab (never stops)", uptodate=[lambda: False], actions=[PythonInteractiveAction(_lab)], file_dep=serve_deps, @@ -425,6 +449,7 @@ def task_test(): yield dict( name="dryrun", + doc="pass the tests through the robot machinery, but don't actually _run_ anything", uptodate=[config_changed(os.environ.get("ATEST_ARGS", ""))], actions=[clean, [*pym, "_scripts.atest", "--dryrun"], touch], file_dep=robot_deps, @@ -462,8 +487,13 @@ def _make_lock_task_name(key): def _make_lock_task(key, target): (flow, pf, py, lab) = key + ft = [k for k in [py, lab] if k] + if ft: + ft = f"(ft. {', '.join(ft)})" + task = dict( name=_make_lock_task_name(key), + doc=f"lock the {flow} environment for {pf} {ft}".strip(), actions=[[*P.SCRIPT_LOCK, target]], file_dep=[*P.ENV_DEPS[key]], targets=[target], @@ -497,7 +527,7 @@ def task_lock(): yield _make_lock_task(key, target) def task_publish(): - """publish to pypi""" + """publish distributioons""" def _check_hash(): on_disk = P.SHA256SUMS.read_text() @@ -508,14 +538,16 @@ def _check_hash(): raise RuntimeError("SHA256SUMS do not match:") print("SHA256SUMS are OK") - return dict( + yield dict( + name="pypi", + doc="upload python sdist and wheel to PyPI", actions=[ _check_hash, InteractiveAction( [*P.RUN_IN["meta"], "twine", "upload", P.SDIST, P.WHEEL], shell=False, ), - ] + ], )