diff --git a/.gitignore b/.gitignore
index 61519e3..2a149a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
# Directory created from using the 'rsm' CLI tool.
-.rsm/
+/.rsm/
# The recommended virtualenv directory.
/.venv/
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000..9919bf8
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.10.13
diff --git a/.rsmrc b/.rsmrc
index f10b5d8..cd08df6 100644
--- a/.rsmrc
+++ b/.rsmrc
@@ -25,8 +25,8 @@
# `.rsmrc` as the working directory so that any paths that we specify
# below are relative to it.
protoc --working-directory=.
-dev --working-directory=.
-dev --protoc-watch
+dev run --working-directory=.
+dev run --protoc-watch
# Generate code from our '.proto' files in 'api/'.
protoc --output-directory=api/
@@ -39,19 +39,23 @@ protoc api/
protoc -- --resemble_python_out=backend/api --resemble_react_out=web/src/api
# Watch if any generated files are modified.
-dev --watch=backend/api/**/*.py
+dev run --watch=backend/api/**/*.py
# Watch if any of our source files are modified.
-dev --watch=backend/src/**/*.py
+dev run --watch=backend/src/**/*.py
# PYTHONPATH must be explicitly set to pick up generated code.
-dev --env=PYTHONPATH=backend/api/
+dev run --env=PYTHONPATH=backend/api/
# Tell `rsm` that this is a Python application.
-dev --python
+dev run --python
# Save state between chaos restarts.
-dev --name=hello
+dev run --name=hello
# Run the application!
-dev backend/src/main.py
+dev run backend/src/main.py
+
+# When running `rsm dev expunge`, the state we want to remove is that of the
+# "hello" application, since that's what we were running with `rsm dev run`.
+dev expunge --name=hello
diff --git a/.tests/test.sh b/.tests/test.sh
index fd686b3..1de854d 100755
--- a/.tests/test.sh
+++ b/.tests/test.sh
@@ -12,48 +12,30 @@ ls -l api/ backend/src/ web/ 2> /dev/null > /dev/null || {
exit 1
}
-# Create and activate a virtual environment so that we don't pollute the
-# system's Python installation.
-VENV="./.resemble-hello-venv"
-python -m venv $VENV
-source $VENV/bin/activate
-
-# If `REBOOT_RESEMBLE_WHL_FILE` is set, have it refer to an absolute non-symlink
-# (= canonical) path.
-if [ -v REBOOT_RESEMBLE_WHL_FILE ]; then
- REBOOT_RESEMBLE_WHL_FILE=$(readlink --canonicalize $REBOOT_RESEMBLE_WHL_FILE)
+# Convert symlinks to files that we need to mutate into copies.
+for file in "requirements.lock" "requirements-dev.lock" "pyproject.toml"; do
+ cp "$file" "${file}.tmp"
+ rm "$file"
+ mv "${file}.tmp" "$file"
+done
+
+# Use the published Resemble pip package by default, but allow the test system
+# to override them with a different value.
+if [ -n "$REBOOT_RESEMBLE_WHL_FILE" ]; then
+ # Install the `reboot-resemble` package from the specified path explicitly, over-
+ # writing the version from `pyproject.toml`.
+ rye remove --no-sync reboot-resemble
+ rye remove --no-sync --dev reboot-resemble
+ rye add --dev reboot-resemble --absolute --path=$REBOOT_RESEMBLE_WHL_FILE
fi
-# Normally, tests will use the published Resemble PyPI package; this is what
-# happens when this test is run from `.github/workflows/*.yml`.
-#
-# However, when there is a need to test changes to the Resemble package itself,
-# the test system can override the default and use an explicit local wheel file
-# instead.
-REBOOT_RESEMBLE_PACKAGE=${REBOOT_RESEMBLE_WHL_FILE:-"reboot-resemble"}
-
-# Manually install the Resemble pip package before installing the
-# requirements.txt. This allows us to install unreleased versions of
-# the Resemble package during tests.
-pip install $REBOOT_RESEMBLE_PACKAGE
-
-# Save the pip show info on the package so that we can compare it after
-# installing the rest of the requirements, to check that our custom whl hasn't
-# been overwritten.
-resemble_info=$(pip show reboot-resemble)
-
-pip install -r backend/src/requirements.txt
-
-# Double check that we haven't reinstalled another version of the
-# reboot-resemble package.
-if [ "$resemble_info" != "$(pip show reboot-resemble)" ]; then
- echo "ERROR: reboot-resemble whl overwritten by pip install. Are the package versions out of sync?"
- exit 1
-fi
+# Create and activate a virtual environment.
+rye sync --no-lock
+source .venv/bin/activate
rsm protoc
-mypy --python-executable=$VENV/bin/python backend/
+mypy backend/
pytest backend/
@@ -62,6 +44,11 @@ pytest backend/
# We will only do this if this machine has the `docker` command installed. That
# means this is skipped on e.g. GitHub's Mac OS X runners.
if command -v docker &> /dev/null; then
+ if [ -n "$REBOOT_RESEMBLE_WHL_FILE" ]; then
+ # If `REBOOT_RESEMBLE_WHL_FILE` is set, have it refer to an absolute non-symlink
+ # (= canonical) path.
+ REBOOT_RESEMBLE_WHL_FILE=$(readlink --canonicalize $REBOOT_RESEMBLE_WHL_FILE)
+ fi
# Since Docker can't follow symlinks to files outside the build context, we
# can't build the Docker image in a directory where the Dockerfile is a symlink.
# That situation occurs when e.g. running this test on Bazel. Follow the symlink
@@ -71,6 +58,3 @@ if command -v docker &> /dev/null; then
popd
fi
-
-# Clean up.
-rm -rf ./.resemble-hello-venv
diff --git a/BUILD.bazel b/BUILD.bazel
index deb8585..06e36fc 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -11,6 +11,7 @@ filegroup(
# Files that may be created by activity in this directory, such
# as manually following the steps of the `README.md` file, but which
# are not part of the "source code" of this repository.
+ ".venv/**/*",
".resemble-hello-venv/**/*",
".pytest_cache/**/*",
"api/gen/**/*.js",
diff --git a/Dockerfile b/Dockerfile
index 84f8dbe..f2e9755 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -52,6 +52,15 @@ RUN mkdir $NVM_DIR && \
# Our container will run as `root`, so the root user must load `nvm` on login.
RUN echo ". $NVM_DIR/nvm.sh" >> /root/.bashrc
+### Our application.
+
+# First ONLY copy and install the requirements, so that changes outside
+# `requirements.txt` don't force a re-install of all dependencies.
+#
+# Note that this will install the Resemble library and CLI.
+COPY requirements.lock requirements.txt
+RUN pip install -r requirements.txt
+
### Unpublished Resemble package.
# If you plan to use this Dockerfile for your own project, you may omit this
# section; it is useful only for Reboot's internal development. Outside of
@@ -70,17 +79,9 @@ COPY .unpublished-*-wheel/*.whl .unpublished-resemble-wheel/
# If `.unpublished-resemble-wheel/` was empty or did not exist, the following
# `ls` will fail and instead of `pip install` we'll run `echo`, which means this
# RUN has no effects in that case.
-RUN (ls ./.unpublished-resemble-wheel/*.whl && pip install ./.unpublished-resemble-wheel/*.whl) \
+RUN (ls ./.unpublished-resemble-wheel/*.whl && pip install --force-reinstall ./.unpublished-resemble-wheel/*.whl) \
|| echo "No unpublished wheels to install."
-
-### Our application.
-
-# First ONLY copy and install the requirements, so that changes outside
-# `requirements.txt` don't force a re-install of all dependencies.
-#
-# Note that this will install the Resemble library and CLI.
-COPY backend/src/requirements.txt requirements.txt
-RUN pip install -r requirements.txt
+### End of interlude.
# Next, copy the API definition and generate Resemble code. This step is also
# separate so it is only repeated if the `api/` code changes.
diff --git a/README.md b/README.md
index eebb3e8..eb9400a 100644
--- a/README.md
+++ b/README.md
@@ -1,188 +1,80 @@
# Resemble Hello World
For the impatient:
-1. Get a suitable environment:
- * Use VSCode (on your machine)
- * [... connected to a GitHub Codespace](#use-vscode-connected-to-a-github-codespace)
- * [... with a local Dev Container](#use-vscode-with-a-local-dev-container)
- * [Use a Docker Container](#use-a-docker-container)
- * [Install prerequisites manually](#install-prerequisites-manually)
+1. Prepare an environment by either:
+ * [Using VSCode connected to a GitHub Codespace](#using-vscode-connected-to-a-github-codespace)
+ * [Installing prerequisites directly](#installing-prerequisites-directly)
2. [Run the application](#run-the-application)
### Overview
This repository contains a simple example application written using Resemble.
-The '.proto' files can be found in the `api/` directory, grouped into
+The [Resemble '.proto' definition](https://docs.reboot.dev/docs/model/overview#generated-code)
+can be found in the `api/` directory, grouped into
subdirectories by proto package, while backend specific code can be
found in `backend/` and web specific code in `web/`.
-This repository includes a [Dev Container](https://containers.dev/) that _has all of the dependencies you need to build and run code in this repository already installed_.
+## Prepare an environment by...
-> [!NOTE]
-> The Dev Container's configuration for this repository is found in
-> [`.devcontainer/devcontainer.json`](main/.devcontainer/devcontainer.json). You
-> may expand on it to customize your development environment to your
-> liking.
+
+### Using VSCode connected to a GitHub Codespace
-You can start the Dev Container in two different ways.
+This method requires running [VSCode](https://code.visualstudio.com/) on your machine: if that isn't your bag, see [the other environment option](#install-prerequisites-directly) below.
-
-## Use VSCode connected to a GitHub Codespace
+This repository includes a [Dev Container config](./.devcontainer/devcontainer.json) (more about [Dev Containers](https://containers.dev/)) that declares all of the dependencies that you need to build and run the example. Dev Containers can be started locally with VSCode, but we recommend using GitHub's [Codespaces](https://github.com/features/codespaces) to quickly launch the Dev Container:
-GitHub's [Codespaces](https://github.com/features/codespaces) are machines that
-are hosted in the cloud for you.
-
-> [!IMPORTANT]
-> You must connect your local VSCode to the codespace, you can not use VSCode in a browser window.
-
-[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/reboot-dev/resemble-hello)
-
-(Right-Click to open in new tab or window)
-
-If you haven't [set your default editor to VSCode for codespaces](https://docs.github.com/en/codespaces/customizing-your-codespace/setting-your-default-editor-for-github-codespaces), then the 'Open in GitHub Codespaces' button above will end up opening VSCode in the browser. You can close that browser tab because _YOU MUST_ [open the existing codespace](https://docs.github.com/en/codespaces/developing-in-codespaces/opening-an-existing-codespace?tool=vscode) using the VSCode on your machine. You can also go to [https://github.com/codespaces](https://github.com/codespaces) and click the three dots next to the codespace you just created and then click `Open in ...` then `Open in Visual Studio Code`.
-
-Now you're ready to [run the application](#run-the-application)!
-
-
-## Use VSCode with a local Dev Container
-
-> [!IMPORTANT]
-> Currently, our Dev Container at [`.devcontainer/devcontainer.json`](main/.devcontainer/devcontainer.json) **only works on x86 CPU architectures**.
-
-If your machine meets the required specifications, you can start this
-repository's Dev Container with VSCode locally rather than using a GitHub Codespace.
-
-Clone this repository:
-
-
-
-```shell
-git clone https://github.com/reboot-dev/resemble-hello.git
-```
-
-Open the Dev Container:
-
-- In VSCode, open the `resemble-hello` folder you've cloned.
-- Press: Ctrl+Shift+P (Linux / Windows) or Command+Shift+P (Mac)
-- Type/Select: `Dev Containers: Reopen In Container`
-
-VSCode will now start the Dev Container and restart VSCode to be running
-inside of that container.
+1. Right-click to create a Codespace in a new tab or window:
+ * [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/reboot-dev/resemble-hello)
+ * *Important*: In order to view the example's frontend, you must connect your local VSCode to the codespace: you cannot use VSCode in a browser window.
+2. Go to [https://github.com/codespaces](https://github.com/codespaces) and click the three dots next to the codespace you just created and then click `Open in Visual Studio Code`.
+ * You can [set your default editor to VSCode for codespaces](https://docs.github.com/en/codespaces/customizing-your-codespace/setting-your-default-editor-for-github-codespaces) to avoid this step in the future. See [these instructions](https://docs.github.com/en/codespaces/developing-in-codespaces/opening-an-existing-codespace?tool=vscode) for more information.
Now you're ready to [run the application](#run-the-application)!
-
-## Use a Docker container
-
-We've created a [Docker container](ghcr.io/reboot-dev/resemble-standalone) that _has all of the dependencies you need to build and run code in this repository already installed_.
-> [!IMPORTANT]
-> The Docker container currently **only works on x86 CPU architectures**. Check back soon for more supported architectures.
+
+### Installing prerequisites directly
-Clone this repository:
+Running directly on a host requires:
-```shell
-git clone https://github.com/reboot-dev/resemble-hello.git
-cd resemble-hello/
-```
-
-Run the container:
-
-```shell
-export HOST_WORKING_DIRECTORY="$(pwd)"
-export CONTAINER_WORKSPACE_DIRECTORY="/workspaces/$(basename $HOST_WORKING_DIRECTORY)"
-docker run \
- --mount type=bind,source="$HOST_WORKING_DIRECTORY",target="$CONTAINER_WORKSPACE_DIRECTORY" \
- --workdir "$CONTAINER_WORKSPACE_DIRECTORY" \
- --env "HOST_UID=$(id -u)" \
- --env "HOST_GID=$(id -g)" \
- -p 127.0.0.1:3000:3000/tcp \
- -p 127.0.0.1:9991:9991/tcp \
- --privileged \
- --interactive \
- --tty \
- ghcr.io/reboot-dev/resemble-standalone:latest \
- /bin/bash
-```
-
-Explanation of flags:
-* We --mount our --workdir (working directory), so we can work with it from the container.
-* We tell the container about our user's UID and GID so that the container's
- user can match them, providing the same permissions inside and outside the
- container.
-* We bind port 3000 so that we can access a React web front end (e.g., from a browser), and port 9991 so the web front end can access the Resemble backend.
-* `--privileged` so that we can run Docker inside of the container.
-* `--interactive` and `--tty` (often abbreviated `-it`) lets us interact with
- the created container.
-* `ghcr.io/reboot-dev/resemble-standalone:latest` is the name of the container we'll be running.
-* `/bin/bash` is the shell we'd like to run.
-
-Now you're ready to [run the application](#run-the-application)!
-
-
-## Install prerequisites manually
-
-> [!IMPORTANT]
-> Resemble backends currently can **on x86_64 Linux** machines with
-> `glibc>=2.35` (Ubuntu Jammy and other equivalent-generation Linux
-> distributions), and **on arm64/x86_64 MacOS**, where `MacOS>=13.0` and
-> `Xcode>=14.3`. If you have a machine that doesn't fit this requirement, we
-> suggest using one of the approaches discussed above.
-### Prerequisites
-
-You must have the following tools installed:
-
-- Python (including `pip` and `venv`) >= 3.10
-- Node.js (including `npm`)
+- A platform of either:
+ - `x86_64 Linux` with `glibc>=2.35` (Ubuntu Jammy and other equivalent-generation Linux distributions)
+ - `arm64 or x86_64 MacOS` with `MacOS>=13.0` and `Xcode>=14.3`
+- [Rye](https://rye-up.com/)
+ - A tool to manage `python`, `pip`, and `venv`. If you are already familiar with Python [virtual environments](https://docs.python.org/3/library/venv.html), feel free to use your tool of choice with [`pyproject.toml`](./pyproject.toml). Python>=3.10 is required.
+- Node.js
+ - Including `npm`.
- Docker
+ - Note: the example does not run "inside of" Docker, but Docker is used to host a native support service for local development.
-### Clone Repository
-
-Clone this repository:
-
-```shell
-git clone https://github.com/reboot-dev/resemble-hello.git
-cd resemble-hello/
-```
-
-### Create and activate a virtual environment
-
-Create a new Python virtual environment in which to install Resemble
-requirements and run an application:
-
-```sh
-python -m venv ./.venv
-source ./.venv/bin/activate
-```
-
-To learn more about why virtual environments are a best practice for Python
-projects, see [the Python documentation for the `venv` module.](https://docs.python.org/3/library/venv.html)
+If you are unable to meet any of these requirements, we suggest using the [VSCode and Dev Container environment](#using-vscode-connected-to-a-github-codespace) discussed above.
Now you're ready to [run the application](#run-the-application)!
## Run the application
-### Backend via `rsm dev`
+### Backend via `rsm dev run`
Our backend is implemented in Python and we must install its dependencies before
running it. The most notable of those dependencies is the `reboot-resemble` PyPI
distribution, which contains both the Resemble CLI (`rsm`) and the `resemble`
Python package.
+Using `rye`, we can create and activate a virtualenv containing this project's dependencies (as well as fetch an appropriate Python version) using:
```sh
-pip install -r backend/src/requirements.txt
+rye sync --no-lock
+source .venv/bin/activate
```
-To run the application, you can now use the Resemble CLI `rsm`:
-
+Then, to run the application, you can use the Resemble CLI `rsm` (present in the active virtualenv):
```shell
-rsm dev
+rsm dev run
```
-Running `rsm dev` will watch for file modifications and restart the
+Running `rsm dev run` will watch for file modifications and restart the
application if necessary. See the `.rsmrc` file for flags and
-arguments that get expanded when running `rsm dev`.
+arguments that get expanded when running `rsm dev run`.
### Front end
@@ -200,7 +92,7 @@ If not using VSCode, visit [http://127.0.0.1:3000](http://127.0.0.1:3000)`.
The application comes with backend tests.
Before you run the tests, you'll
-need to ensure you've run `rsm protoc`. If you've already run `rsm dev`
+need to ensure you've run `rsm protoc`. If you've already run `rsm dev run`
without modifying `.rsmrc`, `rsm protoc` will have been run for you as
part of that command.
Otherwise, you can do it manually.
@@ -219,14 +111,14 @@ Now you can run the tests using `pytest`:
pytest backend/
```
-### Running on the Resemble Cloud
+### Running on the Reboot Cloud
Pick a public Docker registry you can push images to. Determine the name you'd
like the image to have in that registry. For example:
`ghcr.io/your-github-username/resemble-hello`.
Then, run the following to build and push your `resemble-hello` container:
-```
+```shell
export IMAGE_NAME=
./build.sh --push $IMAGE_NAME
```
@@ -248,21 +140,21 @@ To make calls to the application that just started, get the endpoint URL from
message output to the console.
```sh
-Application starting; you application will be available at:
+Application starting; your application will be available at:
-..resemble.cloud:9991
+.prod1.resemble.cloud:9991
```
To build a version of the frontend that can talk to the deployed app, replace
-value passed to the `ResembleClient` in `web/serc/index.tsx`:
+value passed to the `ResembleClient` in `web/src/index.tsx`:
```tsx
const client = new ResembleClient(
- "..resemble.cloud:9991")
+ ".prod1.resemble.cloud:9991")
;
```
-Then run `npm run build`.
+Then, in the `web/` directory, run `npm run build`.
Once built, this front end can be deployed to any static hosting provider like
S3, Vercel, Cloudflare or Firebase hosting.
diff --git a/backend/src/main.py b/backend/src/main.py
index 00e5246..71ead02 100644
--- a/backend/src/main.py
+++ b/backend/src/main.py
@@ -11,7 +11,7 @@
async def initialize(workflow: Workflow):
- hello = Hello(EXAMPLE_STATE_MACHINE_ID)
+ hello = Hello.lookup(EXAMPLE_STATE_MACHINE_ID)
# Implicitly construct state machine upon first write.
await hello.Send(workflow, message="Hello, World!")
diff --git a/backend/src/requirements.txt b/backend/src/requirements.txt
deleted file mode 100644
index 04955a0..0000000
--- a/backend/src/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-reboot-resemble>=0.5.3
-pytest>=7.4.2
-types-protobuf>=4.24.0.20240129
diff --git a/backend/tests/hello_servicer_test.py b/backend/tests/hello_servicer_test.py
index 0a85d8f..e23a333 100644
--- a/backend/tests/hello_servicer_test.py
+++ b/backend/tests/hello_servicer_test.py
@@ -19,7 +19,7 @@ async def test_hello(self) -> None:
workflow: Workflow = self.rsm.create_workflow(name=f"test-{self.id()}")
- hello = Hello("testing-hello")
+ hello = Hello.lookup("testing-hello")
await hello.Send(workflow, message="Hello, World")
diff --git a/build.sh b/build.sh
index 830093c..ed5d060 100755
--- a/build.sh
+++ b/build.sh
@@ -36,7 +36,7 @@ DOCKER_IMAGE_NAME=${POSITIONAL_ARGS[0]}
# The following is a little helper for developers working on the Resemble
# library. You likely won't need it if you're just using Resemble.
-if [ -v REBOOT_RESEMBLE_WHL_FILE ]; then
+if [ -n "$REBOOT_RESEMBLE_WHL_FILE" ]; then
# Place the wheel package in a place where the Docker build process can
# reach it. That means placing it in the build context, meaning the
# directory containing the `Dockerfile`, or a subdirectory. We'll create
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..633f110
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,18 @@
+[project]
+requires-python = ">= 3.10"
+dependencies = [
+ "reboot-resemble>=0.5.4",
+]
+
+[tool.rye]
+dev-dependencies = [
+ "reboot-resemble>=0.5.4",
+ "mypy>=1.2.0",
+ "pytest>=7.4.2",
+ "types-protobuf>=4.24.0.20240129",
+]
+
+# This project only uses `rye` to provide `python` and its dependencies, so
+# these settings remove the need to name or version the project.
+virtual = true
+managed = true
diff --git a/requirements-dev.lock b/requirements-dev.lock
new file mode 100644
index 0000000..591df7a
--- /dev/null
+++ b/requirements-dev.lock
@@ -0,0 +1,164 @@
+# generated by rye
+# use `rye lock` or `rye sync` to update this lockfile
+#
+# last locked with the following flags:
+# pre: false
+# features: []
+# all-features: false
+# with-sources: false
+
+aiofiles==23.2.1
+ # via reboot-resemble
+aiohttp==3.8.5
+ # via kubernetes-asyncio
+ # via reboot-resemble
+aiosignal==1.3.1
+ # via aiohttp
+async-timeout==4.0.3
+ # via aiohttp
+attrs==23.2.0
+ # via aiohttp
+cachetools==5.3.3
+ # via google-auth
+certifi==2024.2.2
+ # via kubernetes
+ # via kubernetes-asyncio
+ # via requests
+cffi==1.16.0
+ # via cryptography
+charset-normalizer==3.3.2
+ # via aiohttp
+ # via requests
+colorama==0.4.6
+ # via reboot-resemble
+cryptography==42.0.2
+ # via reboot-resemble
+exceptiongroup==1.2.0
+ # via pytest
+frozenlist==1.4.1
+ # via aiohttp
+ # via aiosignal
+gitdb==4.0.11
+ # via gitpython
+gitpython==3.1.31
+ # via reboot-resemble
+google-auth==2.29.0
+ # via kubernetes
+googleapis-common-protos==1.61.0
+ # via grpcio-status
+ # via reboot-resemble
+grpc-interceptor==0.15.4
+ # via reboot-resemble
+grpcio==1.56.2
+ # via grpc-interceptor
+ # via grpcio-health-checking
+ # via grpcio-reflection
+ # via grpcio-status
+ # via grpcio-tools
+ # via reboot-resemble
+grpcio-health-checking==1.56.2
+ # via reboot-resemble
+grpcio-reflection==1.56.2
+ # via reboot-resemble
+grpcio-status==1.56.2
+ # via reboot-resemble
+grpcio-tools==1.56.2
+ # via reboot-resemble
+idna==3.6
+ # via requests
+ # via yarl
+iniconfig==2.0.0
+ # via pytest
+jinja2==3.1.2
+ # via jinja2-strcase
+ # via reboot-resemble
+jinja2-strcase==0.0.2
+ # via reboot-resemble
+kubernetes==24.2.0
+ # via reboot-resemble
+kubernetes-asyncio==24.2.2
+ # via reboot-resemble
+markupsafe==2.1.5
+ # via jinja2
+multidict==6.0.5
+ # via aiohttp
+ # via yarl
+mypy==1.9.0
+mypy-extensions==1.0.0
+ # via mypy
+mypy-protobuf==3.5.0
+ # via reboot-resemble
+oauthlib==3.2.2
+ # via requests-oauthlib
+packaging==23.1
+ # via pytest
+ # via reboot-resemble
+pluggy==1.4.0
+ # via pytest
+protobuf==4.25.2
+ # via googleapis-common-protos
+ # via grpcio-health-checking
+ # via grpcio-reflection
+ # via grpcio-status
+ # via grpcio-tools
+ # via mypy-protobuf
+ # via reboot-resemble
+psutil==5.9.5
+ # via reboot-resemble
+pyasn1==0.5.1
+ # via pyasn1-modules
+ # via rsa
+pyasn1-modules==0.3.0
+ # via google-auth
+pycparser==2.21
+ # via cffi
+pyjwt==2.8.0
+ # via reboot-resemble
+pyprctl==0.1.3
+ # via reboot-resemble
+pytest==8.1.1
+python-dateutil==2.9.0.post0
+ # via kubernetes
+ # via kubernetes-asyncio
+pyyaml==6.0.1
+ # via kubernetes
+ # via kubernetes-asyncio
+reboot-resemble==0.5.3
+requests==2.31.0
+ # via kubernetes
+ # via requests-oauthlib
+requests-oauthlib==2.0.0
+ # via kubernetes
+rsa==4.9
+ # via google-auth
+setuptools==69.2.0
+ # via grpcio-tools
+ # via kubernetes
+ # via kubernetes-asyncio
+six==1.16.0
+ # via kubernetes
+ # via kubernetes-asyncio
+ # via python-dateutil
+smmap==5.0.1
+ # via gitdb
+tomli==2.0.1
+ # via mypy
+ # via pytest
+types-protobuf==4.24.0.20240311
+ # via mypy-protobuf
+typing-extensions==4.9.0
+ # via mypy
+ # via reboot-resemble
+urllib3==1.26.15
+ # via kubernetes
+ # via kubernetes-asyncio
+ # via reboot-resemble
+ # via requests
+watchdog==3.0.0
+ # via reboot-resemble
+websocket-client==1.7.0
+ # via kubernetes
+websockets==12.0
+ # via reboot-resemble
+yarl==1.9.4
+ # via aiohttp
diff --git a/requirements.lock b/requirements.lock
new file mode 100644
index 0000000..a1498d0
--- /dev/null
+++ b/requirements.lock
@@ -0,0 +1,149 @@
+# generated by rye
+# use `rye lock` or `rye sync` to update this lockfile
+#
+# last locked with the following flags:
+# pre: false
+# features: []
+# all-features: false
+# with-sources: false
+
+aiofiles==23.2.1
+ # via reboot-resemble
+aiohttp==3.8.5
+ # via kubernetes-asyncio
+ # via reboot-resemble
+aiosignal==1.3.1
+ # via aiohttp
+async-timeout==4.0.3
+ # via aiohttp
+attrs==23.2.0
+ # via aiohttp
+cachetools==5.3.3
+ # via google-auth
+certifi==2024.2.2
+ # via kubernetes
+ # via kubernetes-asyncio
+ # via requests
+cffi==1.16.0
+ # via cryptography
+charset-normalizer==3.3.2
+ # via aiohttp
+ # via requests
+colorama==0.4.6
+ # via reboot-resemble
+cryptography==42.0.2
+ # via reboot-resemble
+frozenlist==1.4.1
+ # via aiohttp
+ # via aiosignal
+gitdb==4.0.11
+ # via gitpython
+gitpython==3.1.31
+ # via reboot-resemble
+google-auth==2.29.0
+ # via kubernetes
+googleapis-common-protos==1.61.0
+ # via grpcio-status
+ # via reboot-resemble
+grpc-interceptor==0.15.4
+ # via reboot-resemble
+grpcio==1.56.2
+ # via grpc-interceptor
+ # via grpcio-health-checking
+ # via grpcio-reflection
+ # via grpcio-status
+ # via grpcio-tools
+ # via reboot-resemble
+grpcio-health-checking==1.56.2
+ # via reboot-resemble
+grpcio-reflection==1.56.2
+ # via reboot-resemble
+grpcio-status==1.56.2
+ # via reboot-resemble
+grpcio-tools==1.56.2
+ # via reboot-resemble
+idna==3.6
+ # via requests
+ # via yarl
+jinja2==3.1.2
+ # via jinja2-strcase
+ # via reboot-resemble
+jinja2-strcase==0.0.2
+ # via reboot-resemble
+kubernetes==24.2.0
+ # via reboot-resemble
+kubernetes-asyncio==24.2.2
+ # via reboot-resemble
+markupsafe==2.1.5
+ # via jinja2
+multidict==6.0.5
+ # via aiohttp
+ # via yarl
+mypy-protobuf==3.5.0
+ # via reboot-resemble
+oauthlib==3.2.2
+ # via requests-oauthlib
+packaging==23.1
+ # via reboot-resemble
+protobuf==4.25.2
+ # via googleapis-common-protos
+ # via grpcio-health-checking
+ # via grpcio-reflection
+ # via grpcio-status
+ # via grpcio-tools
+ # via mypy-protobuf
+ # via reboot-resemble
+psutil==5.9.5
+ # via reboot-resemble
+pyasn1==0.5.1
+ # via pyasn1-modules
+ # via rsa
+pyasn1-modules==0.3.0
+ # via google-auth
+pycparser==2.21
+ # via cffi
+pyjwt==2.8.0
+ # via reboot-resemble
+pyprctl==0.1.3
+ # via reboot-resemble
+python-dateutil==2.9.0.post0
+ # via kubernetes
+ # via kubernetes-asyncio
+pyyaml==6.0.1
+ # via kubernetes
+ # via kubernetes-asyncio
+reboot-resemble==0.5.3
+requests==2.31.0
+ # via kubernetes
+ # via requests-oauthlib
+requests-oauthlib==2.0.0
+ # via kubernetes
+rsa==4.9
+ # via google-auth
+setuptools==69.2.0
+ # via grpcio-tools
+ # via kubernetes
+ # via kubernetes-asyncio
+six==1.16.0
+ # via kubernetes
+ # via kubernetes-asyncio
+ # via python-dateutil
+smmap==5.0.1
+ # via gitdb
+types-protobuf==4.24.0.20240311
+ # via mypy-protobuf
+typing-extensions==4.9.0
+ # via reboot-resemble
+urllib3==1.26.15
+ # via kubernetes
+ # via kubernetes-asyncio
+ # via reboot-resemble
+ # via requests
+watchdog==3.0.0
+ # via reboot-resemble
+websocket-client==1.7.0
+ # via kubernetes
+websockets==12.0
+ # via reboot-resemble
+yarl==1.9.4
+ # via aiohttp
diff --git a/web/.env b/web/.env
new file mode 100644
index 0000000..75144a8
--- /dev/null
+++ b/web/.env
@@ -0,0 +1 @@
+REACT_APP_REBOOT_RESEMBLE_ENDPOINT=https://localhost.direct:9991
diff --git a/web/package-lock.json b/web/package-lock.json
index 13337cf..58aa6e8 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -8,7 +8,7 @@
"name": "web",
"version": "0.1.0",
"dependencies": {
- "@reboot-dev/resemble-react": "^0.4.0",
+ "@reboot-dev/resemble-react": "^0.5.3",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
@@ -3261,23 +3261,24 @@
}
},
"node_modules/@reboot-dev/resemble-api": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-api/-/resemble-api-0.0.1.tgz",
- "integrity": "sha512-kpEsNYQ0tW20SJg+bP6w7DHG6azCYZbf+f3LOuVayE2hXjnMt+BGJCmcO3GPxRsqZ1UkrE1/hgtqxEa4e4uEwg=="
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-api/-/resemble-api-0.5.3.tgz",
+ "integrity": "sha512-yvLnSXBtKlMs2oZVtztU901o/VdAQ3sv57QJZWziTM+CXvzt0Gcbci8uMd5FbYUdjYzSruHt5jRkLzT9thWE0g=="
},
"node_modules/@reboot-dev/resemble-react": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-react/-/resemble-react-0.2.0.tgz",
- "integrity": "sha512-Y/vtcWmGND31n+49/5EAOC47vPs9R2MpjpFAWENAlaQz0A24Gl4H0D6NRF77nScdSjPniR8tbzJ2Z3CFmpKBjg==",
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-react/-/resemble-react-0.5.3.tgz",
+ "integrity": "sha512-51EB1neUdnzfn8TtrTX/hDrI0XBhkXuZMw8fvUnq3usVL/kC9L250VHgya/NixMS+ZINT18+72uhhX9z/iUKLg==",
"dependencies": {
"@bufbuild/protobuf": "^1.3.1",
- "@reboot-dev/resemble-api": "0.0.1",
+ "@reboot-dev/resemble-api": "0.5.3",
"@types/react": "^18.2.22",
"@types/uuid": "^9.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tslib": "^2.6.2",
- "typescript": "4.8.4"
+ "typescript": "4.8.4",
+ "uuid": "^9.0.1"
}
},
"node_modules/@reboot-dev/resemble-react/node_modules/typescript": {
@@ -3292,6 +3293,18 @@
"node": ">=4.2.0"
}
},
+ "node_modules/@reboot-dev/resemble-react/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -4121,9 +4134,12 @@
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
},
"node_modules/@types/node": {
- "version": "16.18.54",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.54.tgz",
- "integrity": "sha512-oTmGy68gxZZ21FhTJVVvZBYpQHEBZxHKTsGshobMqm9qWpbqdZsA5jvsuPZcHu0KwpmLrOHWPdEfg7XDpNT9UA=="
+ "version": "20.11.30",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
+ "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -16517,6 +16533,11 @@
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
"integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
},
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@@ -19822,29 +19843,35 @@
}
},
"@reboot-dev/resemble-api": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-api/-/resemble-api-0.0.1.tgz",
- "integrity": "sha512-kpEsNYQ0tW20SJg+bP6w7DHG6azCYZbf+f3LOuVayE2hXjnMt+BGJCmcO3GPxRsqZ1UkrE1/hgtqxEa4e4uEwg=="
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-api/-/resemble-api-0.5.3.tgz",
+ "integrity": "sha512-yvLnSXBtKlMs2oZVtztU901o/VdAQ3sv57QJZWziTM+CXvzt0Gcbci8uMd5FbYUdjYzSruHt5jRkLzT9thWE0g=="
},
"@reboot-dev/resemble-react": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-react/-/resemble-react-0.2.0.tgz",
- "integrity": "sha512-Y/vtcWmGND31n+49/5EAOC47vPs9R2MpjpFAWENAlaQz0A24Gl4H0D6NRF77nScdSjPniR8tbzJ2Z3CFmpKBjg==",
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@reboot-dev/resemble-react/-/resemble-react-0.5.3.tgz",
+ "integrity": "sha512-51EB1neUdnzfn8TtrTX/hDrI0XBhkXuZMw8fvUnq3usVL/kC9L250VHgya/NixMS+ZINT18+72uhhX9z/iUKLg==",
"requires": {
"@bufbuild/protobuf": "^1.3.1",
- "@reboot-dev/resemble-api": "0.0.1",
+ "@reboot-dev/resemble-api": "0.5.3",
"@types/react": "^18.2.22",
"@types/uuid": "^9.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tslib": "^2.6.2",
- "typescript": "4.8.4"
+ "typescript": "4.8.4",
+ "uuid": "^9.0.1"
},
"dependencies": {
"typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ=="
+ },
+ "uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
}
}
},
@@ -20468,9 +20495,12 @@
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
},
"@types/node": {
- "version": "16.18.54",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.54.tgz",
- "integrity": "sha512-oTmGy68gxZZ21FhTJVVvZBYpQHEBZxHKTsGshobMqm9qWpbqdZsA5jvsuPZcHu0KwpmLrOHWPdEfg7XDpNT9UA=="
+ "version": "20.11.30",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
+ "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
+ "requires": {
+ "undici-types": "~5.26.4"
+ }
},
"@types/parse-json": {
"version": "4.0.0",
@@ -29324,6 +29354,11 @@
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
"integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
},
+ "undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
"unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
diff --git a/web/package.json b/web/package.json
index 9ff9078..74d0bec 100644
--- a/web/package.json
+++ b/web/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "@reboot-dev/resemble-react": "^0.5.3",
+ "@reboot-dev/resemble-react": "^0.5.4",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
diff --git a/web/src/App.module.css b/web/src/App.module.css
index 8a15ea7..2289ef5 100644
--- a/web/src/App.module.css
+++ b/web/src/App.module.css
@@ -39,6 +39,12 @@
color: #e91e63;
}
+.informationText {
+ text-align: center;
+ color: #a9a9a9;
+ font-family: Sans-Serif;
+}
+
.buttonDisabled,
.buttonEnabled {
float: right;
diff --git a/web/src/App.tsx b/web/src/App.tsx
index 3f809e7..29138a8 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -31,7 +31,7 @@ const App = () => {
const [message, setMessage] = useState("Hello, Resemble!");
const { useMessages, mutators } = useHello({ id: STATE_MACHINE_ID });
- const { response /* , isLoading */ } = useMessages();
+ const { response, isLoading } = useMessages();
const handleClick = () => {
mutators.send({ message: message });
@@ -59,10 +59,13 @@ const App = () => {
>
Send
- {response !== undefined &&
+ {(response !== undefined &&
response.messages.length > 0 &&
response.messages.map((message: string) => (
+ ))) ||
+ (response !== undefined && response.messages.length == 0 && (
+ No messages yet!
))}
{/*
Optimistically render each send. Each pending mutation on
@@ -73,6 +76,13 @@ const App = () => {
{mutators.send.pending.map(({ request: { message }, isLoading }) => (
))}
+ {/*
+ If we're loading our first response, show the user a loading message,
+ so that they don't just see an emtpy screen.
+ */}
+ {isLoading && response === undefined && (
+ Loading...
+ )}
);
};
diff --git a/web/src/index.tsx b/web/src/index.tsx
index 73c16f7..ae550c9 100644
--- a/web/src/index.tsx
+++ b/web/src/index.tsx
@@ -12,7 +12,9 @@ const root = ReactDOM.createRoot(
);
// Use TLS (via localhost.direct) so we get the advantage of HTTP/2
// multiplexing.
-const client = new ResembleClient("https://localhost.direct:9991");
+const client = new ResembleClient(
+ process.env.REACT_APP_REBOOT_RESEMBLE_ENDPOINT as string
+);
root.render(