From 83014256e86da15602d6df65cbd6275a3a6be9ff Mon Sep 17 00:00:00 2001 From: M Atif Ali Date: Sun, 20 Apr 2025 09:56:58 +0000 Subject: [PATCH 1/4] feat: Add marimo terraform module Add a new terraform module for marimo - a reactive Python notebook that's reproducible, git-friendly, and deployable as scripts or apps. - Create marimo module with main.tf, run.sh, and README.md - Add marimo icon SVG - Configure coder_script and coder_app resources --- .icons/marimo.svg | 33 +++++++++++++++++++++ marimo/README.md | 23 +++++++++++++++ marimo/main.tf | 75 +++++++++++++++++++++++++++++++++++++++++++++++ marimo/run.sh | 57 +++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 .icons/marimo.svg create mode 100644 marimo/README.md create mode 100644 marimo/main.tf create mode 100755 marimo/run.sh diff --git a/.icons/marimo.svg b/.icons/marimo.svg new file mode 100644 index 00000000..4fddd3d6 --- /dev/null +++ b/.icons/marimo.svg @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/marimo/README.md b/marimo/README.md new file mode 100644 index 00000000..3fc84a74 --- /dev/null +++ b/marimo/README.md @@ -0,0 +1,23 @@ +--- +display_name: marimo +description: A module that adds marimo in your Coder template. +icon: ../.icons/marimo.svg +maintainer_github: coder +verified: true +tags: [marimo, python, notebook, reactive, helper, ide, web] +--- + +# marimo + +A module that adds [marimo](https://github.com/marimo-team/marimo) in your Coder template. + +marimo is a reactive Python notebook that's reproducible, git-friendly, and deployable as scripts or apps. + +```tf +module "marimo" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/modules/marimo/coder" + version = "1.0.0" + agent_id = coder_agent.example.id +} +``` \ No newline at end of file diff --git a/marimo/main.tf b/marimo/main.tf new file mode 100644 index 00000000..b03a028c --- /dev/null +++ b/marimo/main.tf @@ -0,0 +1,75 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.17" + } + } +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +# Add required variables for your modules and remove any unneeded variables +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "log_path" { + type = string + description = "The path to log marimo to." + default = "/tmp/marimo.log" +} + +variable "port" { + type = number + description = "The port to run marimo on." + default = 18888 +} + +variable "share" { + type = string + default = "owner" + validation { + condition = var.share == "owner" || var.share == "authenticated" || var.share == "public" + error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'." + } +} + +variable "subdomain" { + type = bool + description = "Determines whether marimo will be accessed via its own subdomain or whether it will be accessed via a path on Coder." + default = true +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." + default = null +} + +resource "coder_script" "marimo" { + agent_id = var.agent_id + display_name = "marimo" + icon = "/icon/marimo.svg" + script = templatefile("${path.module}/run.sh", { + LOG_PATH : var.log_path, + PORT : var.port + BASE_URL : var.subdomain ? "" : "/@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}/apps/marimo" + }) + run_on_start = true +} + +resource "coder_app" "marimo" { + agent_id = var.agent_id + slug = "marimo" # sync with the usage in URL + display_name = "marimo" + url = var.subdomain ? "http://localhost:${var.port}" : "http://localhost:${var.port}/@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}/apps/marimo" + icon = "/icon/marimo.svg" + subdomain = var.subdomain + share = var.share + order = var.order +} \ No newline at end of file diff --git a/marimo/run.sh b/marimo/run.sh new file mode 100755 index 00000000..59266495 --- /dev/null +++ b/marimo/run.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env sh +INSTALLER="" +check_available_installer() { + # check if pipx is installed + echo "Checking for a supported installer" + if command -v pipx > /dev/null 2>&1; then + echo "pipx is installed" + INSTALLER="pipx" + return + fi + # check if uv is installed + if command -v uv > /dev/null 2>&1; then + echo "uv is installed" + INSTALLER="uv" + return + fi + echo "No valid installer is not installed" + echo "Please install pipx or uv in your Dockerfile/VM image before running this script" + exit 1 +} + +if [ -n "${BASE_URL}" ]; then + BASE_URL_FLAG="--base-url=${BASE_URL}" +fi + +BOLD='\033[0;1m' + +# check if marimo is installed +if ! command -v marimo > /dev/null 2>&1; then + # install marimo + check_available_installer + printf "$${BOLD}Installing marimo!\n" + case $INSTALLER in + uv) + uv pip install -q marimo[recommended] \ + && printf "%s\n" "🥳 marimo has been installed" + MARIMOPATH="$HOME/.venv/bin/" + ;; + pipx) + pipx install marimo[recommended] \ + && printf "%s\n" "🥳 marimo has been installed" + MARIMOPATH="$HOME/.local/bin" + ;; + esac +else + printf "%s\n\n" "🥳 marimo is already installed" +fi + +printf "👷 Starting marimo in background..." +printf "check logs at ${LOG_PATH}" +$MARIMOPATH/marimo run \ + "$BASE_URL_FLAG" \ + --host="*" \ + --port="${PORT}" \ + --headless \ + --include-code \ + > "${LOG_PATH}" 2>&1 & \ No newline at end of file From d6f501de34a37ab7a87da104e8a59256dc871620 Mon Sep 17 00:00:00 2001 From: M Atif Ali Date: Sun, 20 Apr 2025 10:14:12 +0000 Subject: [PATCH 2/4] test: Add tests for marimo module --- marimo/main.test.ts | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 marimo/main.test.ts diff --git a/marimo/main.test.ts b/marimo/main.test.ts new file mode 100644 index 00000000..a87bfc16 --- /dev/null +++ b/marimo/main.test.ts @@ -0,0 +1,116 @@ +import { describe, expect, it } from "bun:test"; +import { + execContainer, + executeScriptInContainer, + findResourceInstance, + runContainer, + runTerraformApply, + runTerraformInit, + testRequiredVariables, + type TerraformState, +} from "../test"; + +// executes the coder script after installing pip +const executeScriptInContainerWithPipx = async ( + state: TerraformState, + image: string, + shell = "sh", +): Promise<{ + exitCode: number; + stdout: string[]; + stderr: string[]; +}> => { + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer(image); + const respPipx = await execContainer(id, [shell, "-c", "apk add pipx"]); + const resp = await execContainer(id, [shell, "-c", instance.script]); + const stdout = resp.stdout.trim().split("\n"); + const stderr = resp.stderr.trim().split("\n"); + return { + exitCode: resp.exitCode, + stdout, + stderr, + }; +}; + +// executes the coder script after installing uv +const executeScriptInContainerWithUv = async ( + state: TerraformState, + image: string, + shell = "sh", +): Promise<{ + exitCode: number; + stdout: string[]; + stderr: string[]; +}> => { + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer(image); + const respPipx = await execContainer(id, [ + shell, + "-c", + "apk --no-cache add uv gcc musl-dev linux-headers && uv venv", + ]); + const resp = await execContainer(id, [shell, "-c", instance.script]); + const stdout = resp.stdout.trim().split("\n"); + const stderr = resp.stderr.trim().split("\n"); + return { + exitCode: resp.exitCode, + stdout, + stderr, + }; +}; + +describe("marimo", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, { + agent_id: "foo", + }); + + it("fails without installers", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + }); + const output = await executeScriptInContainer(state, "alpine"); + expect(output.exitCode).toBe(1); + expect(output.stdout).toEqual([ + "Checking for a supported installer", + "No valid installer is not installed", + "Please install pipx or uv in your Dockerfile/VM image before running this script", + ]); + }); + + // TODO: Add faster test to run with uv. + // currently times out. + // it("runs with uv", async () => { + // const state = await runTerraformApply(import.meta.dir, { + // agent_id: "foo", + // }); + // const output = await executeScriptInContainerWithUv(state, "python:3-alpine"); + // expect(output.exitCode).toBe(0); + // expect(output.stdout).toEqual([ + // "Checking for a supported installer", + // "uv is installed", + // "\u001B[0;1mInstalling marimo!", + // "🥳 marimo has been installed", + // "👷 Starting marimo in background...check logs at /tmp/marimo.log", + // ]); + // }); + + // TODO: Add faster test to run with pipx. + // currently times out. + // it("runs with pipx", async () => { + // const state = await runTerraformApply(import.meta.dir, { + // agent_id: "foo", + // }); + // const output = await executeScriptInContainerWithPipx(state, "alpine"); + // expect(output.exitCode).toBe(0); + // expect(output.stdout).toEqual([ + // "Checking for a supported installer", + // "pipx is installed", + // "\u001B[0;1mInstalling marimo!", + // "🥳 marimo has been installed", + // "👷 Starting marimo in background...check logs at /tmp/marimo.log", + // ]); + // }); +}); \ No newline at end of file From b002fd840fbac4d5693f153994d40f805d58238b Mon Sep 17 00:00:00 2001 From: M Atif Ali Date: Sun, 20 Apr 2025 10:51:00 +0000 Subject: [PATCH 3/4] refactor: Remove commented test TODOs --- marimo/main.test.ts | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/marimo/main.test.ts b/marimo/main.test.ts index a87bfc16..b76446a5 100644 --- a/marimo/main.test.ts +++ b/marimo/main.test.ts @@ -80,37 +80,4 @@ describe("marimo", async () => { ]); }); - // TODO: Add faster test to run with uv. - // currently times out. - // it("runs with uv", async () => { - // const state = await runTerraformApply(import.meta.dir, { - // agent_id: "foo", - // }); - // const output = await executeScriptInContainerWithUv(state, "python:3-alpine"); - // expect(output.exitCode).toBe(0); - // expect(output.stdout).toEqual([ - // "Checking for a supported installer", - // "uv is installed", - // "\u001B[0;1mInstalling marimo!", - // "🥳 marimo has been installed", - // "👷 Starting marimo in background...check logs at /tmp/marimo.log", - // ]); - // }); - - // TODO: Add faster test to run with pipx. - // currently times out. - // it("runs with pipx", async () => { - // const state = await runTerraformApply(import.meta.dir, { - // agent_id: "foo", - // }); - // const output = await executeScriptInContainerWithPipx(state, "alpine"); - // expect(output.exitCode).toBe(0); - // expect(output.stdout).toEqual([ - // "Checking for a supported installer", - // "pipx is installed", - // "\u001B[0;1mInstalling marimo!", - // "🥳 marimo has been installed", - // "👷 Starting marimo in background...check logs at /tmp/marimo.log", - // ]); - // }); }); \ No newline at end of file From b36f1c42aaa415fe8e3a7c4fa4457920d458f9ae Mon Sep 17 00:00:00 2001 From: M Atif Ali Date: Sun, 20 Apr 2025 16:00:24 +0500 Subject: [PATCH 4/4] Update marimo.svg --- .icons/marimo.svg | 137 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 27 deletions(-) diff --git a/.icons/marimo.svg b/.icons/marimo.svg index 4fddd3d6..db76c169 100644 --- a/.icons/marimo.svg +++ b/.icons/marimo.svg @@ -1,33 +1,116 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file +