Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions recipes/dodo/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
context:
version: "0.0.1"
mojo_version: "=1.2.0"

package:
name: dodo
version: ${{ version }}

source:
- git: https://github.com/dodo-mojo/dodo.git
tag: v${{ version }}

build:
number: 0
script:
interpreter: bash
content:
- |
set -euo pipefail
mkdir -p "${PREFIX}/lib/mojo"

# The wrapper build scripts install into $CONDA_PREFIX/lib, which
# here is the package prefix, and fail loudly on a broken build.
export CONDA_PREFIX="${PREFIX}"
export DODO_CC="${CC}"
export DODO_CXX="${CXX}"
source tools/build_native.sh

mojo precompile dodo -o "${PREFIX}/lib/mojo/dodo.mojoc"

requirements:
build:
- ${{ compiler('c') }}
- ${{ compiler('cxx') }}
- mojo-compiler ${{ mojo_version }}
host:
- mojo-compiler ${{ mojo_version }}
- openssl >=3.6.1,<4
- zlib >=1.3.1,<2
- libdeflate >=1.22,<2
- libbrotlicommon >=1.2.1,<2
- libbrotlidec >=1.2.1,<2
- libbrotlienc >=1.2.1,<2
- simdjson >=4.6.11,<5
- libsqlite >=3.45,<4
run:
- mojo-compiler ${{ mojo_version }}
- ca-certificates >=2025.1.31
# dodo opens libsqlite3 by name at runtime, so nothing links it.
- libsqlite >=3.45,<4

tests:
- requirements:
run:
- mojo ${{ mojo_version }}
- curl
# mojo build links through the system C compiler, which Linux lacks.
- if: linux
then: gcc
files:
recipe:
- tests/package_smoke.mojo
- tests/hello_server.mojo
script:
interpreter: bash
content:
- |
set -euo pipefail
test -f "${PREFIX}/lib/mojo/dodo.mojoc"
for name in tls zlib brotli fs simdjson; do
test -f "${PREFIX}/lib/libdodo_${name}.so"
done

mojo build tests/package_smoke.mojo -o package_smoke
./package_smoke

mojo build tests/hello_server.mojo -o hello_server
./hello_server 18471 &
SERVER=$!
trap 'kill "${SERVER}" 2>/dev/null || true' EXIT
BODY=""
for attempt in $(seq 1 50); do
if BODY="$(curl -fsS http://127.0.0.1:18471/)"; then
break
fi
sleep 0.2
done
test "${BODY}" = "hello from dodo"

about:
homepage: https://github.com/dodo-mojo/dodo
license: Apache-2.0
license_file:
- LICENSE
- THIRD_PARTY_NOTICES.md
summary: Mojo web framework with AI, GPU and computer vision natively supported, all in one language
repository: https://github.com/dodo-mojo/dodo

extra:
maintainers:
- anubhav-qt
project_name: dodo
16 changes: 16 additions & 0 deletions recipes/dodo/tests/hello_server.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""A real server built from the installed package. Usage: hello_server PORT"""

from std.sys import argv

from dodo import App, Request, Response, ok


def hello(req: Request) raises -> Response:
return ok("hello from dodo")


def main() raises:
var app = App()
app.get["/"](hello)
var port = UInt16(Int(argv()[1]))
app.serve(port=port)
103 changes: 103 additions & 0 deletions recipes/dodo/tests/package_smoke.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Checks an installed dodo package: the App and every native library.

Run by the conda recipe's test step against the installed package, not the
source tree.
"""

from std.testing import assert_equal, assert_true

from dodo import App, JsonBody, PathParams, Request
from dodo.crypto.digest import sha256
from dodo.db.sqlite import Sqlite
from dodo.http import Method
from dodo.http.encoding import (
compress_brotli,
compress_gzip,
decode_content,
)
from dodo.json.ffi.simdjson import simdjson_available


@fieldwise_init
struct NamePath(PathParams):
var name: String


@fieldwise_init
struct NewUser(JsonBody):
var name: String
var age: Int


@fieldwise_init
struct Greeting(Movable):
var message: String


@fieldwise_init
struct Created(Movable):
var name: String
var age: Int


def hello(req: Request, path: NamePath) raises -> Greeting:
return Greeting(String("hi ", path.name))


def create(req: Request, body: NewUser) raises -> Created:
return Created(body.name, body.age)


def check_app() raises:
var app = App()
app.get["/hello/{name}"](hello)
app.post["/users"](create)

var get_req = Request(method=Method.GET, url="/hello/dodo")
var got = app.serve(get_req)
assert_equal(got.status, 200)
assert_equal(got.text(), '{"message":"hi dodo"}')

var body = String('{"name":"ada","age":36}')
var post_req = Request(
method=Method.POST, url="/users", body=List[UInt8](body.as_bytes())
)
var posted = app.serve(post_req)
assert_equal(posted.status, 200)
assert_equal(posted.text(), '{"name":"ada","age":36}')


def check_compression() raises:
var text = String("dodo dodo dodo dodo dodo dodo")
var gzipped = compress_gzip(text.as_bytes())
var unzipped = decode_content(gzipped, "gzip")
assert_equal(String(unsafe_from_utf8=unzipped), text)

var brotli = compress_brotli(text.as_bytes())
var unbrotli = decode_content(brotli, "br")
assert_equal(String(unsafe_from_utf8=unbrotli), text)


def check_openssl() raises:
var empty = String("")
var digest = sha256(empty.as_bytes())
assert_equal(len(digest), 32)
assert_equal(Int(digest[0]), 0xE3)


def check_simdjson() raises:
assert_true(simdjson_available(), "libdodo_simdjson.so did not load")


def check_sqlite() raises:
var db = Sqlite("sqlite://:memory:")
assert_equal(db.query("select 41 + 1 as n")[0].int("n"), 42)


def main() raises:
check_app()
check_compression()
check_openssl()
check_simdjson()
check_sqlite()
print("dodo package smoke test passed")
Loading