forked from cerc-io/stack-orchestrator
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dd54892a1 | ||
|
|
ab0e70ed83 | ||
|
|
c319e90ddd | ||
|
|
2173e7ce6a | ||
|
|
c19559967d | ||
|
|
d7093277b4 | ||
|
|
03a3645b3c | ||
|
|
113c0bfbf1 |
+11
-1
@@ -34,7 +34,7 @@ To test locally run:
|
||||
|
||||
## Running
|
||||
|
||||
With `run-webapp` a new container will be launched with runtime configuration provided by `--env-file` (if specified) and published on an available port. Multiple instances can be launched with different configuration.
|
||||
With `run-webapp` a new container will be launched on the local machine, with runtime configuration provided by `--env-file` (if specified) and published on an available port. Multiple instances can be launched with different configuration.
|
||||
|
||||
**Example**:
|
||||
```
|
||||
@@ -52,3 +52,13 @@ Image: cerc/test-progressive-web-app:local
|
||||
ID: 9ab96494f563aafb6c057d88df58f9eca81b90f8721a4e068493a289a976051c
|
||||
URL: http://localhost:32769
|
||||
```
|
||||
|
||||
## Deploying
|
||||
|
||||
Use the subcommand `deploy-webapp create` to make a deployment directory that can be subsequently deployed to a Kubernetes cluster.
|
||||
Example commands are shown below, assuming that the webapp container image `cerc/test-progressive-web-app:local` has already been built:
|
||||
```
|
||||
$ laconic-so deploy-webapp create --kube-config ~/kubectl/k8s-kubeconfig.yaml --image-registry registry.digitalocean.com/laconic-registry --deployment-dir webapp-k8s-deployment --image cerc/test-progressive-web-app:local --url https://test-pwa-app.hosting.laconic.com/ --env-file test-webapp.env
|
||||
$ laconic-so deployment --dir webapp-k8s-deployment push-images
|
||||
$ laconic-so deployment --dir webapp-k8s-deployment start
|
||||
```
|
||||
|
||||
@@ -32,8 +32,9 @@ from stack_orchestrator.build import build_containers
|
||||
@click.option('--source-repo', help="directory containing the webapp to build", required=True)
|
||||
@click.option("--force-rebuild", is_flag=True, default=False, help="Override dependency checking -- always rebuild")
|
||||
@click.option("--extra-build-args", help="Supply extra arguments to build")
|
||||
@click.option("--tag", help="Container tag (default: cerc/<app_name>:local)")
|
||||
@click.pass_context
|
||||
def command(ctx, base_container, source_repo, force_rebuild, extra_build_args):
|
||||
def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, tag):
|
||||
'''build the specified webapp container'''
|
||||
|
||||
quiet = ctx.obj.quiet
|
||||
@@ -70,8 +71,11 @@ def command(ctx, base_container, source_repo, force_rebuild, extra_build_args):
|
||||
container_build_env["CERC_CONTAINER_BUILD_DOCKERFILE"] = os.path.join(container_build_dir,
|
||||
base_container.replace("/", "-"),
|
||||
"Dockerfile.webapp")
|
||||
webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1]
|
||||
container_build_env["CERC_CONTAINER_BUILD_TAG"] = f"cerc/{webapp_name}:local"
|
||||
if not tag:
|
||||
webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1]
|
||||
container_build_env["CERC_CONTAINER_BUILD_TAG"] = f"cerc/{webapp_name}:local"
|
||||
else:
|
||||
container_build_env["CERC_CONTAINER_BUILD_TAG"] = tag
|
||||
|
||||
build_containers.process_container(None, base_container, container_build_dir, container_build_env, dev_root_path, quiet,
|
||||
verbose, dry_run, continue_on_error)
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
stack_file_name = "stack.yml"
|
||||
spec_file_name = "spec.yml"
|
||||
config_file_name = "config.env"
|
||||
compose_deploy_type = "compose"
|
||||
k8s_kind_deploy_type = "k8s-kind"
|
||||
k8s_deploy_type = "k8s"
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
version: "3.2"
|
||||
|
||||
services:
|
||||
uniswap-interface:
|
||||
image: cerc/uniswap-interface:local
|
||||
restart: on-failure
|
||||
environment:
|
||||
- REACT_APP_INFURA_KEY=${CERC_INFURA_KEY}
|
||||
- REACT_APP_AWS_API_ENDPOINT=${CERC_UNISWAP_GQL}
|
||||
command: ["./build-app.sh"]
|
||||
volumes:
|
||||
- app_builds:/app-builds
|
||||
- ../config/uniswap-interface/build-app.sh:/app/build-app.sh
|
||||
|
||||
uniswap-glob-host:
|
||||
image: cerc/urbit-globs-host:local
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
uniswap-interface:
|
||||
condition: service_completed_successfully
|
||||
command: ["./host-uniswap-glob.sh"]
|
||||
volumes:
|
||||
- app_globs:/app-globs
|
||||
- ../config/uniswap-interface/host-uniswap-glob.sh:/app/host-uniswap-glob.sh
|
||||
ports:
|
||||
- "3000"
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "-v", "localhost", "3000"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 15
|
||||
start_period: 10s
|
||||
|
||||
uniswap-gql-proxy:
|
||||
image: cerc/uniswap-interface:local
|
||||
restart: on-failure
|
||||
command: ["bash", "-c", "yarn proxy-gql"]
|
||||
ports:
|
||||
- "4000"
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "-v", "localhost", "4000"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 15
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
app_builds:
|
||||
app_globs:
|
||||
@@ -0,0 +1,26 @@
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
urbit-fake-ship:
|
||||
restart: unless-stopped
|
||||
image: tloncorp/vere
|
||||
entrypoint: ["bash", "-c", "./run-urbit-ship.sh && ./deploy-uniswap-app.sh && tail -f /dev/null"]
|
||||
volumes:
|
||||
- urbit_data:/urbit
|
||||
- app_builds:/app-builds
|
||||
- app_globs:/app-globs
|
||||
- ../config/urbit/run-urbit-ship.sh:/urbit/run-urbit-ship.sh
|
||||
- ../config/uniswap-interface/deploy-uniswap-app.sh:/urbit/deploy-uniswap-app.sh
|
||||
ports:
|
||||
- "80"
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "-v", "localhost", "80"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 15
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
urbit_data:
|
||||
app_builds:
|
||||
app_globs:
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Check and exit if a deployment already exists (on restarts)
|
||||
if [ -d /app-builds/uniswap/build ]; then
|
||||
echo "Build already exists, remove volume to rebuild"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
yarn build
|
||||
|
||||
# Move build to app-builds so urbit can deploy it
|
||||
mkdir /app-builds/uniswap
|
||||
cp -r ./build /app-builds/uniswap/
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
uniswap_app_build='/app-builds/uniswap/build'
|
||||
uniswap_desk_dir='/urbit/zod/uniswap'
|
||||
|
||||
if [ -d ${uniswap_desk_dir} ]; then
|
||||
echo "Uniswap desk dir already exists, skipping deployment..."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Fire curl requests to perform operations on the ship
|
||||
dojo () {
|
||||
curl -s --data '{"source":{"dojo":"'"$1"'"},"sink":{"stdout":null}}' http://localhost:12321
|
||||
}
|
||||
|
||||
hood () {
|
||||
curl -s --data '{"source":{"dojo":"+hood/'"$1"'"},"sink":{"app":"hood"}}' http://localhost:12321
|
||||
}
|
||||
|
||||
# Create/mount a uniswap desk
|
||||
hood "merge %uniswap our %landscape"
|
||||
hood "mount %uniswap"
|
||||
|
||||
# Loop until the uniswap build appears
|
||||
while [ ! -d ${uniswap_app_build} ]; do
|
||||
echo "Uniswap app build not found, retrying in 5s..."
|
||||
sleep 5
|
||||
done
|
||||
echo "Build found..."
|
||||
|
||||
# Copy over build to desk data dir
|
||||
cp -r ${uniswap_app_build} ${uniswap_desk_dir}
|
||||
|
||||
# Create a mark file for .map file type
|
||||
cat << EOF > "${uniswap_desk_dir}/mar/map.hoon"
|
||||
::
|
||||
:::: /hoon/map/mar
|
||||
:: Mark for js source maps
|
||||
/? 310
|
||||
::
|
||||
=, eyre
|
||||
|_ mud=@
|
||||
++ grow
|
||||
|%
|
||||
++ mime [/application/octet-stream (as-octs:mimes:html (@t mud))]
|
||||
--
|
||||
++ grab
|
||||
|% :: convert from
|
||||
++ mime |=([p=mite q=octs] (@t q.q))
|
||||
++ noun cord :: clam from %noun
|
||||
--
|
||||
++ grad %mime
|
||||
--
|
||||
EOF
|
||||
|
||||
# Create a mark file for .woff file type
|
||||
cat << EOF > "${uniswap_desk_dir}/mar/woff.hoon"
|
||||
|_ dat=octs
|
||||
++ grow
|
||||
|%
|
||||
++ mime [/font/woff dat]
|
||||
--
|
||||
++ grab
|
||||
|%
|
||||
++ mime |=([=mite =octs] octs)
|
||||
++ noun octs
|
||||
--
|
||||
++ grad %mime
|
||||
--
|
||||
EOF
|
||||
|
||||
# Create a mark file for .ttf file type
|
||||
cat << EOF > "${uniswap_desk_dir}/mar/ttf.hoon"
|
||||
|_ dat=octs
|
||||
++ grow
|
||||
|%
|
||||
++ mime [/font/ttf dat]
|
||||
--
|
||||
++ grab
|
||||
|%
|
||||
++ mime |=([=mite =octs] octs)
|
||||
++ noun octs
|
||||
--
|
||||
++ grad %mime
|
||||
--
|
||||
EOF
|
||||
|
||||
rm "${uniswap_desk_dir}/desk.bill"
|
||||
rm "${uniswap_desk_dir}/desk.ship"
|
||||
|
||||
# Commit changes and create a glob
|
||||
hood "commit %uniswap"
|
||||
dojo "-landscape!make-glob %uniswap /build"
|
||||
|
||||
echo "Copying over glob file to mounted volume"
|
||||
mkdir -p /app-globs/uniswap
|
||||
cp /urbit/zod/.urb/put/* /app-globs/uniswap/
|
||||
|
||||
glob_file=$(ls -1 -c zod/.urb/put | head -1)
|
||||
echo "Glob filename: ${glob_file}"
|
||||
|
||||
# Curl and wait for the glob to be hosted
|
||||
glob_url="http://uniswap-glob-host:3000/${glob_file}"
|
||||
|
||||
echo "Checking if glob file hosted at ${glob_url}"
|
||||
while true; do
|
||||
response=$(curl -sL -w "%{http_code}" -o /dev/null "$glob_url")
|
||||
|
||||
if [ $response -eq 200 ]; then
|
||||
echo "File found at $glob_url"
|
||||
break # Exit the loop if the file is found
|
||||
else
|
||||
echo "File not found. Retrying in a few seconds..."
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
|
||||
glob_hash=$(echo "$glob_file" | sed "s/glob-\([a-z0-9\.]*\).glob/\1/")
|
||||
|
||||
# Update the docket file
|
||||
cat << EOF > "${uniswap_desk_dir}/desk.docket-0"
|
||||
:~ title+'Uniswap'
|
||||
info+'Self-hosted uniswap frontend.'
|
||||
color+0xcd.75df
|
||||
image+'https://logowik.com/content/uploads/images/uniswap-uni7403.jpg'
|
||||
base+'uniswap'
|
||||
glob-http+['http://uniswap-glob-host:3000/${glob_file}' ${glob_hash}]
|
||||
version+[0 0 1]
|
||||
website+'https://uniswap.org/'
|
||||
license+'MIT'
|
||||
==
|
||||
EOF
|
||||
|
||||
# Commit changes and install the app
|
||||
hood "commit %uniswap"
|
||||
hood "install our %uniswap"
|
||||
|
||||
echo "Uniswap app installed"
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Use config from mounted volume (when running web-app along with watcher stack)
|
||||
echo "Waiting for uniswap app glob"
|
||||
while [ ! -d /app-globs/uniswap ]; do
|
||||
echo "Glob directory not found, retrying in 5 seconds..."
|
||||
sleep 5
|
||||
done
|
||||
|
||||
|
||||
# Copy to a new globs directory
|
||||
mkdir -p globs
|
||||
cp -r /app-globs/uniswap/* ./globs
|
||||
|
||||
# Serve the glob file
|
||||
cd globs
|
||||
echo "Hosting glob file at port 3000"
|
||||
python3 -m http.server 3000 --bind 0.0.0.0
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/bin/bash
|
||||
|
||||
# $1: Glob file URL (eg. https://xyz.com/glob-abcd.glob)
|
||||
# $2: Uniswap desk dir (default: ./zod/uniswap)
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Glob file URL arg not provided"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
glob_url=$1
|
||||
glob_file=$(basename "$glob_url")
|
||||
glob_hash=$(echo "$glob_file" | sed "s/glob-\([a-z0-9\.]*\).glob/\1/")
|
||||
echo "Using glob file ${glob_file}"
|
||||
|
||||
# Default desk dir: ./zod/uniswap
|
||||
uniswap_desk_dir="${2:-./zod/uniswap}"
|
||||
|
||||
echo "Using ${uniswap_desk_dir} as the Uniswap desk dir path"
|
||||
|
||||
# Fire curl requests to perform operations on the ship
|
||||
dojo () {
|
||||
curl -s --data '{"source":{"dojo":"'"$1"'"},"sink":{"stdout":null}}' http://localhost:12321
|
||||
}
|
||||
|
||||
hood () {
|
||||
curl -s --data '{"source":{"dojo":"+hood/'"$1"'"},"sink":{"app":"hood"}}' http://localhost:12321
|
||||
}
|
||||
|
||||
# Create/mount a uniswap desk
|
||||
hood "merge %uniswap our %landscape"
|
||||
hood "mount %uniswap"
|
||||
|
||||
# Create a mark file for .map file type
|
||||
cat << EOF > "${uniswap_desk_dir}/mar/map.hoon"
|
||||
::
|
||||
:::: /hoon/map/mar
|
||||
:: Mark for js source maps
|
||||
/? 310
|
||||
::
|
||||
=, eyre
|
||||
|_ mud=@
|
||||
++ grow
|
||||
|%
|
||||
++ mime [/application/octet-stream (as-octs:mimes:html (@t mud))]
|
||||
--
|
||||
++ grab
|
||||
|% :: convert from
|
||||
++ mime |=([p=mite q=octs] (@t q.q))
|
||||
++ noun cord :: clam from %noun
|
||||
--
|
||||
++ grad %mime
|
||||
--
|
||||
EOF
|
||||
|
||||
# Create a mark file for .woff file type
|
||||
cat << EOF > "${uniswap_desk_dir}/mar/woff.hoon"
|
||||
|_ dat=octs
|
||||
++ grow
|
||||
|%
|
||||
++ mime [/font/woff dat]
|
||||
--
|
||||
++ grab
|
||||
|%
|
||||
++ mime |=([=mite =octs] octs)
|
||||
++ noun octs
|
||||
--
|
||||
++ grad %mime
|
||||
--
|
||||
EOF
|
||||
|
||||
# Create a mark file for .ttf file type
|
||||
cat << EOF > "${uniswap_desk_dir}/mar/ttf.hoon"
|
||||
|_ dat=octs
|
||||
++ grow
|
||||
|%
|
||||
++ mime [/font/ttf dat]
|
||||
--
|
||||
++ grab
|
||||
|%
|
||||
++ mime |=([=mite =octs] octs)
|
||||
++ noun octs
|
||||
--
|
||||
++ grad %mime
|
||||
--
|
||||
EOF
|
||||
|
||||
rm "${uniswap_desk_dir}/desk.bill"
|
||||
rm "${uniswap_desk_dir}/desk.ship"
|
||||
|
||||
# Update the docket file
|
||||
cat << EOF > "${uniswap_desk_dir}/desk.docket-0"
|
||||
:~ title+'Uniswap'
|
||||
info+'Self-hosted uniswap frontend.'
|
||||
color+0xcd.75df
|
||||
image+'https://logowik.com/content/uploads/images/uniswap-uni7403.jpg'
|
||||
base+'uniswap'
|
||||
glob-http+['${glob_url}' ${glob_hash}]
|
||||
version+[0 0 1]
|
||||
website+'https://uniswap.org/'
|
||||
license+'MIT'
|
||||
==
|
||||
EOF
|
||||
|
||||
# Commit changes and install the app
|
||||
hood "commit %uniswap"
|
||||
hood "install our %uniswap"
|
||||
|
||||
echo "Uniswap app installed"
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# $1: Remote user host
|
||||
# $2: Path to run the app installation in (where urbit ship dir is located)
|
||||
# $3: Glob file URL (eg. https://xyz.com/glob-abcd.glob)
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Usage: $0 <username@remote_host> </path/to/remote/folder> <glob_url>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remote_user_host="$1"
|
||||
remote_folder="$2"
|
||||
glob_url="$3"
|
||||
|
||||
installation_script="./install-uniswap-app.sh"
|
||||
|
||||
ssh "$remote_user_host" "cd $remote_folder && bash -s $glob_url" < "$installation_script"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
pier_dir="/urbit/zod"
|
||||
|
||||
# Run urbit ship in daemon mode
|
||||
# Check if the directory exists
|
||||
if [ -d "$pier_dir" ]; then
|
||||
echo "Pier directory already exists, rebooting..."
|
||||
urbit -d zod
|
||||
else
|
||||
echo "Creating a new fake ship..."
|
||||
urbit -d -F zod
|
||||
fi
|
||||
@@ -24,6 +24,8 @@ RUN \
|
||||
&& su ${USERNAME} -c "npm config -g set prefix ${NPM_GLOBAL}" \
|
||||
# Install eslint
|
||||
&& su ${USERNAME} -c "umask 0002 && npm install -g eslint" \
|
||||
# Install semver
|
||||
&& su ${USERNAME} -c "umask 0002 && npm install -g semver" \
|
||||
&& npm cache clean --force > /dev/null 2>&1
|
||||
|
||||
# [Optional] Uncomment this section to install additional OS packages.
|
||||
|
||||
@@ -4,10 +4,12 @@ if [ -n "$CERC_SCRIPT_DEBUG" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
CERC_MIN_NEXTVER=13.4.2
|
||||
|
||||
CERC_NEXT_VERSION="${CERC_NEXT_VERSION:-keep}"
|
||||
CERC_BUILD_TOOL="${CERC_BUILD_TOOL}"
|
||||
if [ -z "$CERC_BUILD_TOOL" ]; then
|
||||
if [ -f "yarn.lock" ] && [ ! -f "package-lock.json" ]; then
|
||||
if [ -f "yarn.lock" ]; then
|
||||
CERC_BUILD_TOOL=yarn
|
||||
else
|
||||
CERC_BUILD_TOOL=npm
|
||||
@@ -101,13 +103,35 @@ cat package.dist | jq '.scripts.cerc_compile = "next experimental-compile"' | jq
|
||||
CUR_NEXT_VERSION="`jq -r '.dependencies.next' package.json`"
|
||||
|
||||
if [ "$CERC_NEXT_VERSION" != "keep" ] && [ "$CUR_NEXT_VERSION" != "$CERC_NEXT_VERSION" ]; then
|
||||
echo "Changing 'next' version specifier from '$CUR_NEXT_VERSION' to '$CERC_NEXT_VERSION' (set with --build-arg CERC_NEXT_VERSION)"
|
||||
echo "Changing 'next' version specifier from '$CUR_NEXT_VERSION' to '$CERC_NEXT_VERSION' (set with '--extra-build-args \"--build-arg CERC_NEXT_VERSION=$CERC_NEXT_VERSION\"')"
|
||||
cat package.json | jq ".dependencies.next = \"$CERC_NEXT_VERSION\"" | sponge package.json
|
||||
else
|
||||
echo "'next' version specifier '$CUR_NEXT_VERSION' (override with --build-arg CERC_NEXT_VERSION)"
|
||||
fi
|
||||
|
||||
$CERC_BUILD_TOOL install || exit 1
|
||||
|
||||
CUR_NEXT_VERSION=`jq -r '.version' node_modules/next/package.json`
|
||||
|
||||
semver -p -r ">=$CERC_MIN_NEXTVER" $CUR_NEXT_VERSION
|
||||
if [ $? -ne 0 ]; then
|
||||
cat <<EOF
|
||||
|
||||
###############################################################################
|
||||
|
||||
WARNING: 'next' $CUR_NEXT_VERSION < minimum version $CERC_MIN_NEXTVER.
|
||||
|
||||
Attempting to build with '^$CERC_MIN_NEXTVER'. If this fails, you should upgrade
|
||||
the dependency in your webapp, or specify an explicit 'next' version
|
||||
to use for the build with:
|
||||
|
||||
--extra-build-args "--build-arg CERC_NEXT_VERSION=<version>"
|
||||
|
||||
###############################################################################
|
||||
|
||||
EOF
|
||||
cat package.json | jq ".dependencies.next = \"^$CERC_MIN_NEXTVER\"" | sponge package.json
|
||||
$CERC_BUILD_TOOL install || exit 1
|
||||
fi
|
||||
|
||||
$CERC_BUILD_TOOL run cerc_compile || exit 1
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM node:18.17.1-alpine3.18
|
||||
|
||||
RUN apk --update --no-cache add git make alpine-sdk bash
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN echo "Building uniswap-interface" && \
|
||||
yarn
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the uniswap-interface image
|
||||
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||
|
||||
# See: https://stackoverflow.com/a/246128/1701505
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
docker build -t cerc/uniswap-interface:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/uniswap-interface
|
||||
@@ -0,0 +1,7 @@
|
||||
FROM python:3.13.0a2-alpine3.18
|
||||
|
||||
RUN apk --update --no-cache add alpine-sdk jq bash curl wget
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT [ "bash" ]
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the urbit-globs-host image
|
||||
|
||||
source ${CERC_CONTAINER_BASE_DIR}/build-base.sh
|
||||
|
||||
# See: https://stackoverflow.com/a/246128/1701505
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
docker build -t cerc/urbit-globs-host:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} ${SCRIPT_DIR}
|
||||
@@ -59,3 +59,4 @@ cerc/ponder
|
||||
cerc/nitro-rpc-client
|
||||
cerc/watcher-merkl-sushiswap-v3
|
||||
cerc/watcher-sushiswap-v3
|
||||
cerc/uniswap-interface
|
||||
|
||||
@@ -49,3 +49,4 @@ github.com/cerc-io/mobymask-snap
|
||||
github.com/cerc-io/ponder
|
||||
github.com/cerc-io/merkl-sushiswap-v3-watcher-ts
|
||||
github.com/cerc-io/sushiswap-v3-watcher-ts
|
||||
github.com/cerc-io/uniswap-interface
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# Self-hosted Uniswap Frontend
|
||||
|
||||
Instructions to setup and deploy Uniswap app on Urbit
|
||||
|
||||
Build and deploy:
|
||||
|
||||
- Urbit
|
||||
- Uniswap app
|
||||
|
||||
## Setup
|
||||
|
||||
Clone required repositories:
|
||||
|
||||
```bash
|
||||
laconic-so --stack uniswap-urbit-app setup-repositories --pull
|
||||
|
||||
# If this throws an error as a result of being already checked out to a branch/tag in a repo, remove the repositories mentioned below and re-run the command
|
||||
```
|
||||
|
||||
Build the container images:
|
||||
|
||||
```bash
|
||||
laconic-so --stack uniswap-urbit-app build-containers
|
||||
```
|
||||
|
||||
## Create a deployment
|
||||
|
||||
First, create a spec file for the deployment, which will map the stack's ports and volumes to the host:
|
||||
|
||||
```bash
|
||||
laconic-so --stack uniswap-urbit-app deploy init --output uniswap-urbit-app-spec.yml
|
||||
```
|
||||
|
||||
### Ports
|
||||
|
||||
Edit `network` in spec file to map container ports to same ports in host
|
||||
|
||||
```
|
||||
...
|
||||
network:
|
||||
ports:
|
||||
urbit-fake-ship:
|
||||
- '8080:80'
|
||||
uniswap-glob-host:
|
||||
- '3000:3000'
|
||||
uniswap-gql-proxy:
|
||||
- '4000:4000'
|
||||
...
|
||||
```
|
||||
|
||||
### Data volumes
|
||||
|
||||
Container data volumes are bind-mounted to specified paths in the host filesystem.
|
||||
The default setup (generated by `laconic-so deploy init`) places the volumes in the `./data` subdirectory of the deployment directory. The default mappings can be customized by editing the "spec" file generated by `laconic-so deploy init`.
|
||||
|
||||
---
|
||||
|
||||
Once you've made any needed changes to the spec file, create a deployment from it:
|
||||
|
||||
```bash
|
||||
laconic-so --stack uniswap-urbit-app deploy create --spec-file uniswap-urbit-app-spec.yml --deployment-dir uniswap-urbit-app-deployment
|
||||
```
|
||||
|
||||
## Set env variables
|
||||
|
||||
Inside the deployment directory, open the file `config.env` and add variable for infura key :
|
||||
|
||||
```bash
|
||||
# External RPC endpoints
|
||||
# https://docs.infura.io/getting-started#2-create-an-api-key
|
||||
CERC_INFURA_KEY=
|
||||
|
||||
# Uniswap API GQL Endpoint
|
||||
# Set this to GQL proxy server endpoint for uniswap app
|
||||
# (Eg. http://localhost:4000/graphql)
|
||||
CERC_UNISWAP_GQL=
|
||||
```
|
||||
|
||||
## Start the stack
|
||||
|
||||
Start the deployment:
|
||||
|
||||
```bash
|
||||
laconic-so deployment --dir uniswap-urbit-app-deployment start
|
||||
```
|
||||
|
||||
* List and check the health status of all the containers using `docker ps` and wait for them to be `healthy`
|
||||
|
||||
* Run the following to get login password for Urbit web interface:
|
||||
|
||||
```bash
|
||||
laconic-so deployment --dir uniswap-urbit-app-deployment exec urbit-fake-ship "curl -s --data '{\"source\":{\"dojo\":\"+code\"},\"sink\":{\"stdout\":null}}' http://localhost:12321"
|
||||
|
||||
# Expected output: "<PASSWORD>\n"%
|
||||
```
|
||||
|
||||
* Open the Urbit web UI at http://localhost:8080 and use the `PASSWORD` from previous step to login
|
||||
|
||||
* The uniswap app is not available when starting stack for the first time. Check `urbit-fake-ship` logs to see that app has installed
|
||||
```
|
||||
laconic-so deployment --dir uniswap-urbit-app-deployment logs -f
|
||||
|
||||
# Expected output:
|
||||
# laconic-3ccf7ee79bdae874-urbit-fake-ship-1 | docket: fetching %http glob for %uniswap desk
|
||||
# laconic-3ccf7ee79bdae874-urbit-fake-ship-1 | ">="">="Uniswap app installed
|
||||
```
|
||||
|
||||
* The uniswap app will be now visible at http://localhost:8080
|
||||
|
||||
## Clean up
|
||||
|
||||
To stop all uniswap-urbit-app services running in the background, while preserving chain data:
|
||||
|
||||
```bash
|
||||
laconic-so deployment --dir uniswap-urbit-app-deployment stop
|
||||
```
|
||||
|
||||
To stop all uniswap-urbit-app services and also delete data:
|
||||
|
||||
```bash
|
||||
laconic-so deployment --dir uniswap-urbit-app-deployment stop --delete-volumes
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
version: "0.1"
|
||||
name: uniswap-urbit-app
|
||||
repos:
|
||||
- github.com/cerc-io/uniswap-interface@laconic # TODO: Use release
|
||||
containers:
|
||||
- cerc/uniswap-interface
|
||||
- cerc/urbit-globs-host
|
||||
pods:
|
||||
- uniswap-interface
|
||||
- uniswap-urbit
|
||||
@@ -64,10 +64,10 @@ class DockerDeployer(Deployer):
|
||||
except DockerException as e:
|
||||
raise DeployerException(e)
|
||||
|
||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, detach=False):
|
||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, ports=[], detach=False):
|
||||
try:
|
||||
return self.docker.run(image=image, command=command, user=user, volumes=volumes,
|
||||
entrypoint=entrypoint, envs=env, detach=detach, publish_all=True)
|
||||
entrypoint=entrypoint, envs=env, detach=detach, publish=ports, publish_all=len(ports) == 0)
|
||||
except DockerException as e:
|
||||
raise DeployerException(e)
|
||||
|
||||
|
||||
@@ -271,8 +271,10 @@ def _make_cluster_context(ctx, stack, include, exclude, cluster, env_file):
|
||||
|
||||
if cluster is None:
|
||||
# Create default unique, stable cluster name from confile file path and stack name if provided
|
||||
# TODO: change this to the config file path
|
||||
path = os.path.realpath(sys.argv[0])
|
||||
if deployment:
|
||||
path = os.path.realpath(os.path.abspath(compose_dir))
|
||||
else:
|
||||
path = "internal"
|
||||
unique_cluster_descriptor = f"{path},{stack},{include},{exclude}"
|
||||
if ctx.debug:
|
||||
print(f"pre-hash descriptor: {unique_cluster_descriptor}")
|
||||
|
||||
@@ -44,7 +44,7 @@ class Deployer(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, detach=False):
|
||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, ports=[], detach=False):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.deploy.stack import Stack
|
||||
from stack_orchestrator.deploy.spec import Spec
|
||||
|
||||
@@ -26,13 +27,13 @@ class DeploymentContext:
|
||||
stack: Stack
|
||||
|
||||
def get_stack_file(self):
|
||||
return self.deployment_dir.joinpath("stack.yml")
|
||||
return self.deployment_dir.joinpath(constants.stack_file_name)
|
||||
|
||||
def get_spec_file(self):
|
||||
return self.deployment_dir.joinpath("spec.yml")
|
||||
return self.deployment_dir.joinpath(constants.spec_file_name)
|
||||
|
||||
def get_env_file(self):
|
||||
return self.deployment_dir.joinpath("config.env")
|
||||
return self.deployment_dir.joinpath(constants.config_file_name)
|
||||
|
||||
# TODO: implement me
|
||||
def get_cluster_name(self):
|
||||
|
||||
@@ -25,7 +25,7 @@ from stack_orchestrator import constants
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import (get_stack_file_path, get_parsed_deployment_spec, get_parsed_stack_config,
|
||||
global_options, get_yaml, get_pod_list, get_pod_file_path, pod_has_scripts,
|
||||
get_pod_script_paths, get_plugin_code_paths, error_exit)
|
||||
get_pod_script_paths, get_plugin_code_paths, error_exit, env_var_map_from_file)
|
||||
from stack_orchestrator.deploy.deploy_types import LaconicStackSetupCommand
|
||||
from stack_orchestrator.deploy.deployer_factory import getDeployerConfigGenerator
|
||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||
@@ -244,12 +244,13 @@ def _parse_config_variables(variable_values: str):
|
||||
variable_name = variable_value_pair[0]
|
||||
variable_value = variable_value_pair[1]
|
||||
result_values[variable_name] = variable_value
|
||||
result = {"config": result_values}
|
||||
result = result_values
|
||||
return result
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--config", help="Provide config variables for the deployment")
|
||||
@click.option("--config-file", help="Provide config variables in a file for the deployment")
|
||||
@click.option("--kube-config", help="Provide a config file for a k8s deployment")
|
||||
@click.option("--image-registry", help="Provide a container image registry url for this k8s cluster")
|
||||
@click.option("--output", required=True, help="Write yaml spec file here")
|
||||
@@ -257,14 +258,15 @@ def _parse_config_variables(variable_values: str):
|
||||
help="Map ports to the host as one of: any-variable-random (default), "
|
||||
"localhost-same, any-same, localhost-fixed-random, any-fixed-random")
|
||||
@click.pass_context
|
||||
def init(ctx, config, kube_config, image_registry, output, map_ports_to_host):
|
||||
def init(ctx, config, config_file, kube_config, image_registry, output, map_ports_to_host):
|
||||
stack = global_options(ctx).stack
|
||||
deployer_type = ctx.obj.deployer.type
|
||||
deploy_command_context = ctx.obj
|
||||
return init_operation(
|
||||
deploy_command_context,
|
||||
stack, deployer_type,
|
||||
config, kube_config,
|
||||
config, config_file,
|
||||
kube_config,
|
||||
image_registry,
|
||||
output,
|
||||
map_ports_to_host)
|
||||
@@ -272,7 +274,8 @@ def init(ctx, config, kube_config, image_registry, output, map_ports_to_host):
|
||||
|
||||
# The init command's implementation is in a separate function so that we can
|
||||
# call it from other commands, bypassing the click decoration stuff
|
||||
def init_operation(deploy_command_context, stack, deployer_type, config, kube_config, image_registry, output, map_ports_to_host):
|
||||
def init_operation(deploy_command_context, stack, deployer_type, config,
|
||||
config_file, kube_config, image_registry, output, map_ports_to_host):
|
||||
yaml = get_yaml()
|
||||
default_spec_file_content = call_stack_deploy_init(deploy_command_context)
|
||||
spec_file_content = {"stack": stack, constants.deploy_to_key: deployer_type}
|
||||
@@ -292,12 +295,22 @@ def init_operation(deploy_command_context, stack, deployer_type, config, kube_co
|
||||
if default_spec_file_content:
|
||||
spec_file_content.update(default_spec_file_content)
|
||||
config_variables = _parse_config_variables(config)
|
||||
# Implement merge, since update() overwrites
|
||||
if config_variables:
|
||||
# Implement merge, since update() overwrites
|
||||
orig_config = spec_file_content.get("config", {})
|
||||
new_config = config_variables["config"]
|
||||
new_config = config_variables
|
||||
merged_config = {**new_config, **orig_config}
|
||||
spec_file_content.update({"config": merged_config})
|
||||
if config_file:
|
||||
config_file_path = Path(config_file)
|
||||
if not config_file_path.exists():
|
||||
error_exit(f"config file: {config_file} does not exist")
|
||||
config_file_variables = env_var_map_from_file(config_file_path)
|
||||
if config_file_variables:
|
||||
orig_config = spec_file_content.get("config", {})
|
||||
new_config = config_file_variables
|
||||
merged_config = {**new_config, **orig_config}
|
||||
spec_file_content.update({"config": merged_config})
|
||||
if opts.o.debug:
|
||||
print(f"Creating spec file for stack: {stack} with content: {spec_file_content}")
|
||||
|
||||
@@ -368,10 +381,10 @@ def create_operation(deployment_command_context, spec_file, deployment_dir, netw
|
||||
error_exit(f"{deployment_dir_path} already exists")
|
||||
os.mkdir(deployment_dir_path)
|
||||
# Copy spec file and the stack file into the deployment dir
|
||||
copyfile(spec_file, deployment_dir_path.joinpath("spec.yml"))
|
||||
copyfile(spec_file, deployment_dir_path.joinpath(constants.spec_file_name))
|
||||
copyfile(stack_file, deployment_dir_path.joinpath(os.path.basename(stack_file)))
|
||||
# Copy any config varibles from the spec file into an env file suitable for compose
|
||||
_write_config_file(spec_file, deployment_dir_path.joinpath("config.env"))
|
||||
_write_config_file(spec_file, deployment_dir_path.joinpath(constants.config_file_name))
|
||||
# Copy any k8s config file into the deployment dir
|
||||
if deployment_type == "k8s":
|
||||
_write_kube_config_file(Path(parsed_spec[constants.kube_config_key]),
|
||||
|
||||
@@ -17,9 +17,10 @@ from kubernetes import client
|
||||
from typing import Any, List, Set
|
||||
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import env_var_map_from_file
|
||||
from stack_orchestrator.deploy.k8s.helpers import named_volumes_from_pod_files, volume_mounts_for_service, volumes_for_pod_files
|
||||
from stack_orchestrator.deploy.k8s.helpers import get_node_pv_mount_path
|
||||
from stack_orchestrator.deploy.k8s.helpers import env_var_map_from_file, envs_from_environment_variables_map
|
||||
from stack_orchestrator.deploy.k8s.helpers import envs_from_environment_variables_map
|
||||
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names, images_for_deployment
|
||||
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
||||
from stack_orchestrator.deploy.spec import Spec
|
||||
|
||||
@@ -230,7 +230,7 @@ class K8sDeployer(Deployer):
|
||||
log_data = self.core_api.read_namespaced_pod_log(k8s_pod_name, namespace="default", container="test")
|
||||
return log_stream_from_string(log_data)
|
||||
|
||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, detach=False):
|
||||
def run(self, image: str, command=None, user=None, volumes=None, entrypoint=None, env={}, ports=[], detach=False):
|
||||
# We need to figure out how to do this -- check why we're being called first
|
||||
pass
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
from kubernetes import client
|
||||
from dotenv import dotenv_values
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
@@ -224,7 +223,3 @@ def generate_kind_config(deployment_dir: Path):
|
||||
f"{port_mappings_yml}\n"
|
||||
f"{mounts_yml}\n"
|
||||
)
|
||||
|
||||
|
||||
def env_var_map_from_file(file: Path) -> Mapping[str, str]:
|
||||
return dotenv_values(file)
|
||||
|
||||
@@ -98,6 +98,7 @@ def create(ctx, deployment_dir, image, url, kube_config, image_registry, env_fil
|
||||
stack,
|
||||
"k8s",
|
||||
None,
|
||||
env_file,
|
||||
kube_config,
|
||||
image_registry,
|
||||
spec_file_name,
|
||||
|
||||
@@ -27,13 +27,16 @@ from dotenv import dotenv_values
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.deploy.deployer_factory import getDeployer
|
||||
|
||||
WEBAPP_PORT = 3000
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--image", help="image to deploy", required=True)
|
||||
@click.option("--env-file", help="environment file for webapp")
|
||||
@click.option("--port", help="port to use (default random)")
|
||||
@click.pass_context
|
||||
def command(ctx, image, env_file):
|
||||
'''build the specified webapp container'''
|
||||
def command(ctx, image, env_file, port):
|
||||
'''run the specified webapp container'''
|
||||
|
||||
env = {}
|
||||
if env_file:
|
||||
@@ -49,10 +52,13 @@ def command(ctx, image, env_file):
|
||||
compose_project_name=cluster,
|
||||
compose_env_file=None)
|
||||
|
||||
container = deployer.run(image, command=[], user=None, volumes=[], entrypoint=None, env=env, detach=True)
|
||||
ports = []
|
||||
if port:
|
||||
ports = [(port, WEBAPP_PORT)]
|
||||
container = deployer.run(image, command=[], user=None, volumes=[], entrypoint=None, env=env, ports=ports, detach=True)
|
||||
|
||||
# Make configurable?
|
||||
webappPort = "3000/tcp"
|
||||
webappPort = f"{WEBAPP_PORT}/tcp"
|
||||
# TODO: This assumes a Docker container object...
|
||||
if webappPort in container.network_settings.ports:
|
||||
mapping = container.network_settings.ports[webappPort][0]
|
||||
|
||||
@@ -18,6 +18,8 @@ import os.path
|
||||
import sys
|
||||
import ruamel.yaml
|
||||
from pathlib import Path
|
||||
from dotenv import dotenv_values
|
||||
from typing import Mapping
|
||||
|
||||
|
||||
def include_exclude_check(s, include, exclude):
|
||||
@@ -178,3 +180,7 @@ def global_options2(ctx):
|
||||
def error_exit(s):
|
||||
print(f"ERROR: {s}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def env_var_map_from_file(file: Path) -> Mapping[str, str]:
|
||||
return dotenv_values(file)
|
||||
|
||||
Reference in New Issue
Block a user