diff --git a/.binder/conda-linux-64.lock b/.binder/conda-linux-64.lock index efdc190..945d28c 100644 --- a/.binder/conda-linux-64.lock +++ b/.binder/conda-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: d590a6f1af5b3bf72a3b889d1bf03c4b2d69937757baa840cc0b841a898e956b +# input_hash: 6d5e923fdf0849ccdceed869afeeb7be9753e20caf1c32130dc84bdc218f0412 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2023.11.17-hbcca054_0.conda#01ffc8d36f9eba0ce0b3c1955fa780ee @@ -242,7 +242,9 @@ https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.25.2-pyhd8ed1a https://conda.anaconda.org/conda-forge/noarch/nbconvert-7.11.0-pyhd8ed1ab_0.conda#e492b36cbea1c83d1663fa73a8abff9b https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.3-pyhd8ed1ab_0.conda#67e0fe74c156267d9159e9133df7fd37 https://conda.anaconda.org/conda-forge/label/jupyterlab_alpha/noarch/jupyterlab-4.1.0a4-pyhd9494c6_0.conda#937116905e11106b9bcf4e5cc05b6793 +https://conda.anaconda.org/conda-forge/label/jupyterlab_fonts_alpha/noarch/jupyterlab-fonts-3.0.0a3-pyheae6aa3_0.conda#14b6ea3738c1e800a610f1e1df155548 https://conda.anaconda.org/conda-forge/label/notebook_alpha/noarch/notebook-7.1.0a0-pyhdf78bca_0.conda#aa9e88daa0d394a944f48ffbea1c7556 +https://conda.anaconda.org/conda-forge/label/jupyterlab_deck_alpha/noarch/jupyterlab-deck-0.2.0a0-pyha661375_0.conda#6e6bf18dc450f5b33d9ec9b47abbfea1 https://conda.anaconda.org/conda-forge/noarch/myst-parser-2.0.0-pyhd8ed1ab_0.conda#70699181909e468875f12076e1b0a8a9 https://conda.anaconda.org/conda-forge/noarch/myst-nb-1.0.0-pyhd8ed1ab_0.conda#fe4a9aa4a900dc509fcb5e6210184fa3 https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.14.4-pyhd8ed1ab_0.conda#c79b8443908032263ffb40ee6215e9e4 diff --git a/.binder/environment.yml b/.binder/environment.yml index 8423ac4..ede7d21 100644 --- a/.binder/environment.yml +++ b/.binder/environment.yml @@ -1,7 +1,9 @@ channels: - - conda-forge - conda-forge/label/jupyterlab_alpha - conda-forge/label/notebook_alpha + - conda-forge/label/jupyterlab_fonts_alpha + - conda-forge/label/jupyterlab_deck_alpha + - conda-forge - nodefaults platforms: - linux-64 @@ -14,6 +16,8 @@ dependencies: - notebook >=7.1.0a0,<8 # images - imagemagick + # demo + - jupyterlab-deck >=0.2.0a0 ### .github/environment.yml ### # fix - ruff diff --git a/README.md b/README.md index 142966b..adae6be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # jupyak -> get a JupyterLite preview of pull requests across the Jupyter stack +> get a statically-hosted JupyterLite preview of pull requests from across the +> Jupyter stack ## what does it do? @@ -29,6 +30,10 @@ This repo accepts [pull requests](#how-does-it-work) that build static [preview - PR-based [preview site](#how-do-i-get-a-preview-site) - preview of the docs for the site itself +> The above works on at least _one_ Ubuntu 22.03 LTS machine, but little care has +> been taken to make _anything_ run on any other system, and likely won't be tested +> anywhere else. + ## how does it work? Delivering a preview site uses a few different GitHub Actions workflows and ReadTheDocs. @@ -38,7 +43,12 @@ Delivering a preview site uses a few different GitHub Actions workflows and Read [repos](#what-can-it-build). - Clicking the _submit_ button will open a "new file" GitHub page against this repo - Click _Commit Changes..._ - - Follow the Pull Request template and submit + - Follow the _Pull Request_ template and submit +- A GitHub Action job will create a link to the current site + - This will initially return `404`, as the site isn't built yet + - Clicking instead on the ReadTheDocs check at the bottom of the Pull Request + will show build logs, but these are _intentionally_ sparse, capturing most output + as logs to present in the built site. - ReadTheDocs will check out the PR and build a preview JupyterLite site on a custom domain - If the preview site **fails** to build, the built RTD site will contain @@ -47,11 +57,11 @@ Delivering a preview site uses a few different GitHub Actions workflows and Read ## what can it build? -`jupyak` understand how to work with one or more human-readable HTML URLs of the forms: +`jupyak` understand how to work with one or more human-readable URLs fragments of the forms: - - `{:repo}/pull/{:pull-id}` - - `{:repo}/tree/{:branch}` - - `{:repo}/releases/tag/{:tag}` + - `/pull/{:pull-id}` + - `/tree/{:branch}` + - `/releases/tag/{:tag}` - > note that the `/tree/{:tag}` form will fail loudly ...from the following repos: @@ -69,5 +79,23 @@ Delivering a preview site uses a few different GitHub Actions workflows and Read - https://github.com/jupyterlite/jupyterlite - https://github.com/jupyterlite/pyodide-kernel +Additionally, a single GitHub gist can be used as the contents of the preview site, +and can further configure the build- and runtime behavior of JupyterLite by providing +a custom `jupyter_lite_config.json` and/or `jupyter-lite.json`. + +## what does it _not_ build? + +A few more things _could_ be built, and might be interesting to evaluate +a PR stack, ar note. However, each of these would come at the expense of a lower +chance of a usable JupyterLite site at the end of each PR build. These include: + +- test reports +- examples +- per-project documentation +- lint reports +- anything that doesn't work in the browser +- anything that requires a `c`/`rust`/`emscripten` compiler or other heavy dependencies + such as `pandas` + [issues]: https://github.com/deathbeds/jupyak/issues [pulls]: https://github.com/deathbeds/jupyak/pulls diff --git a/docs/.readthedocs.yaml b/docs/.readthedocs.yaml index d1b0f3e..74e3553 100644 --- a/docs/.readthedocs.yaml +++ b/docs/.readthedocs.yaml @@ -8,7 +8,7 @@ build: pre_build: - git config --global user.email "jupyak@example.com" - git config --global user.name "jupyak" - - JPYK_ALLOW_NO_CONFIG=1 doit bootstrap || echo 'ok' + - JPYK_ALLOW_NO_CONFIG=1 doit bootstrap || echo 'ok' # check out everything - JPYK_LOG_NAME=git doit shave:git:* || echo 'ok' # prepare the work conda env @@ -19,7 +19,7 @@ build: - JPYK_LOG_NAME=shave doit shave || echo 'ok' - JPYK_LOG_NAME=shave2 doit shave || echo 'ok' - doit self:docs:deploy:work || echo 'ok' - - JPYK_ALLOW_NO_CONFIG=1 doit self:docs:graph || echo 'ok' + - JPYK_ALLOW_NO_CONFIG=1 doit self:docs:graph || echo 'ok' sphinx: builder: html diff --git a/docs/_static/css/theme.css b/docs/_static/css/theme.css index 5a09d71..f01383b 100644 --- a/docs/_static/css/theme.css +++ b/docs/_static/css/theme.css @@ -4,11 +4,6 @@ --jpyk-color-jupyter-orange: #f37726; } -html[data-theme="light"], -html[data-theme="dark"] { - --pst-color-primary: #9556cf; -} - html[data-theme="dark"] { --pst-color-border: #444; } @@ -106,7 +101,7 @@ table.jsonschema h2 { #task-graph-legend { position: fixed; - top: 100px; + bottom: 100px; right: 0; pointer-events: none; opacity: 0.75; @@ -118,7 +113,65 @@ input[id*="svg-zoom-2"]:checked ~ svg { width: 200%; transition: width 0.5s;} input[id*="svg-zoom-4"]:checked ~ svg { width: 400%; transition: width 0.5s;} input[id*="svg-zoom-8"]:checked ~ svg { width: 800%; transition: width 0.5s;} -/* work */ +/* form sidebar*/ + +form#propose button { + width: 100%; + margin: 0.5rem 0 1rem; +} +form#propose textarea { + width: 100%; + min-height: 10rem; +} +form#propose textarea:placeholder-shown ~ button { + display: none; +} +/* form main */ +.show-repo-label { + padding-right: 1rem; +} + +form#new input[type="text"] { + width: 100%; +} + +form#new select { + width: 100%; + height: 2rem; +} + +form#new code { + text-wrap: nowrap; +} + +form#new input[id*="merge_with"] { + word-wrap: break-word; + word-break: break-all; +} + +form#new table { + width: 100%; +} + +form#new table label { + display: block; +} + +form#new input[pattern]:invalid { + border: solid 1px var(--jpyk-color-jupyter-orange); + color: solid 1px var(--jpyk-color-jupyter-orange); +} + +form#new input[pattern]:not(:invalid) ~ label { + display: none; +} + +form#new th, +form#new td { + vertical-align: top; +} + +/* work sidebar */ .work-sidebar { border: solid 2px var(--jpyk-color-wasm-purple); padding: 0; diff --git a/docs/_templates/demo.html b/docs/_templates/demo.html index 20bf2ac..ee463b0 100644 --- a/docs/_templates/demo.html +++ b/docs/_templates/demo.html @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/docs/index.ipynb b/docs/index.ipynb new file mode 100644 index 0000000..b37eb36 --- /dev/null +++ b/docs/index.ipynb @@ -0,0 +1,50 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "48401394-c158-4e89-86ff-7bd5919eab9a", + "metadata": {}, + "source": [ + "```{include} ../README.md\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "e495cdca-bdc6-4ab4-9a42-69d3c7d9cde6", + "metadata": {}, + "source": [ + "## documentation contents\n", + "\n", + "```{toctree}\n", + ":maxdepth: 2\n", + "new\n", + "graph\n", + "work\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 63f8422..0000000 --- a/docs/index.md +++ /dev/null @@ -1,11 +0,0 @@ -```{include} ../README.md - -``` - -## documentation contents - -```{toctree} -:maxdepth: 2 -graph -work -``` diff --git a/docs/new.md b/docs/new.md new file mode 100644 index 0000000..7514643 --- /dev/null +++ b/docs/new.md @@ -0,0 +1,1477 @@ +# request a preview site + +> Make selections below for the most common configuration options, then use the +> _start pull request_ form to be redirected to GitHub. +> A large number of other configuration options are _possible_, but are not yet fully described. + + +
+ +
+

customize GitHub checkout

+ +
+All of the repositories below will be checked out at the current main branch as a baseline. +Check one or more repos to provide use a differnt branch, PR, or tag, or provide one or more PRs to +merge with it, and/or customize the merge strategy and options to work around merge conflicts. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
repobaselinemerge withmerge strategymerge options
+GitHub repository to check out and build +
+starting point GitHub reference +
+optional space-delimted list of references to merge into the baseline +
+merge strategy +
+additional space-delimeted -X options to pass to git merge +
https://github.com/ipython/traitlets/ + + + + + + + + + +
https://github.com/ipython/comm/ + + + + + + + + + +
https://github.com/jupyterlab/lumino/ + + + + + + + + + +
https://github.com/jupyter-server/jupyter_server_terminals/ + + + + + + + + + +
https://github.com/jupyter/jupyter_core/ + + + + + + + + + +
https://github.com/jupyter/jupyter_events/ + + + + + + + + + +
https://github.com/ipython/ipython/ + + + + + + + + + +
https://github.com/jupyter/jupyter_client/ + + + + + + + + + +
https://github.com/jupyter/nbformat/ + + + + + + + + + +
https://github.com/jupyter/nbconvert/ + + + + + + + + + +
https://github.com/ipython/ipykernel/ + + + + + + + + + +
https://github.com/jupyter/nbclient/ + + + + + + + + + +
https://github.com/jupyter-server/jupyter_server/ + + + + + + + + + +
https://github.com/jupyter/notebook_shim/ + + + + + + + + + +
https://github.com/jupyterlab/jupyterlab_server/ + + + + + + + + + +
https://github.com/jupyterlab/jupyterlab/ + + + + + + + + + +
https://github.com/jupyter/notebook/ + + + + + + + + + +
https://github.com/jupyter-widgets/ipywidgets/ + + + + + + + + + +
https://github.com/jupyterlite/jupyterlite/ + + + + + + + + + +
https://github.com/jupyterlite/pyodide-kernel/ + + + + + + + + + +
+ +
+customize JupyterLite + +
+If given, an optional gist will be cloned +to provide the content of the JupyterLite site. +If the gist contains jupyter_lite_config.json and/or jupyter-lite.json, +this will be merged into the generated +configuration +of jupyter lite build and the runtime application. +
+ + + +
+ + + +
+
+ +start pull request +
+
+
+ + + +
+ +
+
+
+ + diff --git a/docs/work.ipynb b/docs/work.ipynb index 717267e..31f991b 100644 --- a/docs/work.ipynb +++ b/docs/work.ipynb @@ -124,10 +124,10 @@ " commit = \"\"\n", " cols = f\"\"\"\n", " \n", - " {last}\n", + " {last}\n", " \n", " \n", - " {commit[:7]}\n", + " {commit[:7]}\n", " \n", " \n", " {log}\n", @@ -159,10 +159,6 @@ "source": [ "YAK_TMPL = jinja2.Template(\n", " \"\"\"\n", - "

{{ yak.pr.title }}

\n", - "\n", - "{{ md2h(yak.pr.description) }}\n", - "\n", "

repos

\n", "\n", "\n", @@ -188,11 +184,11 @@ " {% set attrs = 'rowspan=\"' ~ (gh.merge_with | length) ~ '\"' %}\n", " {% endif %}\n", " \n", - " {{ ref(gh.baseline, r, attrs=attrs) -}}\n", + " {{ ref(gh.url ~ '/' ~ gh.baseline, r, attrs=attrs) -}}\n", " {% for m in gh.merge_with %}\n", - " {{ ref(m, r, loop=loop) }}\n", + " {{ ref(gh.url ~ '/' ~ m, r, loop=loop) }}\n", " {%- endfor -%}\n", " \n", " {% endfor %}\n", diff --git a/examples/some-prs/jupyak_config.toml b/examples/some-prs/jupyak_config.toml index f8da0fb..292ae54 100644 --- a/examples/some-prs/jupyak_config.toml +++ b/examples/some-prs/jupyak_config.toml @@ -1,24 +1,14 @@ -[pr] -title = "jupyak demo 2023-11-29" -description = """ -A demo of jupyak. -""" - [lite] -gist = "https://gist.github.com/bollwyvl/c9efdd860fe031887c948ecc6cf27a5a" +gist = "c9efdd860fe031887c948ecc6cf27a5a" [repos] -lumino.github.merge_with = [ - "https://github.com/jupyterlab/lumino/pull/655" -] -ipywidgets.github.merge_with = [ - "https://github.com/jupyter-widgets/ipywidgets/pull/3847" -] +lumino.github.merge_with = ["pull/655"] +ipywidgets.github.merge_with = ["pull/3847"] [repos.jupyterlab.github] merge_strategy = "ort" merge_options = ["theirs"] merge_with = [ - "https://github.com/jupyterlab/jupyterlab/pull/15443", - "https://github.com/jupyterlab/jupyterlab/pull/15347" + "pull/15443", + "pull/15347" ] diff --git a/src/jupyak/__init__.py b/src/jupyak/__init__.py index f692816..bde485c 100644 --- a/src/jupyak/__init__.py +++ b/src/jupyak/__init__.py @@ -1,6 +1,2 @@ -__all__ = ["__version__", "_STDOUT", "_STDERR"] +__all__ = ["__version__"] __version__ = __import__("importlib.metadata").metadata.version("jupyak") - - -_STDOUT = None -_STDERR = None diff --git a/src/jupyak/tasks/_actions.ipynb b/src/jupyak/tasks/_actions.ipynb index 52526fe..2035804 100644 --- a/src/jupyak/tasks/_actions.ipynb +++ b/src/jupyak/tasks/_actions.ipynb @@ -272,7 +272,6 @@ "outputs": [], "source": [ "def replace_between(src: Path, dest: Path, delimiter: str) -> Boolish:\n", - " print(src, dest, delimiter)\n", " src_text = src.read_text(encoding=\"utf-8\")\n", " dest_text = dest.read_text(encoding=\"utf-8\")\n", " src_chunks = src_text.split(delimiter)\n", diff --git a/src/jupyak/tasks/_form.ipynb b/src/jupyak/tasks/_form.ipynb new file mode 100644 index 0000000..5188ba9 --- /dev/null +++ b/src/jupyak/tasks/_form.ipynb @@ -0,0 +1,345 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ab2ac71e-feb4-4264-9ee1-067476c2d268", + "metadata": {}, + "source": [ + "# form\n", + "\n", + "> this builds up an `.md` with a lot of nastiness to get to an HTML form to propose a new config file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab905463-34e2-452a-bb89-33d0760adcd5", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "from pathlib import Path\n", + "\n", + "import importnb\n", + "import IPython\n", + "import jinja2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d0b77f5-9f98-4c61-835e-94a4664e82fa", + "metadata": {}, + "outputs": [], + "source": [ + "URL = \"https://github.com/deathbeds/jupyak/new/main\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22fabc86-2a1d-44d8-b49d-46c653521bc1", + "metadata": {}, + "outputs": [], + "source": [ + "with importnb.Notebook():\n", + " from jupyak.tasks import load_tasks\n", + " from jupyak.tasks._yak import Yak" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad752a5e-4e9b-4254-8a9f-209f40a3fd60", + "metadata": {}, + "outputs": [], + "source": [ + "def load_yak():\n", + " load_tasks()\n", + " return Yak({\"repos\": {}})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38188d04-18b6-4769-86e6-940814116f71", + "metadata": {}, + "outputs": [], + "source": [ + "REPOS_TMPL = r\"\"\"\n", + "
\n", + "

customize GitHub checkout

\n", + "\n", + "
\n", + "All of the repositories below will be checked out at the current main branch as a baseline.\n", + "Check one or more repos to provide use a differnt branch, PR, or tag, or provide one or more PRs to\n", + "merge with it, and/or customize the merge strategy and options to work around merge conflicts.\n", + "
\n", + "\n", + "{% for name in yak.repos %}\n", + " \n", + " \n", + "{% endfor %}\n", + "
\n", - " {{r.name}}\n", + " {{r.name}}\n", "
\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " {% for name, repo in yak.repos.items() %}\n", + " {% set stem = \"repos|\" ~ name ~ \"|github\" %}\n", + " {% set id_stem = \"repos-\" ~ name ~ \"-github\" %}\n", + " {% set gh = repo.github %}\n", + " {% set gh_pattern = \"(tree/[^s]+|pull/\\d+|releases/tag/[^s]+)\" %}\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " {% endfor %}\n", + " \n", + "
repobaselinemerge withmerge strategymerge options
\n", + " GitHub repository to check out and build\n", + "
\n", + " starting point GitHub reference\n", + "
\n", + " optional space-delimted list of references to merge into the baseline\n", + "
\n", + " merge strategy\n", + "
\n", + " additional space-delimeted -X options to pass to git merge\n", + "
{{ gh.url }}/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "895ceb9c-89e1-420b-b98a-470f12c6c23b", + "metadata": {}, + "outputs": [], + "source": [ + "def make_repos_form(yak: Yak):\n", + " return [jinja2.Template(REPOS_TMPL).render(yak=yak)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eaaeb682-52b5-4136-ad51-990d0398fd74", + "metadata": {}, + "outputs": [], + "source": [ + "REPO_STYLE_TEMPL = \"\"\"\n", + "{% for name in yak.repos %}\n", + "#show-repo-{{ name }}:not(:checked) ~ table tbody #repo-{{ name }} {\n", + " display: none;\n", + "}\n", + "{% endfor %}\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43bf62e4-30bf-40d4-a0bb-3459fab2155a", + "metadata": {}, + "outputs": [], + "source": [ + "def make_repos_style(yak: Yak):\n", + " return [jinja2.Template(REPO_STYLE_TEMPL).render(yak=yak)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80c59837-bc25-4571-9c28-bcb61dac930e", + "metadata": {}, + "outputs": [], + "source": [ + "LITE_TMPL = r\"\"\"\n", + "
\n", + "customize JupyterLite\n", + "\n", + "
\n", + "If given, an optional gist will be cloned\n", + "to provide the content of the JupyterLite site.\n", + "If the gist contains jupyter_lite_config.json and/or jupyter-lite.json,\n", + "this will be merged into the generated\n", + "configuration\n", + "of jupyter lite build and the runtime application.\n", + "
\n", + "\n", + "\n", + "\n", + "
\n", + "\"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b128fc0-36ad-463d-a1ff-8bfe0e3a4ffe", + "metadata": {}, + "outputs": [], + "source": [ + "def make_lite_form(yak: Yak):\n", + " return [jinja2.Template(LITE_TMPL).render(yak=yak)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78413a7e-6db6-49d2-9b00-59028d7b86a1", + "metadata": {}, + "outputs": [], + "source": [ + "def make_form():\n", + " yak = load_yak()\n", + " chunks = []\n", + " style_chunks = []\n", + " script_chunks = []\n", + " for trait_name in [\"repos\", \"lite\"]:\n", + " value = yak.trait_values()[trait_name]\n", + " maker = globals().get(f\"make_{trait_name}_form\")\n", + " if maker:\n", + " chunks += maker(yak)\n", + " styler = globals().get(f\"make_{trait_name}_style\")\n", + " if styler:\n", + " style_chunks += styler(yak)\n", + "\n", + " return [\n", + " \"\",\n", + " \"\"\"
\"\"\",\n", + " *chunks,\n", + " \"
\",\n", + " *script_chunks,\n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09af8cb7-ced6-4d2a-b170-42c374d75a0d", + "metadata": {}, + "outputs": [], + "source": [ + "def write_form(dest: Path, **options):\n", + " dest.parent.mkdir(parents=True, exist_ok=True)\n", + " final_tmpl = dest.parent / \"_templates/new-form.html\"\n", + " chunks = [\n", + " \"# request a preview site\",\n", + " \"\",\n", + " \"> Make selections below for the most common configuration options, then use the \",\n", + " \"> _start pull request_ form to be redirected to GitHub.\",\n", + " \"> A large number of other configuration options are _possible_, but are not yet fully described.\",\n", + " \"\",\n", + " *make_form(),\n", + " final_tmpl.read_text(encoding=\"utf-8\"),\n", + " ]\n", + " txt = re.sub(r\"^ +\", \"\", \"\\n\".join(chunks), flags=re.M)\n", + " dest.write_text(txt, encoding=\"utf-8\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ac0c6c3-a69b-4e5e-bf55-1dcc2449f283", + "metadata": {}, + "outputs": [], + "source": [ + "if __name__ == \"__main__\":\n", + " IPython.display.display(IPython.display.HTML(\"\\n\".join(make_form())))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/jupyak/tasks/_git.ipynb b/src/jupyak/tasks/_git.ipynb index 00bbb52..abdb961 100644 --- a/src/jupyak/tasks/_git.ipynb +++ b/src/jupyak/tasks/_git.ipynb @@ -67,6 +67,7 @@ " yield from init_task(repo.name, gh.url, work_path)\n", "\n", " local_refs, ref_paths, tasks = fetch_tasks(\n", + " base_url=gh.url,\n", " name=repo.name,\n", " urls=[gh.baseline, *gh.merge_with],\n", " work_path=work_path,\n", @@ -131,6 +132,48 @@ " )" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "c66c8338-3e71-4f5b-a2a8-dbfd14befcf4", + "metadata": {}, + "outputs": [], + "source": [ + "def fetch_tasks(\n", + " name: str,\n", + " urls: str,\n", + " work_path: Path,\n", + " file_dep: list[Path],\n", + " base_url: str | None = None,\n", + "):\n", + " in_repo = {\"cwd\": work_path}\n", + " local_refs = []\n", + " ref_paths = []\n", + " tasks = []\n", + " targets = []\n", + " file_dep += [work_path / W.GITCONFIG]\n", + " fetch = [\"fetch\", \"--no-tags\"]\n", + "\n", + " if len(urls) == 1:\n", + " fetch += [\"--depth=1\"]\n", + "\n", + " for url in urls:\n", + " remote_ref, local_ref, target = _url_to_refs_and_target(url, work_path)\n", + " task = dict(\n", + " name=f\"{name}:fetch:{local_ref}\",\n", + " doc=f\"> fetch {name} at `{remote_ref}` to `{local_ref}`\",\n", + " actions=[\n", + " A.git([*fetch, \"origin\", f\"{remote_ref}:{local_ref}\"], in_repo),\n", + " ],\n", + " file_dep=file_dep,\n", + " targets=[target],\n", + " )\n", + " tasks += [task]\n", + " ref_paths += [target]\n", + " local_refs += [local_ref]\n", + " return local_refs, ref_paths, tasks" + ] + }, { "cell_type": "code", "execution_count": null, @@ -168,46 +211,10 @@ " actions=actions,\n", " file_dep=file_dep,\n", " targets=targets,\n", - " teardown=[A.git([\"git\", \"diff\"], in_repo)],\n", + " teardown=[A.git([\"diff\"], in_repo)],\n", " )" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "c66c8338-3e71-4f5b-a2a8-dbfd14befcf4", - "metadata": {}, - "outputs": [], - "source": [ - "def fetch_tasks(name: str, urls: str, work_path: Path, file_dep: list[Path]):\n", - " in_repo = {\"cwd\": work_path}\n", - " local_refs = []\n", - " ref_paths = []\n", - " tasks = []\n", - " targets = []\n", - " file_dep += [work_path / W.GITCONFIG]\n", - " fetch = [\"fetch\", \"--no-tags\"]\n", - "\n", - " if len(urls) == 1:\n", - " fetch += [\"--depth=1\"]\n", - "\n", - " for url in urls:\n", - " remote_ref, local_ref, target = _url_to_refs_and_target(url, work_path)\n", - " task = dict(\n", - " name=f\"{name}:fetch:{local_ref}\",\n", - " doc=f\"> fetch {name} at `{remote_ref}` to `{local_ref}`\",\n", - " actions=[\n", - " A.git([*fetch, \"origin\", f\"{remote_ref}:{local_ref}\"], in_repo),\n", - " ],\n", - " file_dep=file_dep,\n", - " targets=[target],\n", - " )\n", - " tasks += [task]\n", - " ref_paths += [target]\n", - " local_refs += [local_ref]\n", - " return local_refs, ref_paths, tasks" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/src/jupyak/tasks/_graph.ipynb b/src/jupyak/tasks/_graph.ipynb index 59e0680..7b3eb79 100644 --- a/src/jupyak/tasks/_graph.ipynb +++ b/src/jupyak/tasks/_graph.ipynb @@ -56,11 +56,7 @@ " \"run\": \"💭\",\n", " \"error\": \"❌\",\n", "}\n", - "GRAPH_OPTIONS = {\n", - " \"flowchart\": {\n", - " \"defaultRenderer\": \"elk\",\n", - " },\n", - "}\n", + "GRAPH_OPTIONS = {\"flowchart\": {\"defaultRenderer\": \"elk\"}}\n", "BLURB = \"\"\"\n", "The graph below shows the current state of the executed graph of tasks to go from\n", "git clone to jupyter lite build.\n", @@ -154,25 +150,23 @@ "metadata": {}, "outputs": [], "source": [ - "def build_legend():\n", - " return \"\\n\".join(\n", - " [\n", - " *mermaid_preamble(\n", - " direction=\"TB\",\n", - " title=\"Legend\",\n", - " description=\"Examples of graph node styles used in the full task diagram\",\n", - " ),\n", - " \"subgraph task types\",\n", - " *[\n", - " f\"\"\"{cls}{NODE_SHAPES[cls][0]}{cls}{NODE_SHAPES[cls][1]}:::{cls}\"\"\"\n", - " for cls in NODE_STYLES\n", - " ],\n", - " \"end\",\n", - " \"subgraph task status\",\n", - " *[f\"\"\"{status}[\"{emoji} {status}\"]\"\"\" for status, emoji in STATUS.items()],\n", - " \"end\",\n", + "def build_legend() -> list[str]:\n", + " return [\n", + " *mermaid_preamble(\n", + " direction=\"TB\",\n", + " title=\"Legend\",\n", + " description=\"Examples of graph node styles used in the full task diagram\",\n", + " ),\n", + " \"subgraph task types\",\n", + " *[\n", + " f\"\"\"{cls}{NODE_SHAPES[cls][0]}{cls}{NODE_SHAPES[cls][1]}:::{cls}\"\"\"\n", + " for cls in NODE_STYLES\n", " ],\n", - " )" + " \"end\",\n", + " \"subgraph task status\",\n", + " *[f\"\"\"{status}[\"{emoji} {status}\"]\"\"\" for status, emoji in STATUS.items()],\n", + " \"end\",\n", + " ]" ] }, { @@ -182,7 +176,7 @@ "metadata": {}, "outputs": [], "source": [ - "def build_graph(direction=\"LR\", title=None, description=None):\n", + "def build_graph(direction=\"LR\", title=None, description=None) -> list[str]:\n", " title = title or \"Task Graph\"\n", " description = \"A flow chart of the jupyak task graph\"\n", " tasks, tracker = filtered_tasks()\n", @@ -226,7 +220,7 @@ " mmd += [f\"\"\" {task.name}{shape}:::{cls}\"\"\"]\n", " mmd += [\"end\"]\n", "\n", - " return \"\\n\".join(mmd)" + " return mmd" ] }, { @@ -236,18 +230,17 @@ "metadata": {}, "outputs": [], "source": [ - "def div_wrappers(text: str, dom_id=None, show_zoom=False):\n", + "def div_wrappers(chunks: list[str], dom_id=None, show_zoom=False, legend=False) -> str:\n", " dom_id = dom_id or f\"id-{uuid4()}\"\n", - " return \"\\n\".join(\n", - " [\n", - " f\"\"\"
\"\"\",\n", - " zoom(f\"{dom_id}-\") if show_zoom else \"\",\n", - " \"\"\"
\"\"\",\n", - " text,\n", - " \"
\",\n", - " \"
\",\n", - " ],\n", - " )" + " chunks = [\n", + " f\"\"\"
\"\"\",\n", + " zoom(f\"{dom_id}-\") if show_zoom else \"\",\n", + " \"\"\"
\"\"\",\n", + " *chunks,\n", + " \"
\",\n", + " \"
\",\n", + " ]\n", + " return \"\\n\".join(chunks)" ] }, { @@ -277,11 +270,7 @@ "def write_graph(dest: Path, **options):\n", " dest.parent.mkdir(parents=True, exist_ok=True)\n", " chunks = [\n", - " \"---\",\n", - " \"html_theme.sidebar_secondary.remove: true\",\n", - " \"---\",\n", - " \"\",\n", - " \"# task graph\",\n", + " \"# graph\",\n", " \"\",\n", " BLURB,\n", " \"\",\n", diff --git a/src/jupyak/tasks/_self.ipynb b/src/jupyak/tasks/_self.ipynb index 122505b..7d25cdf 100644 --- a/src/jupyak/tasks/_self.ipynb +++ b/src/jupyak/tasks/_self.ipynb @@ -30,6 +30,7 @@ "\n", "with importnb.Notebook():\n", " from jupyak.tasks import _actions as A\n", + " from jupyak.tasks import _form as F\n", " from jupyak.tasks import _graph as G\n", " from jupyak.tasks import _well_known as W\n", "try:\n", @@ -181,6 +182,7 @@ " *ALL_DOCS_PY,\n", " *ALL_DOCS_MD,\n", " *ALL_IPYNB,\n", + " *ALL_DOCS_TEMPLATES,\n", " DOCS_GRAPH,\n", " PPT,\n", " README,\n", @@ -245,12 +247,21 @@ " )\n", "\n", " yield dict(\n", + " name=\"form\",\n", + " doc=\"> build a form for the config\",\n", + " actions=[(F.write_form, [DOCS_FORM])],\n", + " file_dep=[*ALL_IPYNB],\n", + " targets=[DOCS_FORM],\n", + " )\n", + "\n", + " yield dict(\n", " name=\"graph\",\n", " doc=\"> build a graph of the tasks\",\n", " actions=[(G.write_graph, [DOCS_GRAPH])],\n", " file_dep=[*ALL_IPYNB],\n", " targets=[DOCS_GRAPH],\n", " )\n", + "\n", " yield dict(\n", " name=\"sphinx\",\n", " doc=\"> build documentation with sphinx\",\n", @@ -446,17 +457,23 @@ "DOCS_IMG = DOCS_STATIC / \"img\"\n", "DOCS_BUILD = BUILD / \"docs\"\n", "DOCS_BUILDINFO = DOCS_BUILD / \".buildinfo\"\n", - "DOCS_FORM = DOCS_STATIC / \"new.html\"\n", + "DOCS_FORM = DOCS / \"new.md\"\n", "DOCS_ICON = DOCS_IMG / \"logo.svg\"\n", "DOCS_FAVICON = DOCS_IMG / \"favicon.ico\"\n", "DOCS_GRAPH = DOCS / \"graph.md\"\n", + "DOCS_TEMPLATES = DOCS / \"_templates\"\n", "DOCS_CSS = DOCS_STATIC / \"css\"\n", "DOCS_STATIC_WORK = DOCS_STATIC / \"work\"\n", "DOCS_STATIC_WORK_FAVICON = DOCS_STATIC_WORK / \"lite/lab\" / DOCS_FAVICON.name\n", - "ALL_DOCS_STATIC = sorted([*DOCS_CSS.rglob(\"*.*\"), *DOCS_IMG.rglob(\"*.*\")])\n", + "ALL_DOCS_STATIC = [\n", + " p\n", + " for p in [*DOCS_CSS.rglob(\"*.*\"), *DOCS_IMG.rglob(\"*.*\")]\n", + " if \"ipynb_checkpoints\" not in str(p)\n", + "]\n", "ALL_DOCS_PY = sorted(DOCS.rglob(\"*.py\"))\n", "ALL_DOCS_SVG = sorted(DOCS_IMG.glob(\"*.svg\"))\n", - "ALL_DOCS_MD = [DOCS_GRAPH, *sorted(DOCS.rglob(\"*.md\"))]" + "ALL_DOCS_MD = [DOCS_GRAPH, *sorted(DOCS.rglob(\"*.md\"))]\n", + "ALL_DOCS_TEMPLATES = [*DOCS_TEMPLATES.glob(\"*.html\")]" ] }, { diff --git a/src/jupyak/tasks/_yak.ipynb b/src/jupyak/tasks/_yak.ipynb index 5d2fc6c..4e9fa4b 100644 --- a/src/jupyak/tasks/_yak.ipynb +++ b/src/jupyak/tasks/_yak.ipynb @@ -155,27 +155,6 @@ " self.error(obj, value)" ] }, - { - "cell_type": "markdown", - "id": "c2fec9ac-5d20-4a94-a268-799cff6f093c", - "metadata": {}, - "source": [ - "The pull request metadata." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e8c5526-d2c2-4f41-a64f-35c4f56cda4c", - "metadata": {}, - "outputs": [], - "source": [ - "class PullRequest(T.HasTraits):\n", - " parent = T.Instance(\"jupyak.tasks._yak.Yak\")\n", - " title = T.Unicode(\"Untitled PR\")\n", - " description = T.Unicode(\"An undescribed PR\")" - ] - }, { "cell_type": "markdown", "id": "f5b71c9e-58f0-40f8-88dc-a4a58884d57d", @@ -338,6 +317,7 @@ "source": [ "class GitHub(T.HasTraits):\n", " _re_github = r\"https://github\\.com/.*\"\n", + " _re_mergeable = r\"(tree/[^s]+|pull/\\d+|releases/tag/[^s]+)\"\n", "\n", " parent = T.Instance(\"jupyak.tasks._yak.Yak\")\n", " url = UnicodeWithRegex(\n", @@ -346,13 +326,13 @@ " allow_none=False,\n", " )\n", " baseline = UnicodeWithRegex(\n", - " pattern=_re_github,\n", + " pattern=_re_mergeable,\n", " help=\"the URL of the baseline HEAD\",\n", " allow_none=False,\n", " )\n", " merge_with = TypedTuple(\n", " UnicodeWithRegex(\n", - " pattern=_re_github,\n", + " pattern=_re_mergeable,\n", " allow_none=False,\n", " ),\n", " help=\"an optional, ordered list of branches to merge into the `baseline`\",\n", @@ -366,7 +346,7 @@ "\n", " @T.default(\"baseline\")\n", " def _default_baseline(self):\n", - " return f\"{self.url}/tree/main\"" + " return \"tree/main\"" ] }, { @@ -599,7 +579,7 @@ " parent = T.Instance(\"jupyak.tasks._yak.Yak\")\n", "\n", " gist = T.Unicode(\n", - " help=\"a URL for a gist on GitHub to use as jupyterlite contents and config\",\n", + " help=\"a gist ID on GitHub to use as JupyterLite contents and config\",\n", " allow_none=True,\n", " )\n", "\n", @@ -646,7 +626,6 @@ "class Yak(T.HasTraits):\n", " conf_dir = T.Unicode().tag(sync=True)\n", " work_dir = T.Unicode().tag(sync=True)\n", - " pr = InstanceDict(PullRequest)\n", " repos = T.Dict(value_trait=InstanceDict(Repo))\n", " env = InstanceDict(CondaEnv)\n", " issue = T.Dict()\n", @@ -658,13 +637,12 @@ " issue = issue or Yak.find_config(conf_dir=kwargs.get(\"conf_dir\"))\n", "\n", " kwargs[\"issue\"] = deepcopy(\n", - " nested_update({\"repos\": {}, \"pr\": {}, \"env\": {}, \"lite\": {}}, issue)\n", + " nested_update({\"repos\": {}, \"env\": {}, \"lite\": {}}, issue)\n", " )\n", - " kwargs[\"pr\"] = kwargs[\"issue\"][\"pr\"]\n", " kwargs[\"env\"] = kwargs[\"issue\"][\"env\"]\n", " kwargs[\"lite\"] = kwargs[\"issue\"][\"lite\"]\n", " super().__init__(**kwargs)\n", - " for child in [self.pr, self.env, self.lite]:\n", + " for child in [self.env, self.lite]:\n", " child.parent = self\n", " for name, factory in self._sorted_default_repos():\n", " defaults = deepcopy(factory(self))\n", @@ -769,14 +747,6 @@ " issue = {}\n", " return issue" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bc223117-4230-4dbb-94be-751543220530", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": {