diff --git a/.circleci/config.yml b/.circleci/config.yml index ac2308f350..67e4df9450 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -129,18 +129,6 @@ executors: REDIS_PASSWORD: "fm_redis" jobs: - set_docker_tag_for_ci_images: - docker: *base_docker - steps: - - run: - name: Set a common docker tag - command: | - echo "export SAFE_TAG=${CIRCLE_TAG:-$(echo -n $CIRCLE_BRANCH | md5sum | cut -d" " -f1)}"| tee -a $BASH_ENV /home/circleci/.env - - persist_to_workspace: - root: /home/circleci/ - paths: - - .env - build_flowdb: machine: image: circleci/classic:201808-01 @@ -148,6 +136,10 @@ jobs: environment: *flowdb_env steps: - checkout + - run: + name: Set a common docker tag + command: | + echo "export SAFE_TAG=${CIRCLE_TAG:-$(echo -n $CIRCLE_BRANCH | md5sum | cut -d" " -f1)}"| tee -a $BASH_ENV /home/circleci/.env - attach_workspace: at: /home/circleci/ - restore_cache: @@ -462,7 +454,9 @@ jobs: parameters: component: type: enum - enum: ["flowmachine", "flowauth", "flowapi"] + enum: ["flowmachine", "flowauth", "flowapi", "flowkit-examples"] + component_path: + type: string docker: - image: circleci/python:3.7 working_directory: /home/circleci/project/ @@ -479,7 +473,7 @@ jobs: name: Build <> image command: | echo "Tagging as $CIRCLE_SHA1" - docker build -t flowminder/<>:$CIRCLE_SHA1 ./<> + docker build -t flowminder/<>:$CIRCLE_SHA1 <> - run: name: Push images to Docker cloud command: | @@ -518,6 +512,18 @@ jobs: path: /home/circleci/project/ - run: ./quick_start.sh - run: ./quick_start.sh stop + - run: ./quick_start.sh examples smaller_data + - run: ./quick_start.sh stop + #- run: ./quick_start.sh larger_data # Synth data spinup with this data volume is very slow so not testing it right now on CI + #- run: ./quick_start.sh stop + - run: + name: Check all containers are down + command: | + RUNNING=`docker ps -q` + if [[ "$RUNNING" != "" ]] + then + exit 1 + fi build_docs: parameters: @@ -631,7 +637,7 @@ jobs: command: | export DOCKER_USER=$DOCKER_CLOUD_USER export DOCKER_PASS=$DOCKER_CLOUD_PASSWORD - for IMAGE in flowapi flowmachine flowdb flowdb-synthetic-data flowdb-testdata flowauth; do + for IMAGE in flowapi flowmachine flowdb flowdb-synthetic-data flowdb-testdata flowauth flowkit-examples; do docker-retag flowminder/$IMAGE:$CIRCLE_SHA1 ${<< parameters.tag >>:-latest} done @@ -642,11 +648,7 @@ jobs: workflows: run_build_pipeline: jobs: - - set_docker_tag_for_ci_images: - <<: *run_always_org_context - build_flowdb: - requires: - - set_docker_tag_for_ci_images <<: *run_always_org_context - install_flowmachine_deps: <<: *run_always_org_context @@ -679,11 +681,15 @@ workflows: requires: - build_flowdb <<: *run_always_org_context + - build_docker_image: + name: build_examples + component: flowkit-examples + component_path: . + <<: *run_always_org_context - build_docker_image: name: build_flowmachine component: flowmachine - requires: - - run_flowmachine_tests + component_path: flowmachine <<: *run_always_org_context - build_docs: name: build_docs @@ -694,29 +700,24 @@ workflows: - build_docker_image: name: build_flowauth component: flowauth - requires: - - run_flowauth_backend_tests - - run_flowauth_frontend_tests + component_path: flowauth <<: *run_always_org_context - build_docker_image: name: build_flowapi component: flowapi - requires: - - run_flowkit_api_tests + component_path: flowapi <<: *run_always_org_context - build_python_wheel: name: build_flowclient_wheel project_name: flowclient requires: - - run_flowclient_tests - - integration_tests + - install_flowmachine_deps <<: *run_always_org_context - build_python_wheel: name: build_flowmachine_wheel project_name: flowmachine requires: - - run_flowmachine_tests - - integration_tests + - install_flowmachine_deps <<: *run_always_org_context - test_quickstart: requires: @@ -724,6 +725,7 @@ workflows: - build_flowapi - build_flowmachine - build_flowauth + - build_examples <<: *run_always_org_context - integration_tests: requires: @@ -734,32 +736,40 @@ workflows: name: retag_master_branch requires: - test_quickstart + - integration_tests - build_docs + - run_flowkit_api_tests + - run_flowauth_backend_tests + - run_flowauth_frontend_tests + - run_flowclient_tests <<: *master_only_org_context - retag_images: name: retag_tagged_build requires: - test_quickstart + - integration_tests - build_docs + - run_flowkit_api_tests + - run_flowauth_backend_tests + - run_flowauth_frontend_tests + - run_flowclient_tests tag: CIRCLE_TAG <<: *tag_only_org_context - push_wheel: name: push_flowclient_wheel project_name: flowclient requires: - - build_flowclient_wheel + - retag_tagged_build <<: *tag_only_org_context - push_wheel: name: push_flowmachine_wheel project_name: flowmachine requires: - - build_flowmachine_wheel + - retag_tagged_build <<: *tag_only_org_context - build_docs: name: Deploy docs deploy: true requires: - - build_docs - retag_tagged_build - - push_flowclient_wheel <<: *tag_only_org_context diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..91244e8c51 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# This .dockerignore file follows the "whitelisting" approach described here: +# https://youknowfordevs.com/2018/12/07/getting-control-of-your-dockerignore-files.html + +# +# First, exclude everything by default: +# + +* + +# +# Now un-exclude only those folders and files that are needed for building the image: +# +!docs/source/worked_examples/*.ipynb + +!flowmachine/flowmachine +!flowmachine/Pipfile* +!flowmachine/setup.cfg +!flowmachine/setup.py +!flowmachine/README.md +!flowmachine/versioneer.py +!flowmachine/MANIFEST.in + +!flowclient/flowclient +!flowclient/Pipfile* +!flowclient/setup.cfg +!flowclient/setup.py +!flowclient/README.md +!flowclient/versioneer.py +!flowclient/MANIFEST.in \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee4092831..c513b20d4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Error is displayed in the add group form of Flowauth if group name already exists. [#709](https://github.com/Flowminder/FlowKit/issues/709) ### Added +- FlowKit's worked examples are now Dockerized, and available as part of the quick setup script [#614](https://github.com/Flowminder/FlowKit/issues/614) ### Changed diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..8190cbe55c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Worked examples docker container +# Provides all worked examples from the FlowKit docs in a ready-to-go JupyterLab install +# + +FROM jupyter/scipy-notebook + +RUN rm -rf /home/$NB_USER/work + +COPY docs/source/worked_examples/*.ipynb /home/$NB_USER/ +COPY flowmachine /flowmachine +COPY flowclient /flowclient +RUN pip install folium /flowclient /flowmachine && \ + fix-permissions $CONDA_DIR && \ + fix-permissions /home/$NB_USER && \ + cd /home/$NB_USER/ && jupyter trust -y * \ No newline at end of file diff --git a/Makefile b/Makefile index e98d4d4831..ee2252e4f5 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ DOCKER_COMPOSE_FILE ?= docker-compose.yml DOCKER_COMPOSE_FILE_BUILD ?= docker-compose-build.yml FLOWDB_SERVICES ?= flowdb_testdata -DOCKER_SERVICES ?= $(FLOWDB_SERVICES) flowapi flowmachine flowauth flowmachine_query_locker +DOCKER_SERVICES ?= $(FLOWDB_SERVICES) flowapi flowmachine flowauth flowmachine_query_locker worked_examples export DOCKER_FLOWDB_HOST=$(word 1, $(FLOWDB_SERVICES)) @@ -108,6 +108,16 @@ flowauth-build: docker-compose -f $(DOCKER_COMPOSE_FILE) -f $(DOCKER_COMPOSE_FILE_BUILD) build flowauth +worked_examples-up: worked_examples-build + docker-compose -f $(DOCKER_COMPOSE_FILE) -f $(DOCKER_COMPOSE_FILE_BUILD) up -d --build worked_examples + +worked_examples-down: + docker-compose -f $(DOCKER_COMPOSE_FILE) rm -f -s -v worked_examples + +worked_examples-build: + docker-compose -f $(DOCKER_COMPOSE_FILE) -f $(DOCKER_COMPOSE_FILE_BUILD) build worked_examples + + flowmachine_query_locker-up: docker-compose -f $(DOCKER_COMPOSE_FILE) up -d flowmachine_query_locker diff --git a/development_environment b/development_environment index 4c09fadf05..0d3199561a 100644 --- a/development_environment +++ b/development_environment @@ -116,3 +116,7 @@ OUTPUT_ROOT_DIR=/docker-entrypoint-initdb.d # Integration tests FLOWKIT_INTEGRATION_TESTS_DISABLE_AUTOSTART_SERVERS=FALSE + +# Worked examples + +WORKED_EXAMPLES_PORT=8888 \ No newline at end of file diff --git a/docker-compose-build.yml b/docker-compose-build.yml index 3a7646ab85..cf764f9646 100644 --- a/docker-compose-build.yml +++ b/docker-compose-build.yml @@ -5,7 +5,7 @@ # DOCKER COMPOSE BUILDER FOR FLOWKIT # -version: '3.7' +version: '3.5' services: @@ -33,6 +33,13 @@ services: context: ./flowmachine dockerfile: Dockerfile + worked_examples: + container_name: worked_examples + image: flowminder/flowkit-examples:${CONTAINER_TAG:-latest} + build: + context: . + dockerfile: Dockerfile + flowapi: image: flowminder/flowapi:${CONTAINER_TAG:-latest} build: diff --git a/docker-compose.yml b/docker-compose.yml index 5d85cc9cd2..837d54a7fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ # DOCKER COMPOSE FOR FLOWKIT # -version: '3.7' +version: '3.5' networks: db: @@ -113,6 +113,29 @@ services: - db - redis + worked_examples: + container_name: worked_examples + image: flowminder/flowkit-examples:${CONTAINER_TAG:-latest} + ports: + - ${WORKED_EXAMPLES_PORT:?Must set WORKED_EXAMPLES_PORT env var}:8888 + tty: true + stdin_open: true + environment: + - FLOWDB_PORT=5432 + - FLOWDB_HOST=${DOCKER_FLOWDB_HOST:?Must set DOCKER_FLOWDB_HOST env var} + - FLOWMACHINE_FLOWDB_USER=${FLOWMACHINE_FLOWDB_USER:?Must set FLOWMACHINE_FLOWDB_USER env var} + - FLOWMACHINE_FLOWDB_PASSWORD=${FLOWMACHINE_FLOWDB_PASSWORD:?Must set FLOWMACHINE_FLOWDB_PASSWORD env var} + - REDIS_HOST=query_locker + - REDIS_PORT=6379 + - REDIS_PASSWORD=${REDIS_PASSWORD:?Must set REDIS_PASSWORD env var} + - FLOWAPI_URL=http://flowapi:9090 + restart: always + networks: + - db + - redis + command: + ["start.sh", "jupyter", "lab", "--LabApp.token=''"] + flowapi: container_name: flowapi image: flowminder/flowapi:${CONTAINER_TAG:-latest} diff --git a/docs/source/install.md b/docs/source/install.md index 2146cd8dd0..6bb23bffe4 100644 --- a/docs/source/install.md +++ b/docs/source/install.md @@ -1,6 +1,6 @@ Title: Installation -#How to Install FlowKit +# How to Install FlowKit There are three main ways to install FlowKit. @@ -18,10 +18,7 @@ The bulk of the installation process consists of downloading [Docker](https://do These instructions assume use of [Pyenv](https://github.com/pyenv/pyenv) and [Pipenv](https://github.com/pypa/pipenv). [Anaconda](https://www.anaconda.com/what-is-anaconda/) stack based installation commands may be different. - -### Server and Authentication Installation - -Docker containers for FlowAPI, FlowMachine and FlowDB are provided in the [DockerCloud](https://cloud.docker.com/) repositories `flowminder/flowapi`, `flowminder/flowmachine` and `flowminder/flowdb`, respectively. You will need [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/). +Docker containers for [FlowAPI](http://https://hub.docker.com/r/flowminder/flowapi), [FlowMachine](http://https://hub.docker.com/r/flowminder/flowmachine), [FlowDB](http://https://hub.docker.com/r/flowminder/flowdb), [FlowAuth](http://https://hub.docker.com/r/flowminder/flowauth) and the [worked examples](http://https://hub.docker.com/r/flowminder/flowkit-examples) are provided in the [DockerCloud](https://hub.docker.com/) repositories `flowminder/flowapi`, `flowminder/flowmachine`, `flowminder/flowdb`, `flowminder/flowauth`, and `flowminder/flowkit-examples` respectively. You will need [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/). Start the FlowKit test system by running @@ -34,9 +31,32 @@ This will pull any necessary docker containers, and start the system in the back The default system includes a small amount of test data. For a test system with considerably more data you can run ```bash -bash <(curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/master/quick_start.sh) synth +bash <(curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/master/quick_start.sh) larger_data +``` + +!!! warning + The larger data container will take considerably longer to start up, as it generates data when first run. + +The [worked examples](worked_examples) are also available as part of the demo system. To install these run + +```bash +bash <(curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/master/quick_start.sh) examples smaller_data +``` + +for the examples with a small dataset, or + +```bash +bash <(curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/master/quick_start.sh) examples ``` +to get the examples with the larger dataset (the one used when producing this documentation). + +To shut down the system, you can either stop all the docker containers directly, or run + +```bash +bash <(curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/master/quick_start.sh) stop +``` + In order to use the test system, now install FlowClient, and generate a token using FlowAuth. #### FlowAuth Quickstart diff --git a/docs/source/worked_examples/Flows.ipynb b/docs/source/worked_examples/Flows.ipynb index 630dcfa944..b3af776cbf 100644 --- a/docs/source/worked_examples/Flows.ipynb +++ b/docs/source/worked_examples/Flows.ipynb @@ -21,7 +21,7 @@ "metadata": {}, "outputs": [], "source": [ - "import flowclient, folium" + "import flowclient, folium, os" ] }, { @@ -37,7 +37,7 @@ "metadata": {}, "outputs": [], "source": [ - "conn = flowclient.connect(\"http://localhost:9090\", TOKEN)" + "conn = flowclient.connect(os.getenv(\"FLOWAPI_URL\", \"http://localhost:9090\"), TOKEN)" ] }, { diff --git a/docs/source/worked_examples/commuting-patterns.ipynb b/docs/source/worked_examples/commuting-patterns.ipynb index aabfda212c..ee4d9be547 100644 --- a/docs/source/worked_examples/commuting-patterns.ipynb +++ b/docs/source/worked_examples/commuting-patterns.ipynb @@ -19,7 +19,7 @@ "metadata": {}, "outputs": [], "source": [ - "import flowclient, folium" + "import flowclient, folium, os" ] }, { @@ -35,7 +35,7 @@ "metadata": {}, "outputs": [], "source": [ - "conn = flowclient.connect(\"http://localhost:9090\", TOKEN)" + "conn = flowclient.connect(os.getenv(\"FLOWAPI_URL\", \"http://localhost:9090\"), TOKEN)" ] }, { diff --git a/docs/source/worked_examples/index.md b/docs/source/worked_examples/index.md index 5e897a4c57..7c84ccd13b 100644 --- a/docs/source/worked_examples/index.md +++ b/docs/source/worked_examples/index.md @@ -9,3 +9,5 @@ We have three worked examples of using FlowKit for CDR analysis: - [Mobile Data Usage](./mobile-data-usage/) (using FlowMachine). The Jupyter notebooks for these worked examples can be found [here](https://github.com/Flowminder/FlowKit/tree/master/docs/source/worked_examples/). + +Alternatively you can run the worked examples using the [quick start setup](../install.md#quickinstall) which includes JupyterLab. diff --git a/flowauth/backend/flowauth/models.py b/flowauth/backend/flowauth/models.py index 3b50edb339..0918ed0bf5 100644 --- a/flowauth/backend/flowauth/models.py +++ b/flowauth/backend/flowauth/models.py @@ -556,12 +556,9 @@ def make_demodata(): # pragma: no cover gsp = GroupServerPermission( group=groups[0], server_capability=sc, get_result=True, run=True, poll=True ) - gsp.spatial_aggregation.append( - agg_units[0] - ) # Give Bob access to admin0 agg units - gsp.spatial_aggregation.append( - agg_units[1] - ) # Give Bob access to admin1 agg units + for agg_unit in agg_units[:4]: # Give Bob access to adminX agg units + gsp.spatial_aggregation.append(agg_unit) + db.session.add(gsp) db.session.add( GroupServerTokenLimits( diff --git a/quick_start.sh b/quick_start.sh index 2473ecc02d..4c275a854f 100755 --- a/quick_start.sh +++ b/quick_start.sh @@ -5,6 +5,8 @@ set -e set -a + + # Read a single char from /dev/tty, prompting with "$*" # Note: pressing enter will return a null string. Perhaps a version terminated with X and then remove it in caller? # See https://unix.stackexchange.com/a/367880/143394 for dealing with multi-byte, etc. @@ -49,13 +51,42 @@ if [ "$CI" = "true" ]; then export BRANCH=$CIRCLE_SHA1 fi -if [ $# -gt 0 ] && [ "$1" = "synth" ] +if [ $# -gt 0 ] && [ "$1" = "larger_data" ] || [ "$2" = "larger_data" ] then export DOCKER_FLOWDB_HOST=flowdb_synthetic_data else export DOCKER_FLOWDB_HOST=flowdb_testdata fi +if [ $# -gt 0 ] && [ "$1" = "examples" ] || [ "$2" = "examples" ] +then + export WORKED_EXAMPLES=worked_examples + if [ $# -gt 0 ] && [ "$1" = "smaller_data" ] || [ "$2" = "smaller_data" ] + then + export DOCKER_FLOWDB_HOST=flowdb_testdata + else + export DOCKER_FLOWDB_HOST=flowdb_testdata + fi +else + export WORKED_EXAMPLES= +fi + +DOCKER_ENGINE_VERSION=`docker version --format '{{.Server.Version}}'` +DOCKER_COMPOSE_VERSION=`docker-compose version --short` +if [[ "$DOCKER_ENGINE_VERSION" < "17.12.0" ]] +then + echo "Docker version not supported. Please upgrade docker to at least v17.12.0" + exit 1 +fi + +if [[ "$DOCKER_COMPOSE_VERSION" < "1.21.0" ]] +then + echo "docker-compose version not supported. Please upgrade docker to at least v1.21.0 (e.g. by running 'pip install --upgrade docker-compose'" + echo "or installing a newer version of Docker desktop." + exit 1 +fi + + if [ $# -gt 0 ] && [ "$1" = "stop" ] then export DOCKER_FLOWDB_HOST=flowdb_testdata @@ -64,12 +95,13 @@ then curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/docker-compose.yml | docker-compose -f - down else source /dev/stdin <<< "$(curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/development_environment)" - echo "Starting containers" - RUNNING=`curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/docker-compose.yml | docker-compose -f - ps -q $DOCKER_FLOWDB_HOST flowapi flowmachine flowauth flowmachine_query_locker` + echo "Starting containers (this may take a few minutes)" + RUNNING=`curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/docker-compose.yml | docker-compose -f - ps -q $DOCKER_FLOWDB_HOST flowapi flowmachine flowauth flowmachine_query_locker $WORKED_EXAMPLES` if [[ "$RUNNING" != "" ]]; then confirm "Existing containers are running and will be replaced. Are you sure?" || exit 1 fi - curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/docker-compose.yml | docker-compose -f - up -d $DOCKER_FLOWDB_HOST flowapi flowmachine flowauth flowmachine_query_locker + curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/docker-compose.yml | docker-compose -f - pull + curl -s https://raw.githubusercontent.com/Flowminder/FlowKit/${BRANCH:-master}/docker-compose.yml | docker-compose -f - up -d $DOCKER_FLOWDB_HOST flowapi flowmachine flowauth flowmachine_query_locker $WORKED_EXAMPLES echo "Waiting for containers to be ready.." docker exec ${DOCKER_FLOWDB_HOST} bash -c 'i=0; until [ $i -ge 24 ] || (pg_isready -h 127.0.0.1 -p 5432); do let i=i+1; echo Waiting 10s; sleep 10; done' || (>&2 echo "FlowDB failed to start :( Please open an issue at https://github.com/Flowminder/FlowKit/issues/new?template=bug_report.md&labels=FlowDB,bug including the output of running 'docker logs flowdb'" && exit 1) echo "FlowDB ready." @@ -79,9 +111,18 @@ else echo "FlowAPI ready." i=0; until [ $i -ge 24 ] || (curl -s http://localhost:$FLOWAUTH_PORT > /dev/null) ; do let i=i+1; echo Waiting 10s; sleep 10; done || (>&2 echo "FlowAuth failed to start :( Please open an issue at https://github.com/Flowminder/FlowKit/issues/new?template=bug_report.md&labels=FlowAuth,bug including the output of running 'docker logs flowauth'" && exit 1) echo "FlowAuth ready." + if [[ "$WORKED_EXAMPLES" = "worked_examples" ]] + then + i=0; until [ $i -ge 24 ] || (curl -s http://localhost:$WORKED_EXAMPLES_PORT > /dev/null) ; do let i=i+1; echo Waiting 10s; sleep 10; done || (>&2 echo "Worked examples failed to start :( Please open an issue at https://github.com/Flowminder/FlowKit/issues/new?template=bug_report.md&labels=docs,bug including the output of running 'docker logs worked_examples'" && exit 1) + fi + echo "Worked examples ready." echo "All containers ready!" echo "Access FlowDB using 'PGHOST=$FLOWDB_HOST PGPORT=$FLOWDB_PORT PGDATABASE=flowdb PGUSER=$FLOWMACHINE_FLOWDB_USER PGPASSWORD=$FLOWMACHINE_FLOWDB_PASSWORD psql'" echo "Access FlowAPI using FlowClient at http://localhost:$FLOWAPI_PORT" echo "View the FlowAPI spec at http://localhost:$FLOWAPI_PORT/api/0/spec/redoc" echo "Generate FlowAPI access tokens using FlowAuth with user TEST_USER and password DUMMY_PASSWORD at http://localhost:$FLOWAUTH_PORT" -fi \ No newline at end of file + if [[ "$WORKED_EXAMPLES" = "worked_examples" ]] + then + echo "Try out the interactive examples at http://localhost:$WORKED_EXAMPLES_PORT" + fi +fi