From 3c9ca4ac991e3d113e44a329619d935d4cabf37c Mon Sep 17 00:00:00 2001 From: Ian Thomas Date: Fri, 8 Aug 2025 14:06:52 +0100 Subject: [PATCH] Support creating a local JupyterLite deployment with git2cpp built from the local repo --- README.md | 8 +++++ lite-deploy/.gitignore | 6 ++++ lite-deploy/Makefile | 40 ++++++++++++++++++++++++ lite-deploy/README.md | 49 ++++++++++++++++++++++++++++++ lite-deploy/cockle-config-in.json | 19 ++++++++++++ lite-deploy/deploy-environment.yml | 14 +++++++++ lite-deploy/jupyter-lite.json | 6 ++++ lite-deploy/modify-recipe.py | 49 ++++++++++++++++++++++++++++++ 8 files changed, 191 insertions(+) create mode 100644 lite-deploy/.gitignore create mode 100644 lite-deploy/Makefile create mode 100644 lite-deploy/README.md create mode 100644 lite-deploy/cockle-config-in.json create mode 100644 lite-deploy/deploy-environment.yml create mode 100644 lite-deploy/jupyter-lite.json create mode 100644 lite-deploy/modify-recipe.py diff --git a/README.md b/README.md index 64c6743..ba29379 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,11 @@ The CLI is tested using `python`. From the top-level directory: ```bash pytest -v ``` + +# JupyterLite deployment + +The `lite-deploy` directory contains everything needed to build the local `git2cpp` source code as +an [Emscripten-forge](https://emscripten-forge.org/) package and create a local JupyterLite +deployment that can run it in a [terminal](https://github.com/jupyterlite/terminal). + +See the `README.md` in the `lite-deploy` directory for further details. diff --git a/lite-deploy/.gitignore b/lite-deploy/.gitignore new file mode 100644 index 0000000..4812692 --- /dev/null +++ b/lite-deploy/.gitignore @@ -0,0 +1,6 @@ +.cockle_temp/ +.jupyterlite.doit.db +cockle-config.json +cockle_wasm_env/ +dist/ +em-forge-recipes/ diff --git a/lite-deploy/Makefile b/lite-deploy/Makefile new file mode 100644 index 0000000..85226b0 --- /dev/null +++ b/lite-deploy/Makefile @@ -0,0 +1,40 @@ +default: all +.PHONY: all build-deployment build-recipe clean clean-deployment clean-package modify-recipe rebuild serve + +EM_FORGE_RECIPES_DIR = em-forge-recipes +GIT2CPP_RECIPE_DIR = recipes/recipes_emscripten/git2cpp +BUILT_PACKAGE_DIR = $(EM_FORGE_RECIPES_DIR)/output + +# Note removing the .git directory otherwise `git clean -fxd` will not remove the directory. +$(EM_FORGE_RECIPES_DIR): + git clone https://github.com/emscripten-forge/recipes --depth 1 $@ + rm -rf $@/.git + +modify-recipe: $(EM_FORGE_RECIPES_DIR) + python modify-recipe.py $(EM_FORGE_RECIPES_DIR)/$(GIT2CPP_RECIPE_DIR) + +build-recipe: modify-recipe + cd $(EM_FORGE_RECIPES_DIR) && pixi run build-emscripten-wasm32-pkg $(GIT2CPP_RECIPE_DIR) + +build-deployment: build-recipe + jupyter lite --version + COCKLE_WASM_EXTRA_CHANNEL=./$(BUILT_PACKAGE_DIR) jupyter lite build --output-dir dist + +all: build-deployment + +# Rebuild package and deployment after changing git2cpp source code. +rebuild: clean-package all + +# Run `make` before this. +serve: + npx static-handler dist + +clean: clean-deployment + rm -rf $(EM_FORGE_RECIPES_DIR) + +clean-package: + rm -rf $(BUILT_PACKAGE_DIR) cockle_wasm_env/ + +# Clean the deployment without removing the built package. +clean-deployment: + rm -rf .cockle_temp/ .jupyterlite.doit.db cockle-config.json cockle_wasm_env/ dist/ diff --git a/lite-deploy/README.md b/lite-deploy/README.md new file mode 100644 index 0000000..fe2e5f6 --- /dev/null +++ b/lite-deploy/README.md @@ -0,0 +1,49 @@ +# JupyterLite deployment + +This directory contains everything needed to build the local `git2cpp` source code as an +[Emscripten-forge](https://emscripten-forge.org/) package and create a local JupyterLite deployment +that can run it in a [terminal](https://github.com/jupyterlite/terminal). + +It works on Linux and macOS but not Windows. + +It uses a separate `micromamba` environment defined in `deploy-environment.yml`. To set this up use: +```bash +micromamba create -f deploy-environment.yml +micromamba activate git2cpp-deploy +``` + +Then to build `git2cpp` and create the JupyterLite deployment use: +```bash +make +``` + +This performs the following steps: + + 1. Clones the `emscripten-forge/recipes` repository. + 2. Modifies the `git2cpp` recipe (using `modify_recipe.py`) to build from the local `git2cpp` source code in `../src/`. + 3. Builds the package from the recipe using `pixi`. + 4. Builds the JupyterLite deployment in the `dist/` directory using the locally-built package. + +The built package will be in the `em-forge-recipes/output/emscripten-wasm32` directory with a name +something like `git2cpp-0.0.3-h2072262_3.tar.bz2`. If you compare this with the latest +Emscripten-forge package on `https://prefix.dev/channels/emscripten-forge-dev/packages/git2cpp`, +the local package should have the same version number and the build number should be one higher. + +To serve the JupyterLite deployment use: +```bash +make serve +``` +and open a web browser at the URL http://localhost:8080/. Start a terminal and run the +`cockle-config` command (typing `coc`, then the tab key then enter should suffice). Amongst the +displayed information should be the `git2cpp` package showing that it is from a local directory +such as `file:///something-or-other/git2cpp/lite-deploy/em-forge-recipes/output` rather than from +`prefix.dev` such as `https://repo.prefix.dev/emscripten-forge-dev`. + +After making changes to `git2cpp` source code, to rebuild the package and deployment use: +```bash +make rebuild +``` +and then re-serve using: +```bash +make serve +``` diff --git a/lite-deploy/cockle-config-in.json b/lite-deploy/cockle-config-in.json new file mode 100644 index 0000000..bbb59a1 --- /dev/null +++ b/lite-deploy/cockle-config-in.json @@ -0,0 +1,19 @@ +{ + "packages": { + "git2cpp": {}, + "nano": {}, + "tree": {}, + "vim": {} + }, + "aliases": { + "git": "git2cpp", + "vi": "vim" + }, + "environment": { + "GIT_CORS_PROXY": "https://corsproxy.io/?url=", + "GIT_AUTHOR_NAME": "Jane Doe", + "GIT_AUTHOR_EMAIL": "jane.doe@blabla.com", + "GIT_COMMITTER_NAME": "Jane Doe", + "GIT_COMMITTER_EMAIL": "jane.doe@blabla.com" + } +} diff --git a/lite-deploy/deploy-environment.yml b/lite-deploy/deploy-environment.yml new file mode 100644 index 0000000..5efa57d --- /dev/null +++ b/lite-deploy/deploy-environment.yml @@ -0,0 +1,14 @@ +name: git2cpp-deploy +channels: + - conda-forge +dependencies: + # To modify emscripten-forge recipe + - python + - pyyaml + # To build emscripten-forge recipe + - pixi + - rattler-build + # For JupyterLite deployment + - jupyter_server + - jupyterlite-terminal + - nodejs diff --git a/lite-deploy/jupyter-lite.json b/lite-deploy/jupyter-lite.json new file mode 100644 index 0000000..31aaf07 --- /dev/null +++ b/lite-deploy/jupyter-lite.json @@ -0,0 +1,6 @@ +{ + "jupyter-lite-schema-version": 0, + "jupyter-config-data": { + "terminalsAvailable": true + } +} diff --git a/lite-deploy/modify-recipe.py b/lite-deploy/modify-recipe.py new file mode 100644 index 0000000..3a1ecc6 --- /dev/null +++ b/lite-deploy/modify-recipe.py @@ -0,0 +1,49 @@ +# Modify the git2cpp emscripten-forge recipe to build from the local repo. +# This can be called repeatedly and will produce the same output. + +from pathlib import Path +import shutil +import sys +import yaml + + +def quit(msg): + print(msg) + exit(1) + +if len(sys.argv) < 2: + quit(f'Usage: {sys.argv[0]} ') + +input_dir = Path(sys.argv[1]) +if not input_dir.is_dir(): + quit(f'{input_dir} should exist and be a directory') + +input_filename = input_dir / 'recipe.yaml' +if not input_filename.is_file(): + quit(f'{input_filename} should exist and be a file') + +# If backup does not exist create it. +input_backup = input_dir / 'recipe_original.yaml' +backup_exists = input_backup.exists() +if not backup_exists: + shutil.copy(input_filename, input_backup) + +# Read and parse input backup file which is the original recipe file. +with open(input_backup) as f: + recipe = yaml.safe_load(f) + +build_number = recipe['build']['number'] +print(f' Changing build number from {build_number} to {build_number+1}') +recipe['build']['number'] = build_number+1 + +source = recipe['source'] +if not ('sha256' in source and 'url' in source): + raise RuntimeError('Expected recipe to have both a source sha256 and url') +del source['sha256'] +del source['url'] +print(' Changing source to point to local git2cpp repo') +source['path'] = '../../../../../' + +# Overwrite recipe file. +with open(input_filename, 'w') as f: + yaml.dump(recipe, f)