Skip to content

Conversation

@ghukill
Copy link
Contributor

@ghukill ghukill commented Aug 15, 2025

Purpose and background context

This PR is the first pass at a CLI capable of preparing a Marimo notebook for running, then launching it either in run or edit mode for interaction. In this way, this CLI will be running as long as the notebook is. Perhaps even more than a "launcher" it's a "wrapper" of sorts.

Apologies for a larger PR. This felt very difficult to chunk, given how much it changed from moment to moment as the pieces were falling into place. For review, it's recommend to focus on these chunks:

  1. the CLI itself
  2. the Makefile commands for testing launching of test notebooks
  3. the Dockerfile to convince yourself there is no notebook here 😎, just a CLI eager to clone a repository and launch it

Overall flow of this CLI:

  1. Invoke launcher run with arguments
  2. Unless a notebook is mounted into the container (rare, for dev work mostly) it will expect a NOTEBOOK_REPOSITORY env var where it goes out and clones a repository
  3. It will then expect a notebook.py in the root of the repository, or you can provide runtime arguments indicating where that is
  4. Lastly, it will launch the notebook and expose the default port 2718 (again, configurable)

Something that may not be immediately obvious: there is no ECS task/service associated with this app / ECR image! This will take a little getting used to, but think of this application as a generic notebook launcher. When we do finally define an ECS task/service, it will oriented around an actual notebook repository (e.g.https://github.com/MITLibraries/marimo-helloworld). Invoking that task/service will run this CLI / ECR image, but passing NOTEBOOK_REPOSITORY env var to this CLI to inform it where to clone the repo from.

As such, you'll notice in testing below we interact with this more like a true Docker image than an ECS task/service we can run.

How can a reviewer manually see the effects of these changes?

Run a tests fixture notebook with inline dependencies

1- Run the following Makefile command:

make cli-test-inline-run

Note a few things here:

  • the logged line ['uv', 'run', 'marimo', 'run', '--headless', '--host', '0.0.0.0', '--port', '2718', '--sandbox', '--no-token', 'notebook.py'] shows the exact command arguments used to launch the notebook
  • due to the --headless flag, you must manually navigate to http://0.0.0.0:2718
    • binding to 0.0.0.0 just means it will work on all network hosts
  • navigating to http://0.0.0.0:2718/ will show the running notebook, with some output that indicates all imports and dependencies are working

Run a notebook with external requirements dependencies file

1- Run the following Makefile command:

make cli-test-reqs-txt-run

The running notebook should look and feel mostly identical, but you can see the presence of the requirements file tests/fixtures/static_deps_reqs_txt/requirements.txt in the tests fixtures, and the arguments '--with-requirements', 'requirements.txt' in the logged command that was run.

Discussion

We don't currently have a Github repository that we can use to test the cloning of a notebook repository. The above examples use the --mount option.

But one can see that looking at the github repository cloning code that it's pretty straight-forward. We clone the repository and it becomes the root location, as if it existed already. We anticipate that most of our Marimo notebook repositories will have a notebook.py in the root of the repository and therefore won't even require a --path flag or NOTEBOOK_PATH env var. But, they can be as complex as needed, with utility files and dependencies as required.

Includes new or updated dependencies?

YES

Changes expectations for external applications?

YES

What are the relevant tickets?

Why these changes are being introduced:

This builds on the project scaffolding to provide a CLI capable
of preparing the notebook environment and running the notebook.

This does NOT yet contain much of any meaningful testing.

How this addresses that need:
* Establishes CLI command and signature
* Supports a pre-existing ("mounted") notebook location or
a git clone for a repository
* Prepares a full notebook path to run
* Prepares a marimo subprocess command that handles
inlined or external dependencies
* Whether inline or external dependencies, the process
is always isolated via `uv` given the `--sandbox` or
`--with-requirements` flags.

Side effects of this change:
* None

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/IN-1415
@coveralls
Copy link

coveralls commented Aug 18, 2025

Pull Request Test Coverage Report for Build 17051383608

Details

  • 24 of 65 (36.92%) changed or added relevant lines in 1 file are covered.
  • 3 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-43.2%) to 51.579%

Changes Missing Coverage Covered Lines Changed/Added Lines %
launcher/cli.py 24 65 36.92%
Files with Coverage Reduction New Missed Lines %
launcher/cli.py 3 39.47%
Totals Coverage Status
Change from base Build 16993776776: -43.2%
Covered Lines: 49
Relevant Lines: 95

💛 - Coveralls

@ghukill ghukill marked this pull request as ready for review August 18, 2025 16:05
@ghukill ghukill requested a review from a team August 18, 2025 16:05
@ehanson8 ehanson8 self-assigned this Aug 18, 2025
Copy link

@ehanson8 ehanson8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff, a few comments!

Why these changes are being introduced:

With the --no-token or --token flag, it was a bit confusing
and it did not allow for setting an explicit token for the noteobok.
We need to support setting the token such that we can know it
advance.

How this addresses that need:
* Changes the CLI arg to `--token:str|None` which when None
sets `--no-token` in marimo launch else sets the explicit token
passed

Side effects of this change:
* None

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/IN-1415
How this addresses that need:
* More explicit names for helper functions around establishing
notebook paths and cloning repositories
* Support checkout of a git branch on clone

Side effects of this change:
* None

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/IN-1415
@ghukill ghukill requested a review from ehanson8 August 18, 2025 20:34
Copy link

@ehanson8 ehanson8 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

Comment on lines +245 to +248
# set token if passed
if token:
cmd += ["--token", "--token-password", token]
else:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much cleaner, great work!

envvar="NOTEBOOK_REPOSITORY",
help="git repository URL containing the notebook (env: NOTEBOOK_REPOSITORY)",
)
@click.option(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good addition!

if mount:
p = Path(mount)
if not p.exists():
notebook_dir_path = Path(mount)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always appreciate explicit var names!

Comment on lines +167 to +169
notebook_dir_path = workdir / f"notebook-clone-{uuid.uuid4()}"

clone_notebook_repository(notebook_dir_path, repo, repo_branch)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful!

@ghukill ghukill merged commit cf4a593 into main Aug 19, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants