Skip to content

Commit

Permalink
feat: template for motoko/rust examples: project hello (dfinity#134)
Browse files Browse the repository at this point in the history
This PR tries to create a single, coherent example of the same dapp in both Rust in Motoko.

The example is just the default project created with `dfx new (--type rust)` and has both a Motoko (Rust) backend and frontend.
(Unfortunately, none of the existing dual language examples are either equivalent or simple enough.)

Each project has:

- [x] A tutorial style README for setting up, building and running the sample
- [x] A link to the same example in the other language (if possible).
- [x] Makefile (to be used for testing by CI)
- [x] GH actions so it can be tested against newer versions of dfx (leveraging the Makefile)
- [x] Docs onepager, c.f. dfinity/docs#659
- [x] RECIPE.md outlining steps required for adding a new dual language example.

Issues:
- How does one test the asset canister/frontend? The only existing example is out of date and commented out.
- Might be nicer if we could have the adoc in this examples repo, not the docs repo.
  • Loading branch information
crusso authored Jan 20, 2022
1 parent 3afab32 commit 4b5d5e6
Show file tree
Hide file tree
Showing 35 changed files with 17,898 additions and 0 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/hello-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: hello-example
on:
push:
branches:
- master
pull_request:
jobs:
motoko-hello-example-darwin:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v1
- name: Provision Darwin
run: bash .github/workflows/provision-darwin.sh
- name: Motoko Hello Example Darwin
run: |
dfx start --background
pushd motoko/hello
make test
popd
motoko-hello-example-linux:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- name: Provision Linux
run: bash .github/workflows/provision-linux.sh
- name: Motoko Hello Example Linux
run: |
dfx start --background
pushd motoko/hello
make test
popd
rust-hello-example-darwin:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v1
- name: Provision Darwin
run: bash .github/workflows/provision-darwin.sh
- name: Install Rust Darwin
run: bash .github/workflows/install-rust-darwin.sh
- name: Rust Hello Example Darwin
run: |
dfx start --background
pushd rust/hello
make test
popd
rust-hello-example-linux:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
- name: Provision Linux
run: bash .github/workflows/provision-linux.sh
- name: Install Rust Linux
run: bash .github/workflows/install-rust-linux.sh
- name: Rust Hello Example Linux
run: |
dfx start --background
pushd rust/hello
make test
popd
17 changes: 17 additions & 0 deletions .github/workflows/install-rust-darwin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

set -ex

# Enter temporary directory.
pushd /tmp

# Install cmake
brew install cmake

# Install rust
curl --location --output install-rustup.sh "https://sh.rustup.rs"
bash install-rustup.sh -y
rustup target add wasm32-unknown-unknown

# Exit temporary directory.
popd
20 changes: 20 additions & 0 deletions .github/workflows/install-rust-linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

set -ex

# Enter temporary directory.
pushd /tmp

# Install cmake
sudo apt-get install --yes cmake

# Install rust
wget --output-document install-rustup.sh "https://sh.rustup.rs"
sudo bash install-rustup.sh -y
rustup target add wasm32-unknown-unknown

# Set environment variables.
echo "$HOME/.cargo/bin" >> $GITHUB_PATH

# Exit temporary directory.
popd
8 changes: 8 additions & 0 deletions .github/workflows/provision-darwin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,13 @@ curl --location --output install-dfx.sh "https://sdk.dfinity.org/install.sh"
DFX_VERSION=$version bash install-dfx.sh < <(yes Y)
rm install-dfx.sh

# Install cmake
brew install cmake

# Install rust
curl --location --output install-rustup.sh "https://sh.rustup.rs"
bash install-rustup.sh -y
rustup target add wasm32-unknown-unknown

# Exit temporary directory.
popd
9 changes: 9 additions & 0 deletions .github/workflows/provision-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ wget --output-document install-dfx.sh "https://sdk.dfinity.org/install.sh"
DFX_VERSION=$version bash install-dfx.sh < <(yes Y)
rm install-dfx.sh

# Install cmake
sudo apt-get install --yes cmake

# Install rust
wget --output-document install-rustup.sh "https://sh.rustup.rs"
sudo bash install-rustup.sh -y
rustup target add wasm32-unknown-unknown

# Set environment variables.
echo "$HOME/bin" >> $GITHUB_PATH
echo "$HOME/.cargo/bin" >> $GITHUB_PATH

# Exit temporary directory.
popd
59 changes: 59 additions & 0 deletions ADDING_AN_EXAMPLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# How to Add a New Example

Ideally, each example should come with both Rust and Motoko implementations,
implementing the same Candid interface (and, ideally, semantics).

To illustrate the pattern, this repo now contains one such example, project `hello`.

The (dual) sources for the projects live in:

motoko/hello
rust/hello

When adding a new dfx generated project, make sure to delete its GH metadata files (.gitignore, .git etc).

Each project should have a similar, but language specific `README.md`. E.g.

motoko/hello/README.md
rust/hello/README.md

Each `README.md` should link to the other project's README.md for
language-curious readers.

# CI

Apart from the standard dfx material, each project should provide a
`Makefile` used by GitHub Actions CI to run (very) basic tests. E.g.

motoko/hello/Makefile
rust/hello/Makefile

For each example, there is a single CI file with four build actions to
produce darwin and linux builds and tests of the motoko/rust
projects. E.g.

.github/workflows/hello.yml

Implementing the GH action will ensure it runs in CI and gives us some
hope of keeping examples in sync with releases of dfx.

# Documentation

In repo dfinity/docs, add some general, language agnostic documentation for the
example. E.g. for `hello`:

* modules/examples/pages/index.adoc: add new bullet in early subsection pointing pointing to hello.adoc.
* modules/examples/pages/hello.adoc: add one page description of hello example.
* modules/examples/pages/assets/hello.png: screenshot of UI linked by hello.adoc (optional).
* modules/ROOT/nav.adoc: add direct site navigation to hello.adoc.

# Issues

This structuring of examples isn't ideal since it requires duplication
of similar files (typically frontend code) but has the advantage that
Motoko users only need to see Motoko specific content and dually for
Rust.

It also makes it possible to have language restricted examples, for
example, when the other language does not support a particular example
well.
34 changes: 34 additions & 0 deletions motoko/hello/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
t.PHONY: all
all: build

.PHONY: node_modules
.SILENT: node_modules
node_modules:
npm install

.PHONY: build
.SILENT: build
build: node_modules
dfx canister create --all
dfx build

.PHONY: install
.SILENT: install
install: build
dfx canister install --all

.PHONY: upgrade
.SILENT: upgrade
upgrade: build
dfx canister install --all --mode=upgrade

.PHONY: test
.SILENT: test
test: install
dfx canister call hello greet '("everyone")' \
| grep '("Hello, everyone!")' && echo 'PASS'

.PHONY: clean
.SILENT: clean
clean:
rm -fr .dfx
128 changes: 128 additions & 0 deletions motoko/hello/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Hello Example

## Summary

This example demonstrates a dead simple dapp consisting of two canister smart contracts:

* a simple backend canister, `hello`, implementing the logic of the application in Motoko, and
* a simple frontend asset canister, `hello_assets` serving the assets of the dapp's web user interface.

This example is based on the default project created by running `dfx new hello` as described
[here](https://smartcontracts.org/docs/quickstart/local-quickstart.html).

### Rust variant

A version of this example with a Rust implementation of canister `hello` can be found [here](../../rust/hello/README.md).

## Interface

Canister `hello` is defined as a Motoko actor:

* [src/hello/main.mo](src/hello/main.mo)

with the Candid interface:

```
service : {
greet: (text) -> (text);
}
```

The frontend displays a page with an HTML text box for the argument and a button for calling the function greet with that argument. The result of the call is displayed in a message box.

The relevant frontend code is:

* [src/hello_assets/src/index.html](src/hello_assets/src/index.html)
* [src/hello_assets/src/index.jsx](src/hello_assets/src/index.jsx)


## Requirements

The example requires an installation of:

* [DFINITY Canister SDK](https://sdk.dfinity.org).
* `node.js` (to build the web frontend).

(The Rust version of this example additionally requires a working Rust environment.)

## Setup

Check, you have stopped any local canister execution environment (i.e. `replica`) or other network process that would create a port conflict on 8000.


## Running Locally

Using two terminal windows, do the following steps:

1. Open the first terminal window.

1. Start a local canister execution environment

```text
dfx start
```

This command produces a lot of distracting diagnostic output which is best ignored by continuing in a second terminal.

1. Open the second terminal window.

1. Ensure that the required `node` modules are available in your project directory, if needed, by running the following command:

```text
npm install
```

1. Register, build and deploy the project.

```text
dfx deploy
```

1. Call the hello canister's greet function:

```text
dfx canister call hello greet '("everyone")'
```

1. Observe the following result.

```text
("Hello,a everyone!")
```

The previous steps use `dfx` to directly call the function on the `hello` (backend) canister.

To access the web user interface of the dapp, that is served by canister `hello_assets`, do the following:

1. Determine the URL of the `hello_assets` asset canister.

```text
echo "http://localhost:8000/?canisterId=$(dfx canister id hello_assets)"
```

1. Navigate to the URL in your browser.

2. The browser should display a simple HTML page with a sample asset image file, an input field, and a button.

3. Enter the text `everyone` and click the button to see the greeting returned by the backend `hello` canister.

## Troubleshooting

If the web page doesn't display properly, or displays the wrong contents,
you may need to clear your browser cache.

Alternatively, open the URL in a fresh, in-private browser window to start with a clean cache.

## Links

For instructions on how to create this example from scratch as well as a more detailed walkthrough and some tips on frontend development using a development server, see:

- [Local Development](https://smartcontracts.org/docs/quickstart/local-quickstart.html)

Other related links you might find useful are:

- [Motoko Programming Language Guide](https://sdk.dfinity.org/docs/language-guide/motoko.html)
- [Motoko Language Quick Reference](https://sdk.dfinity.org/docs/language-guide/language-manual.html)
- [Candid Introduction](https://smartcontracts.org/docs/candid-guide/candid-intro.html)
- [JavaScript API Reference](https://erxue-5aaaa-aaaab-qaagq-cai.raw.ic0.app)
- [Troubleshoot issues](https://smartcontracts.org/docs/developers-guide/troubleshooting.html)
35 changes: 35 additions & 0 deletions motoko/hello/dfx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"canisters": {
"hello": {
"main": "src/hello/main.mo",
"type": "motoko"
},
"hello_assets": {
"dependencies": [
"hello"
],
"frontend": {
"entrypoint": "src/hello_assets/src/index.html"
},
"source": [
"src/hello_assets/assets",
"dist/hello_assets/"
],
"type": "assets"
}
},
"defaults": {
"build": {
"args": "",
"packtool": ""
}
},
"dfx": "0.8.5",
"networks": {
"local": {
"bind": "127.0.0.1:8000",
"type": "ephemeral"
}
},
"version": 1
}
Loading

0 comments on commit 4b5d5e6

Please sign in to comment.